Compare commits
1162 Commits
v0.3_php5.
...
cbb8fdd7c3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbb8fdd7c3 | ||
|
|
e8a62f98fc | ||
|
|
5a9dd5252c | ||
|
|
7992b5413a | ||
|
|
c4f6f76edb | ||
|
|
10f74d90ba | ||
|
|
d95045cb15 | ||
|
|
381d14a6f6 | ||
|
|
0c95431128 | ||
|
|
5b911c856b | ||
|
|
c76ba261d8 | ||
|
|
74eb6d29e1 | ||
|
|
954f0d7968 | ||
|
|
d8dfa0ca25 | ||
|
|
920dba3a4d | ||
|
|
fccaed681d | ||
|
|
fff2e84fb1 | ||
|
|
3495a3d760 | ||
|
|
11aacd5803 | ||
|
|
cbdbbdf394 | ||
|
|
34844760e8 | ||
|
|
d83b72b2a3 | ||
|
|
b1113e826f | ||
|
|
2e3f168036 | ||
|
|
112a128492 | ||
|
|
465d9c0c5f | ||
|
|
d96145530c | ||
|
|
9bc572c590 | ||
|
|
a602fce3ab | ||
|
|
59e23794c0 | ||
|
|
6849c2caae | ||
|
|
fbaf23e7df | ||
|
|
b83c870eed | ||
|
|
8d8ac0a60b | ||
|
|
11b54588ff | ||
|
|
65b6d582d9 | ||
|
|
214aa4d348 | ||
|
|
45ed9bd7a3 | ||
|
|
f281295084 | ||
|
|
9adb37a8e2 | ||
|
|
68cbca5e28 | ||
|
|
078497cca8 | ||
|
|
4892143c0c | ||
|
|
c688586fcb | ||
|
|
1be8df7a75 | ||
|
|
b032dbd137 | ||
|
|
285c682b1e | ||
|
|
e813849c86 | ||
|
|
3acdec1458 | ||
|
|
3e68839706 | ||
|
|
e0b41ca4f7 | ||
|
|
780c614c78 | ||
|
|
f49a23b2fd | ||
|
|
1b940d2adf | ||
|
|
07461f4419 | ||
|
|
48631cffee | ||
|
|
69224ef661 | ||
|
|
68e6dc2d20 | ||
|
|
eea66c6932 | ||
|
|
2f2a4642fb | ||
|
|
df367d8316 | ||
|
|
2602a6fa7c | ||
|
|
9848a2919e | ||
|
|
2489b67542 | ||
|
|
63511709a8 | ||
|
|
821ffc4726 | ||
|
|
1f831551a1 | ||
|
|
7770f13bf3 | ||
|
|
0a594bb5a9 | ||
|
|
b3d43d754b | ||
|
|
a47d5d54b4 | ||
|
|
f89ca2ab93 | ||
|
|
b6e2b781ef | ||
|
|
8f5c3031fc | ||
|
|
f01e62fc6e | ||
|
|
4562bc8314 | ||
|
|
9952673655 | ||
|
|
cf9be800e5 | ||
|
|
ccd1af6154 | ||
|
|
ce633d816a | ||
|
|
83bfb8d32d | ||
|
|
28817cea38 | ||
|
|
3dca204ac6 | ||
|
|
2fb2e6f5aa | ||
|
|
44def81c81 | ||
|
|
03da3afb98 | ||
|
|
3b885e2686 | ||
|
|
e162d35d56 | ||
|
|
3b3ed7a264 | ||
|
|
3b5aa78a47 | ||
|
|
721faa129b | ||
|
|
78806de606 | ||
|
|
d4ea5eea1f | ||
|
|
9213c1c21d | ||
|
|
9312f8d49c | ||
|
|
9674812e78 | ||
|
|
8bda7c2cb0 | ||
|
|
375d63485c | ||
|
|
26045a3db7 | ||
|
|
04ac012754 | ||
|
|
e6f662f0a1 | ||
|
|
04b3c06cda | ||
|
|
24da6d75b5 | ||
|
|
542ae17afd | ||
|
|
97fffaa610 | ||
|
|
3eb5139b62 | ||
|
|
5245393a04 | ||
|
|
e59df1dffb | ||
|
|
61da97974b | ||
|
|
6482cfd4cc | ||
|
|
c3e51a7a6b | ||
|
|
de069ef186 | ||
|
|
cc12d5ff93 | ||
|
|
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 | ||
|
|
7b3707ef89 | ||
|
|
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 | ||
|
|
4d615d8028 | ||
|
|
5e78d32ec4 | ||
|
|
04dc17409e | ||
|
|
a76421c858 | ||
|
|
eb39495fa4 | ||
|
|
28086651e5 | ||
|
|
da80c3623e | ||
|
|
2a28adaca7 | ||
|
|
309512dde0 | ||
|
|
2866145e66 | ||
|
|
8b1d3c9830 | ||
|
|
7049542e59 | ||
|
|
31265104e1 | ||
|
|
3488774481 | ||
|
|
c719d11df2 | ||
|
|
480170b944 | ||
|
|
e6d65a5212 | ||
|
|
3a86a0f689 | ||
|
|
e21233954a | ||
|
|
4ca6c8bf6c | ||
|
|
d36cfd9653 | ||
|
|
2b8fa33f06 | ||
|
|
d141f61055 | ||
|
|
8167cc6a6a | ||
|
|
4b8dc3da02 | ||
|
|
1c82d55c1c | ||
|
|
709689b15b | ||
|
|
ff37597b81 | ||
|
|
3eb989e03c | ||
|
|
3ba1e8b97a | ||
|
|
eb019dd9e5 | ||
|
|
719373e534 | ||
|
|
5abe0770b3 | ||
|
|
1c5d97f06d | ||
|
|
287d9f8f11 | ||
|
|
589168440c | ||
|
|
d3d5b54b39 | ||
|
|
73c5a20158 | ||
|
|
4f6f478a9a | ||
|
|
a44654ec01 | ||
|
|
b8d0e18014 | ||
|
|
7a331d5f4f | ||
|
|
4c44794212 | ||
|
|
daf9fe9391 | ||
|
|
a167b8a885 | ||
|
|
d8767cfb73 | ||
|
|
18d384ecb3 | ||
|
|
64792e6fe2 | ||
|
|
77db0c2ed9 | ||
|
|
424276eeb4 | ||
|
|
ebd80a8313 | ||
|
|
377a4cc88c | ||
|
|
23cdd0e497 | ||
|
|
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 | ||
|
|
893fc74c0a | ||
|
|
6e63365604 | ||
|
|
e76ad897a4 | ||
|
|
4f85dc243e | ||
|
|
e9d92494c6 | ||
|
|
30f755cf42 | ||
|
|
d40f43c617 | ||
|
|
170ba466de | ||
|
|
ac6a830d27 | ||
|
|
d2d1bb3fc4 | ||
|
|
83e552211f | ||
|
|
d024d333d2 | ||
|
|
fe1b0626c2 | ||
|
|
778bb43895 | ||
|
|
68df688f12 | ||
|
|
41321481c7 | ||
|
|
61a00f475a | ||
|
|
fe9f6ce949 | ||
|
|
0c3f4c3e18 | ||
|
|
f81bfdecc2 | ||
|
|
90bf9bac0a | ||
|
|
8dd6fc957d | ||
|
|
4472a124f5 | ||
|
|
d92acf12de | ||
|
|
09e44995f3 | ||
|
|
43d1d2ccf5 | ||
|
|
e42c83c585 | ||
|
|
c06399cf86 | ||
|
|
c31c3a3cdc | ||
|
|
d1187f9bdd | ||
|
|
469962dd40 | ||
|
|
b7a77757f9 | ||
|
|
4d0108c6f8 | ||
|
|
5f199d97fe | ||
|
|
f1d852aea6 | ||
|
|
fc292e3366 | ||
|
|
8f6dd40b9a | ||
|
|
94e300a129 | ||
|
|
1fee7646b6 | ||
|
|
791b2d8ab1 | ||
|
|
97d16bd712 | ||
|
|
091920d80a | ||
|
|
9d716532bb | ||
|
|
1511986ed0 | ||
|
|
4be4224742 | ||
|
|
1e88e1fce2 | ||
|
|
b741bb6b01 | ||
|
|
a2014a916e | ||
|
|
af935ffd39 | ||
|
|
bd52030984 | ||
|
|
a15466df50 | ||
|
|
f23726783e | ||
|
|
f9ff27cad4 | ||
|
|
1d27b4dcb5 | ||
|
|
e33741f579 | ||
|
|
e74f8987d9 | ||
|
|
6b3266f022 | ||
|
|
9c55a047a8 | ||
|
|
b1dfe5bd50 | ||
|
|
6f2038f7b0 | ||
|
|
3696aa011c | ||
|
|
ec22f4b003 | ||
|
|
70629e360b | ||
|
|
6e03664f28 | ||
|
|
fc4e812d67 | ||
|
|
b6ee958c35 | ||
|
|
1b0d752a55 | ||
|
|
f82df229f2 | ||
|
|
9cac7f481e | ||
|
|
96c499786c | ||
|
|
9fe55bd782 | ||
|
|
52e0181bfb | ||
|
|
f8cb1e2ef7 | ||
|
|
b65c5c1dbf | ||
|
|
cbdce4f166 | ||
|
|
e9b9bdc420 | ||
|
|
704412727d | ||
|
|
baa737e628 | ||
|
|
72af3f3247 | ||
|
|
aa307150c8 | ||
|
|
f0693bdc05 | ||
|
|
0727d5f091 | ||
|
|
15f885ab8a | ||
|
|
aa65c18c39 | ||
|
|
b1a7f41934 | ||
|
|
666df07a7c | ||
|
|
4f11e27a76 | ||
|
|
452deb981f | ||
|
|
f8aef6e794 | ||
|
|
29f6d1a717 | ||
|
|
e100c9a88f | ||
|
|
025c3a555a | ||
|
|
1ce71a3936 | ||
|
|
467387a581 | ||
|
|
19f8c18ce9 | ||
|
|
acb8fb44be | ||
|
|
eb28852b87 | ||
|
|
fea21b3ea0 | ||
|
|
98f3dd7688 | ||
|
|
3a248fc1b4 | ||
|
|
c33a823e50 | ||
|
|
d49201211b | ||
|
|
1f97e8db35 | ||
|
|
4358732878 | ||
|
|
76df8c924f | ||
|
|
fdcdc94bec | ||
|
|
598ce5784b | ||
|
|
9a3a24ca0c | ||
|
|
cdb7d4b15c | ||
|
|
35f17ed1de | ||
|
|
07a2be05f7 | ||
|
|
bc95b31521 | ||
|
|
fd1a1f43d4 | ||
|
|
bd26ec7218 | ||
|
|
2e2147b238 | ||
|
|
8df94e9c5f | ||
|
|
a0c00f1a35 | ||
|
|
6f62d84818 | ||
|
|
868e23b982 | ||
|
|
b02a504abb | ||
|
|
e6b0313523 | ||
|
|
bba3af8163 | ||
|
|
654eb0960e | ||
|
|
16f7de421f | ||
|
|
2e36d46329 | ||
|
|
c3e60a65c1 | ||
|
|
1dd0b14861 | ||
|
|
f900853163 | ||
|
|
d75cd10f49 | ||
|
|
0de0c7d5d7 | ||
|
|
a69a56c715 | ||
|
|
a2aa612a5e | ||
|
|
8f7cf202e5 | ||
|
|
b900e49f54 | ||
|
|
58c4f28956 | ||
|
|
ad4e194bf9 | ||
|
|
f3eaa40ea5 | ||
|
|
a5e7366cac | ||
|
|
e784931fa8 | ||
|
|
bd85ce9f51 | ||
|
|
5a7b087ddc | ||
|
|
c58183c965 | ||
|
|
4d62d7da73 | ||
|
|
adc901b7e7 | ||
|
|
cca698d437 | ||
|
|
a84655bcb7 | ||
|
|
5247bb8db6 | ||
|
|
6a7a784cb6 | ||
|
|
cb969ba340 | ||
|
|
7c9637ae35 | ||
|
|
094e15ddf9 | ||
|
|
5d934f756f | ||
|
|
670f0894ea | ||
|
|
6904320e4e | ||
|
|
9e20473b3b | ||
|
|
17297beb65 | ||
|
|
11d5deac85 | ||
|
|
e0ea50d142 | ||
|
|
67280c89e7 | ||
|
|
81b5bfed26 | ||
|
|
1afed6a6e0 | ||
|
|
2ddcdf9419 | ||
|
|
0ff91bf4d8 | ||
|
|
e0d15b7396 | ||
|
|
4d4e96fe1c | ||
|
|
aa846de284 | ||
|
|
32a98b4b6e | ||
|
|
09e650d841 | ||
|
|
817b74b085 | ||
|
|
61e9dad364 | ||
|
|
dc1bb56188 | ||
|
|
91f5ad7660 | ||
|
|
4707f3314d | ||
|
|
14d896d1b3 | ||
|
|
ac2b1530fc | ||
|
|
d2795e8201 | ||
|
|
a5d3ff0b70 | ||
|
|
df91125e96 | ||
|
|
adc87c0763 | ||
|
|
7fda78f820 | ||
|
|
7d81b9766b | ||
|
|
a59d93ebee | ||
|
|
1aa6273ade | ||
|
|
fe66241ca1 | ||
|
|
2c08405d5a | ||
|
|
4c8e3b380e | ||
|
|
e2ed6ed1c7 | ||
|
|
6b357fc285 | ||
|
|
cb716b06b7 | ||
|
|
a9ef3a75ae | ||
|
|
35d7656bc5 | ||
|
|
267357a0f7 | ||
|
|
8818ad7a80 | ||
|
|
1c2c2b457c | ||
|
|
15a4528e75 | ||
|
|
b00944a412 | ||
|
|
44e4477d38 | ||
|
|
4ef09080be | ||
|
|
c4cc3ea812 | ||
|
|
b41ce38526 | ||
|
|
1e10fbbf38 | ||
|
|
f210388f6c | ||
|
|
53a279a6db | ||
|
|
645a3a4797 | ||
|
|
8e0270cc82 | ||
|
|
3a95a994cf | ||
|
|
8f79f0401f | ||
|
|
00c99ea60a | ||
|
|
e40778ddbd | ||
|
|
e09fb7d258 | ||
|
|
6b7ce60ae3 | ||
|
|
37e7212abe | ||
|
|
2b09f4054f | ||
|
|
655e0c3940 | ||
|
|
33a677431f | ||
|
|
e47a2cc7aa | ||
|
|
9bd37ba282 | ||
|
|
0cdcb6252e | ||
|
|
53a8d8fc60 | ||
|
|
bbec6ccbb6 | ||
|
|
91fdef9997 | ||
|
|
4c7e3d1f58 | ||
|
|
3e2d219c8d | ||
|
|
5dc5f087ba | ||
|
|
57d593fc38 | ||
|
|
46744a24d3 | ||
|
|
65b7137f21 | ||
|
|
b553dbb9c3 | ||
|
|
323519ab75 | ||
|
|
3c390d940f | ||
|
|
a1a9c7800b | ||
|
|
777a47a27b | ||
|
|
fe17f87f7d | ||
|
|
237b0a901f | ||
|
|
6b9279f5b0 | ||
|
|
393d9c9d77 | ||
|
|
2882b1917c | ||
|
|
17cd00c90e | ||
|
|
f707448b05 | ||
|
|
066acd14f0 | ||
|
|
5247139fe8 | ||
|
|
87db010dc1 | ||
|
|
d2a4021d6f | ||
|
|
9360ebe661 | ||
|
|
b759f29d91 | ||
|
|
32c81b7367 | ||
|
|
c0d26a8e95 | ||
|
|
0b8d8fe1ac | ||
|
|
a1f30804a7 | ||
|
|
55b3ec947e | ||
|
|
0cb961925d | ||
|
|
876d9b3d33 | ||
|
|
fbfc973dad | ||
|
|
d33bf24958 | ||
|
|
24f245c3c8 | ||
|
|
424cb0ea4f | ||
|
|
e7e3dbc559 | ||
|
|
63de30d392 | ||
|
|
de0cf6467a | ||
|
|
07232c77d5 | ||
|
|
48fb7383de | ||
|
|
1e7557de71 | ||
|
|
94c0efa881 | ||
|
|
d91957faed | ||
|
|
7450d2a98e | ||
|
|
56e6aa1f34 | ||
|
|
3e715bae9f | ||
|
|
6e759e8589 | ||
|
|
347aa20622 | ||
|
|
9733226dfd | ||
|
|
510ca489ec | ||
|
|
be2865b4d7 | ||
|
|
e4426cde55 | ||
|
|
adddfecada | ||
|
|
25e3c87213 | ||
|
|
55c5c9e9e6 | ||
|
|
eb209a455b | ||
|
|
42677ae2f4 | ||
|
|
ccf7a5fccd | ||
|
|
ec8f540107 | ||
|
|
a8862ea3fd | ||
|
|
393c0dbb3f | ||
|
|
f6a93af27d | ||
|
|
f8413b8784 | ||
|
|
e1217cd185 | ||
|
|
37d0fe7f3f | ||
|
|
362226183e | ||
|
|
13f97e5770 | ||
|
|
438610bdf9 | ||
|
|
b789ed62b7 | ||
|
|
cdf6ce86e3 | ||
|
|
2ffe04e3e4 | ||
|
|
3128880c4d | ||
|
|
cdba179d79 | ||
|
|
e23413b4d8 | ||
|
|
5ba6438b77 | ||
|
|
3a6964c4bf | ||
|
|
a4459ef0de | ||
|
|
95f43f07bf | ||
|
|
e7b71e0abb | ||
|
|
bfa71375e8 | ||
|
|
0c06ef6d71 | ||
|
|
48123c98b8 | ||
|
|
0f42ad9b07 | ||
|
|
9d35ed1aa9 | ||
|
|
c20b287c53 | ||
|
|
ae35d637ac | ||
|
|
ef4d8d4d30 | ||
|
|
6cd0a1378d | ||
|
|
e7a659a690 | ||
|
|
1d6c7436d4 | ||
|
|
f6ee56501d | ||
|
|
aa0a60ea2b | ||
|
|
826eb63da1 | ||
|
|
6cf6dda3f8 | ||
|
|
5446f6a266 | ||
|
|
d8fe7f9264 | ||
|
|
b225775a91 | ||
|
|
1372bb68e4 | ||
|
|
a5565546d1 | ||
|
|
cb5b017c7e | ||
|
|
f0b65dcf74 | ||
|
|
8e720dbfec | ||
|
|
ad1758a5dc | ||
|
|
24ccb56e50 | ||
|
|
b3389652fd | ||
|
|
4adf0dd2fc | ||
|
|
ba62eed027 | ||
|
|
24bca2c8e9 | ||
|
|
3dc751c863 | ||
|
|
2cc58255c3 | ||
|
|
68fe787209 | ||
|
|
36f439ff38 | ||
|
|
d4292a85a0 | ||
|
|
7a42bee8ef | ||
|
|
f9635419dd | ||
|
|
0170dbc310 | ||
|
|
d8f10bfd13 | ||
|
|
b820929ddc | ||
|
|
25b7da57f3 | ||
|
|
743ff52e59 | ||
|
|
29fe265daa | ||
|
|
efb4655786 | ||
|
|
769c02a5f1 | ||
|
|
7c34c7faff | ||
|
|
476a179e7b | ||
|
|
e448519a1e | ||
|
|
df8ebe45ef | ||
|
|
a4dff8a6ed | ||
|
|
0bf458f243 | ||
|
|
2db0fc12d3 | ||
|
|
18c4f5ea48 | ||
|
|
991d4d717e | ||
|
|
a0f33054cb | ||
|
|
0f00b42d1f | ||
|
|
446ba0ce28 | ||
|
|
1e0b96953e | ||
|
|
b408d86a98 | ||
|
|
e739282d17 | ||
|
|
4cab2c09c1 | ||
|
|
c73016ecf2 | ||
|
|
127f7f3eb9 | ||
|
|
122dfb10a0 | ||
|
|
ce23bf5abd | ||
|
|
9a32800170 | ||
|
|
5cf40147d9 | ||
|
|
04bc284a76 | ||
|
|
4a94fc3798 | ||
|
|
708759765f | ||
|
|
3c22378498 | ||
|
|
9dccbfeda8 | ||
|
|
43b4ef8890 | ||
|
|
f8c60ec265 | ||
|
|
25bc1607cc | ||
|
|
234999b4d2 | ||
|
|
85edb1c658 | ||
|
|
0ba5007438 | ||
|
|
8cf8f65474 | ||
|
|
368b730744 | ||
|
|
c596dfa5f0 | ||
|
|
8db607ffc5 | ||
|
|
8331be454c | ||
|
|
030617713d | ||
|
|
8b65bc5159 | ||
|
|
1e359f816c | ||
|
|
634f0f5423 | ||
|
|
b641dc98ec | ||
|
|
7aa026f4e0 | ||
|
|
f52fba96c1 | ||
|
|
93ebc450fe | ||
|
|
5a16e497ad | ||
|
|
fd856323a5 | ||
|
|
59e6379977 | ||
|
|
ae5d4763f9 | ||
|
|
d5bcd9a496 | ||
|
|
c002f3f3ba | ||
|
|
bfbf119aca | ||
|
|
f8d4dcef94 | ||
|
|
7181bc7f9b | ||
|
|
a7671e76fe | ||
|
|
7304a0e889 | ||
|
|
fc30dfa2e8 | ||
|
|
3d262fd4db | ||
|
|
6ac0204baf | ||
|
|
af0c10f6a6 | ||
|
|
e303898a95 | ||
|
|
3313a18407 | ||
|
|
b2ab134996 | ||
|
|
fbb751d054 | ||
|
|
fff6eaeeca | ||
|
|
1408a3dcb3 | ||
|
|
9507bfddc8 | ||
|
|
aa682c9bb2 | ||
|
|
0c8649d1c5 | ||
|
|
405e20f37f | ||
|
|
75ff8a2a40 | ||
|
|
20c1284788 | ||
|
|
116b014332 | ||
|
|
8fcecb7092 | ||
|
|
8a36b441de | ||
|
|
b1914f7cca | ||
|
|
0649329dde | ||
|
|
50c4ee225f | ||
|
|
d1075a2ea2 | ||
|
|
6210838b22 | ||
|
|
f5b87fa6b0 | ||
|
|
1d8ef76efa | ||
|
|
60f43efdb7 | ||
|
|
abc5f1feff | ||
|
|
8b7c27b5db | ||
|
|
865d9ee617 | ||
|
|
059dbd3190 | ||
|
|
8a3c6ae21b | ||
|
|
03af4e731f | ||
|
|
2cd73ed9e8 | ||
|
|
b97d071bb4 | ||
|
|
6a0a77c116 | ||
|
|
d6be905c08 | ||
|
|
6e1c684a06 | ||
|
|
0c13ef5cda | ||
|
|
9529431be2 | ||
|
|
d2ac8019ee | ||
|
|
1994512d6e | ||
|
|
a462e9be1c | ||
|
|
ccf0138eee | ||
|
|
288e4c05de | ||
|
|
b3ca719517 | ||
|
|
b8fd17efcf | ||
|
|
ece7ab25ae | ||
|
|
860006ed38 | ||
|
|
e067af589c | ||
|
|
546766d9be | ||
|
|
4b43433d2d | ||
|
|
5a1fdedad0 | ||
|
|
6ce9eec1a4 | ||
|
|
d44007a358 | ||
|
|
8d2a544042 | ||
|
|
d9297d89ab | ||
|
|
46db9c8341 | ||
|
|
ad17023570 | ||
|
|
9cac8c26a9 | ||
|
|
90d990e0a5 | ||
|
|
1cb56894ce | ||
|
|
f2f389ee45 | ||
|
|
5877874e82 | ||
|
|
de5f4208eb | ||
|
|
6507009acc | ||
|
|
6e1eba03a0 | ||
|
|
7a31f5cbd2 | ||
|
|
112aa7cf59 | ||
|
|
372578f171 | ||
|
|
645458c081 | ||
|
|
1afaede12a | ||
|
|
5a4cfb0581 | ||
|
|
17b6986758 | ||
|
|
00b473ee4f | ||
|
|
3463ae329b | ||
|
|
b3bceef570 | ||
|
|
80ddeeed6f | ||
|
|
43787d6434 | ||
|
|
72c55459ca | ||
|
|
977b21ed96 | ||
|
|
e4132237b9 | ||
|
|
c2d2a1c400 | ||
|
|
208aaaa504 | ||
|
|
21aa2c1d26 | ||
|
|
d3959e92e5 | ||
|
|
60c4d950fa | ||
|
|
d7db5a32d6 | ||
|
|
1932a9c316 | ||
|
|
d09af4f475 | ||
|
|
437ebabfd3 | ||
|
|
686c58afea | ||
|
|
754e2327c7 | ||
|
|
aca9d13a7d | ||
|
|
311d8be646 | ||
|
|
f77281835d | ||
|
|
1ed98b5e39 | ||
|
|
a2cb53a83b | ||
|
|
dff8394e91 | ||
|
|
014fa0feb9 | ||
|
|
3aacbb94aa | ||
|
|
5f9b9e15cb | ||
|
|
1f95e835f8 | ||
|
|
f7f9da92f3 | ||
|
|
3f275b127b | ||
|
|
f4319364f2 | ||
|
|
54ded6173a | ||
|
|
b36d3117a3 | ||
|
|
06b8ff2ae9 | ||
|
|
40264d3c39 | ||
|
|
5007890d09 | ||
|
|
aab8a994c8 | ||
|
|
a8c91d8b22 | ||
|
|
d5d2d07894 | ||
|
|
7b3f774304 | ||
|
|
e17cf21351 | ||
|
|
807dc48375 | ||
|
|
8491d81496 | ||
|
|
1c164cebc1 | ||
|
|
db5eed24f6 | ||
|
|
c7d7c9e7e4 | ||
|
|
eba1267f00 | ||
|
|
240a16367b | ||
|
|
cb39f6f0eb | ||
|
|
cd07f51ff9 | ||
|
|
cba8e16be7 | ||
|
|
fbeed43e61 | ||
|
|
1d82e263ad | ||
|
|
aa12a1efa4 | ||
|
|
758a5f700c | ||
|
|
9aa598f81f | ||
|
|
e953f2709d | ||
|
|
96dd06b58a | ||
|
|
5fd783dc50 | ||
|
|
689a6dc7aa | ||
|
|
3e60f8d45e | ||
|
|
09cb297dc7 | ||
|
|
ab0e85824b | ||
|
|
6def9cbb02 | ||
|
|
c7d772be53 | ||
|
|
e584997805 | ||
|
|
b7e0ea8313 | ||
|
|
bb61de60ad | ||
|
|
bc3bcd8312 | ||
|
|
c59f48fb84 | ||
|
|
6844513253 | ||
|
|
dc850f4e8e | ||
|
|
dea1454dcd | ||
|
|
6a8d21dad7 | ||
|
|
27503ac4f9 | ||
|
|
f6401c92be | ||
|
|
d77dcfca75 | ||
|
|
a796e9e82d | ||
|
|
504f1e7cc0 | ||
|
|
591e1db81d | ||
|
|
1ae1b6a4f3 | ||
|
|
021c0626a3 | ||
|
|
63b2e7360b | ||
|
|
3ad0cc1d20 | ||
|
|
8d87a0698c | ||
|
|
44df78e69f | ||
|
|
707c9a87ef | ||
|
|
0558c35ebc | ||
|
|
2b2991a13a | ||
|
|
c1aedde374 | ||
|
|
0b69e065ae | ||
|
|
1c8cc70a28 | ||
|
|
c6f68861b3 | ||
|
|
ce4fa4131a | ||
|
|
80f8bd36d5 | ||
|
|
7aa1100cac | ||
|
|
b9c1a81c3b | ||
|
|
bd0bbd768d | ||
|
|
20b8908a00 | ||
|
|
ab51fd32d2 | ||
|
|
b780ebeb3e | ||
|
|
117e31af69 | ||
|
|
28b3f35318 | ||
|
|
2b8a525718 | ||
|
|
dc6e399fea | ||
|
|
1508f963d3 | ||
|
|
3270ad9bf0 | ||
|
|
febd216810 | ||
|
|
c8b781804a | ||
|
|
3e5ac64108 | ||
|
|
9b6830468e | ||
|
|
063aec6c3c | ||
|
|
a2ae522b2d | ||
|
|
ba218fbeeb | ||
|
|
2e67695154 | ||
|
|
a136fd5313 | ||
|
|
1e3774cad6 | ||
|
|
518f7836ac | ||
|
|
416e230da8 | ||
|
|
cd416ea5fa | ||
|
|
001b156333 | ||
|
|
df1e0daafd | ||
|
|
9331b0a412 | ||
|
|
fae90c3fa9 | ||
|
|
a83edef049 | ||
|
|
4ada3d5bac | ||
|
|
ede1053de4 | ||
|
|
ce9142ffe2 | ||
|
|
0270e5ea89 | ||
|
|
4d2b4779d4 | ||
|
|
fe7d7c6b0f | ||
|
|
435b0614b2 | ||
|
|
708910870c | ||
|
|
8b067bf676 | ||
|
|
388dbc0540 | ||
|
|
ce20a44836 | ||
|
|
1495bb3373 | ||
|
|
eaea52a862 | ||
|
|
271207629b | ||
|
|
edca01f98c | ||
|
|
23f4a29fc7 | ||
|
|
543e2daa43 | ||
|
|
3911b3303f | ||
|
|
d859cb4dee | ||
|
|
39071b39fb | ||
|
|
3dcdcb03eb | ||
|
|
41636f5c75 | ||
|
|
1890a0490e | ||
|
|
2b9369b823 | ||
|
|
4ec3db7b58 | ||
|
|
9f876b78de | ||
|
|
31c603c757 | ||
|
|
4d49ad6516 | ||
|
|
015e6b082d | ||
|
|
cdec4c3301 | ||
|
|
404323c6c1 | ||
|
|
d5dbabd94a | ||
|
|
c90830d41a | ||
|
|
f6ca79d1a9 | ||
|
|
5fab94011b | ||
|
|
af2d5c38f3 | ||
|
|
6b8d05f2a0 | ||
|
|
336ec3b643 | ||
|
|
b4d640f7c2 | ||
|
|
90a16c721e | ||
|
|
e7b4b21328 | ||
|
|
3be775f9b2 | ||
|
|
0278c77610 | ||
|
|
508a8b08aa | ||
|
|
c5bd8b4b16 | ||
|
|
a49b86898e | ||
|
|
55dd94681a | ||
|
|
2f91dd456a | ||
|
|
e32a5e5d4c | ||
|
|
cca48fe86a | ||
|
|
839d9c1697 | ||
|
|
c80c66d958 | ||
|
|
a2fa71038a | ||
|
|
d3c7011fc8 | ||
|
|
2c224dbc62 | ||
|
|
13b2283d76 | ||
|
|
3d89da7a61 | ||
|
|
99263920a3 | ||
|
|
8ab8d087e6 | ||
|
|
18b928411b | ||
|
|
8d062ab92e | ||
|
|
147c1e7097 | ||
|
|
760420bdc5 | ||
|
|
fb48baf575 | ||
|
|
d60c9721ca | ||
|
|
f8eb45a0b9 | ||
|
|
4277962e3b | ||
|
|
a60d69ba52 | ||
|
|
55b896a9f0 | ||
|
|
9f8d4fa9b2 | ||
|
|
4988d2717f | ||
|
|
136d8e845a | ||
|
|
190a9259d4 | ||
|
|
374843aeee | ||
|
|
52032180f3 | ||
|
|
d14b715a88 | ||
|
|
2a2d11ba0f | ||
|
|
2e07cb87a5 | ||
|
|
bb5b563738 | ||
|
|
f3ffa3c079 | ||
|
|
30b934823c | ||
|
|
948ae28616 | ||
|
|
30c6e0b0f4 | ||
|
|
46e3316ce1 | ||
|
|
e1671a9fca | ||
|
|
e586268dee | ||
|
|
24730fda1b | ||
|
|
b6ec85205d | ||
|
|
b6a0809350 | ||
|
|
f42ce65768 | ||
|
|
ef180e7dda | ||
|
|
4aa59a40a2 | ||
|
|
0c43a0f884 | ||
|
|
e65db5c759 | ||
|
|
59b549955e | ||
|
|
603a08393e | ||
|
|
1db887cf38 | ||
|
|
4ebe1db2cc | ||
|
|
f74073a2e1 | ||
|
|
3f6c526718 | ||
|
|
903209a47e | ||
|
|
23a81ae924 | ||
|
|
9db4dd737d | ||
|
|
52528f1c0e | ||
|
|
6bf06a3a3c | ||
|
|
387c1ae87b | ||
|
|
70812e0a0c | ||
|
|
7fae1bcaad | ||
|
|
c041174b0c | ||
|
|
2a4b8079fb | ||
|
|
79bcf99fc5 | ||
|
|
9d09afca35 | ||
|
|
f384e051bf | ||
|
|
2da7b6b2c0 | ||
|
|
2c85566ffc | ||
|
|
db03c73b59 | ||
|
|
684f02ebc5 | ||
|
|
4e33c58246 | ||
|
|
9fc5562dd8 | ||
|
|
3cc8e95ff4 | ||
|
|
15930fae30 | ||
|
|
dbe037fee8 | ||
|
|
5c6189cdef | ||
|
|
4a86d3dda0 | ||
|
|
ec3d986c78 | ||
|
|
e38e99c94e | ||
|
|
aed2c6912c | ||
|
|
7433e6868c | ||
|
|
8393403443 | ||
|
|
ed04f81dda | ||
|
|
4923712238 | ||
|
|
ffe04ed6fd | ||
|
|
0021eb1768 | ||
|
|
0ef51ee30f | ||
|
|
28bf192853 | ||
|
|
df846f9963 | ||
|
|
f4007a0269 | ||
|
|
6b53d8a25a | ||
|
|
76a06d1655 | ||
|
|
eeb8775820 | ||
|
|
2d9a85ad82 | ||
|
|
0da1080b86 | ||
|
|
2665d012c2 | ||
|
|
c5d5e2c651 | ||
|
|
c22be388cf | ||
|
|
220284d20f | ||
|
|
4502b73b2e | ||
|
|
3251ba340d | ||
|
|
c9f9d0867b | ||
|
|
d4122b207b | ||
|
|
562ffc9e4b |
@@ -1,5 +0,0 @@
|
|||||||
<IfModule mod_rewrite.c>
|
|
||||||
RewriteEngine on
|
|
||||||
RewriteRule ^$ webroot/ [L]
|
|
||||||
RewriteRule (.*) webroot/$1 [L]
|
|
||||||
</IfModule>
|
|
||||||
@@ -1,461 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* SVN FILE: $Id: app_controller.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
|
||||||
/**
|
|
||||||
* Short description for file.
|
|
||||||
*
|
|
||||||
* This file is application-wide controller file. You can put all
|
|
||||||
* application-wide controller-related methods here.
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
|
||||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
*
|
|
||||||
* Licensed under The MIT License
|
|
||||||
* Redistributions of files must retain the above copyright notice.
|
|
||||||
*
|
|
||||||
* @filesource
|
|
||||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
|
||||||
* @package cake
|
|
||||||
* @subpackage cake.app
|
|
||||||
* @since CakePHP(tm) v 0.2.9
|
|
||||||
* @version $Revision: 7945 $
|
|
||||||
* @modifiedby $LastChangedBy: gwoo $
|
|
||||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Short description for class.
|
|
||||||
*
|
|
||||||
* Add your application-wide methods in the class below, your controllers
|
|
||||||
* will inherit them.
|
|
||||||
*
|
|
||||||
* @package cake
|
|
||||||
* @subpackage cake.app
|
|
||||||
*/
|
|
||||||
class AppController extends Controller {
|
|
||||||
var $helpers = array('Html', 'Form', 'Javascript', 'Format', 'Time', 'Grid');
|
|
||||||
var $components = array('DebugKit.Toolbar');
|
|
||||||
|
|
||||||
function sideMenuLinks() {
|
|
||||||
return array(
|
|
||||||
array('name' => 'Common', 'header' => true),
|
|
||||||
array('name' => 'Site Map', 'url' => array('controller' => 'maps', 'action' => 'view', 1)),
|
|
||||||
array('name' => 'Units', 'url' => array('controller' => 'units', 'action' => 'index')),
|
|
||||||
array('name' => 'Leases', 'url' => array('controller' => 'leases', 'action' => 'index')),
|
|
||||||
array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index')),
|
|
||||||
array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index')),
|
|
||||||
array('name' => 'Debug', 'header' => true),
|
|
||||||
array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
|
|
||||||
array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')),
|
|
||||||
array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')),
|
|
||||||
array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function beforeRender() {
|
|
||||||
$this->set('sidemenu', $this->sideMenuLinks());
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset_data() {
|
|
||||||
$this->layout = null;
|
|
||||||
$this->autoLayout = false;
|
|
||||||
$this->autoRender = false;
|
|
||||||
Configure::write('debug', '0');
|
|
||||||
$script = $_SERVER['DOCUMENT_ROOT'] . '/pmgr/build.cmd';
|
|
||||||
echo "<P>" . date('r') . "\n";
|
|
||||||
//echo "<P>Script: $script" . "\n";
|
|
||||||
$db = & $this->Account->getDataSource();
|
|
||||||
$script .= ' "' . $db->config['database'] . '"';
|
|
||||||
$script .= ' "' . $db->config['login'] . '"';
|
|
||||||
$script .= ' "' . $db->config['password'] . '"';
|
|
||||||
$handle = popen($script . ' 2>&1', 'r');
|
|
||||||
//echo "<P>Handle: $handle; " . gettype($handle) . "\n";
|
|
||||||
echo "<P><PRE>\n";
|
|
||||||
while (($read = fread($handle, 2096))) {
|
|
||||||
echo $read;
|
|
||||||
}
|
|
||||||
echo "</PRE>\n";
|
|
||||||
pclose($handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* helper: jqGridView
|
|
||||||
* - called by function to create an index listing
|
|
||||||
*/
|
|
||||||
|
|
||||||
function jqGridView($title, $action = null, $element = null) {
|
|
||||||
$this->set('title', $title);
|
|
||||||
// The resulting page will contain a jqGrid, which will
|
|
||||||
// use ajax to obtain the actual data for this action
|
|
||||||
$this->set('action', $action ? $action : $this->params['action']);
|
|
||||||
$this->render('/elements/' . ($element ? $element : $this->params['controller']));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: jqGridData
|
|
||||||
* - Fetches the actual data requested by jqGrid as XML
|
|
||||||
*/
|
|
||||||
|
|
||||||
function jqGridData() {
|
|
||||||
// Grab a copy of the parameters that control this request
|
|
||||||
$params = array();
|
|
||||||
if (isset($this->params['url']) && is_array($this->params['url']))
|
|
||||||
$params = $this->params['url'];
|
|
||||||
|
|
||||||
// Do any preliminary setup necessary
|
|
||||||
$this->jqGridDataSetup($params);
|
|
||||||
|
|
||||||
// Get the top level model for this grid
|
|
||||||
$model = $this->jqGridDataModel($params);
|
|
||||||
|
|
||||||
// Establish the basic query and conditions
|
|
||||||
$query = array_intersect_key($this->jqGridDataCountTables($params, $model),
|
|
||||||
array('link'=>1, 'contain'=>1));
|
|
||||||
$query['conditions'] = $this->jqGridDataCountConditions($params, $model);
|
|
||||||
$query['group'] = $this->jqGridDataCountGroup($params, $model);
|
|
||||||
|
|
||||||
// DEBUG PURPOSES ONLY!
|
|
||||||
$params['count_query'] = $query;
|
|
||||||
|
|
||||||
// Get the number of records prior to pagination
|
|
||||||
$count = $this->jqGridDataRecordCount($params, $model, $query);
|
|
||||||
|
|
||||||
// Start the query over, this time getting the actual set
|
|
||||||
// of tables needed, not just those needed for counting.
|
|
||||||
$query = array_intersect_key($this->jqGridDataTables($params, $model),
|
|
||||||
array('link'=>1, 'contain'=>1));
|
|
||||||
$query['conditions'] = $this->jqGridDataConditions($params, $model);
|
|
||||||
|
|
||||||
// Verify a few parameters and determine our starting row
|
|
||||||
$limit = $params['rows'];
|
|
||||||
$total = ($count < 0) ? 0 : ceil($count/$limit);
|
|
||||||
$page = ($params['page'] <= 1) ? 1 : (($params['page'] > $total) ? $total : $params['page']);
|
|
||||||
$start = $limit*$page - $limit;
|
|
||||||
|
|
||||||
// Grab the actual records taking pagination into account
|
|
||||||
$query['group'] = $this->jqGridDataGroup($params, $model);
|
|
||||||
$query['order'] = $this->jqGridDataOrder($params, $model,
|
|
||||||
isset($params['sidx']) ? $params['sidx'] : null,
|
|
||||||
isset($params['sord']) ? $params['sord'] : null);
|
|
||||||
$query['limit'] = $this->jqGridDataLimit($params, $model, $start, $limit);
|
|
||||||
$query['fields'] = $this->jqGridDataFields($params, $model);
|
|
||||||
$results = $this->jqGridDataRecords($params, $model, $query);
|
|
||||||
|
|
||||||
// DEBUG PURPOSES ONLY!
|
|
||||||
$params['query'] = $query;
|
|
||||||
|
|
||||||
// Post process the records
|
|
||||||
$this->jqGridRecordsPostProcess($params, $model, $results);
|
|
||||||
|
|
||||||
// Add in any needed hyperlinks
|
|
||||||
$this->jqGridRecordLinks($params, $model, $results, array());
|
|
||||||
|
|
||||||
// Finally, dump out the data
|
|
||||||
$this->jqGridDataOutputHeader($params, $model);
|
|
||||||
echo "<?xml version='1.0' encoding='utf-8'?>\n";
|
|
||||||
echo "<rows>\n";
|
|
||||||
$this->jqGridDataOutputSummary($params, $model, $page, $total, $count);
|
|
||||||
$this->jqGridDataOutputRecords($params, $model, $results);
|
|
||||||
echo "</rows>\n";
|
|
||||||
|
|
||||||
// Call out to finalize everything
|
|
||||||
$this->jqGridDataFinalize($params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtual: jqGridData* functions
|
|
||||||
* - These set up the context for the jqGrid data, and will
|
|
||||||
* need to be overridden in the derived class for anything
|
|
||||||
* other than the most basic of grids.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function jqGridDataSetup(&$params) {
|
|
||||||
// Assume we're debugging.
|
|
||||||
// The actual jqGrid request will set this to false
|
|
||||||
$debug = true;
|
|
||||||
|
|
||||||
if (isset($this->passedArgs['debug']))
|
|
||||||
$debug = $this->passedArgs['debug'];
|
|
||||||
|
|
||||||
$params['debug'] = $debug;
|
|
||||||
|
|
||||||
if (!$debug) {
|
|
||||||
$this->layout = null;
|
|
||||||
$this->autoLayout = false;
|
|
||||||
$this->autoRender = false;
|
|
||||||
Configure::write('debug', '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establish some defaults (except for serialized items)
|
|
||||||
$params = array_merge(array('page' => 1,
|
|
||||||
'rows' => 20),
|
|
||||||
$params);
|
|
||||||
|
|
||||||
// Unserialize our complex structure of fields
|
|
||||||
// This SHOULD always be set, except when debugging
|
|
||||||
if (isset($params['fields']))
|
|
||||||
$params['fields'] = unserialize($params['fields']);
|
|
||||||
else
|
|
||||||
$params['fields'] = array($this->{$this->modelClass}->alias
|
|
||||||
.'.'.
|
|
||||||
$this->{$this->modelClass}->primarKey);
|
|
||||||
|
|
||||||
// Unserialize the list of ids, if present.
|
|
||||||
if (isset($params['custom']))
|
|
||||||
$params['custom'] = unserialize($params['custom']);
|
|
||||||
else
|
|
||||||
$params['custom'] = null;
|
|
||||||
|
|
||||||
// Unserialize the list of ids, if present.
|
|
||||||
if (isset($params['idlist']))
|
|
||||||
$params['idlist'] = unserialize($params['idlist']);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataModel(&$params) {
|
|
||||||
return $this->{$this->modelClass};
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataCountTables(&$params, &$model) {
|
|
||||||
// If not overridden, we use the same tables to
|
|
||||||
// perform our count as we do to for the actual query
|
|
||||||
return $this->jqGridDataTables($params, $model);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataTables(&$params, &$model) {
|
|
||||||
return array('contain' => false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataCountConditions(&$params, &$model) {
|
|
||||||
return $this->jqGridDataConditions($params, $model);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataConditions(&$params, &$model) {
|
|
||||||
$searches = array();
|
|
||||||
|
|
||||||
if (isset($params['_search']) && $params['_search'] === 'true') {
|
|
||||||
if (isset($params['searchOper'])) {
|
|
||||||
$searches[] = array('op' => $params['searchOper'],
|
|
||||||
'field' => $params['searchField'],
|
|
||||||
'value' => $params['searchString']);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// DOH! Crappy mechanism puts toolbar search terms
|
|
||||||
// directly into params as name/value pairs. No
|
|
||||||
// way to know which elements of params are search
|
|
||||||
// terms, so skipping this at the moment.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif (isset($params['filt']) && $params['filt']) {
|
|
||||||
$searches[] = array('op' => 'bw',
|
|
||||||
'field' => $params['filtField'],
|
|
||||||
'value' => $params['filtValue']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ops = array('eq' => array('op' => null, 'pre' => '', 'post' => ''),
|
|
||||||
'ne' => array('op' => '<>', 'pre' => '', 'post' => ''),
|
|
||||||
'lt' => array('op' => '<', 'pre' => '', 'post' => ''),
|
|
||||||
'le' => array('op' => '<=', 'pre' => '', 'post' => ''),
|
|
||||||
'gt' => array('op' => '>', 'pre' => '', 'post' => ''),
|
|
||||||
'ge' => array('op' => '>=', 'pre' => '', 'post' => ''),
|
|
||||||
'bw' => array('op' => 'LIKE', 'pre' => '', 'post' => '%'),
|
|
||||||
'ew' => array('op' => 'LIKE', 'pre' => '%', 'post' => ''),
|
|
||||||
'cn' => array('op' => 'LIKE', 'pre' => '%', 'post' => '%'),
|
|
||||||
);
|
|
||||||
|
|
||||||
$conditions = array();
|
|
||||||
foreach ($searches AS $search) {
|
|
||||||
$op = $ops[$search['op']];
|
|
||||||
$field = $search['field'] . ($op['op'] ? ' '.$op['op'] : '');
|
|
||||||
$value = $op['pre'] . $search['value']. $op['post'];
|
|
||||||
$conditions[] = array($field => $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($params['action']) && $params['action'] === 'idlist') {
|
|
||||||
if (count($params['idlist']))
|
|
||||||
$conditions[] = array($model->alias.'.'.$model->primaryKey => $params['idlist']);
|
|
||||||
else
|
|
||||||
$conditions[] = '0=1';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $conditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataFields(&$params, &$model) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataCountGroup(&$params, &$model) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataGroup(&$params, &$model) {
|
|
||||||
return $model->alias.'.'.$model->primaryKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
|
||||||
return $index ? array($index .' '. $direction) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataLimit(&$params, &$model, $start, $limit) {
|
|
||||||
return $start . ', ' . $limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataRecordCount(&$params, &$model, $query) {
|
|
||||||
return $model->find('count', $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataRecords(&$params, &$model, $query) {
|
|
||||||
return $model->find('all', $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridRecordsPostProcess(&$params, &$model, &$records) {
|
|
||||||
$model_alias = $model->alias;
|
|
||||||
$id = $model->primaryKey;
|
|
||||||
|
|
||||||
$subtotals = array();
|
|
||||||
foreach ($params['fields'] AS $field) {
|
|
||||||
if (preg_match('/subtotal-(.*)$/', $field, $matches))
|
|
||||||
$subtotals[] = array('field' => $matches[1],
|
|
||||||
'name' => $field,
|
|
||||||
'amount' => 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($records AS &$record) {
|
|
||||||
$record['jqGrid_id'] = $record[$model_alias][$id];
|
|
||||||
// Add the calculated fields (if any), to the model fields
|
|
||||||
if (isset($record[0])) {
|
|
||||||
$record[$model_alias] += $record[0];
|
|
||||||
unset($record[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($subtotals AS &$subtotal) {
|
|
||||||
$field = $subtotal['field'];
|
|
||||||
if (preg_match("/\./", $field)) {
|
|
||||||
list($tbl, $col) = explode(".", $field);
|
|
||||||
$record['subtotal-'.$tbl][$col] =
|
|
||||||
($subtotal['amount'] += $record[$tbl][$col]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$record[$model->alias]['subtotal-'.$field] =
|
|
||||||
($subtotal['amount'] += $record[$model->alias][$field]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEBUG PURPOSES ONLY!
|
|
||||||
//$params['records'] = $records;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
|
||||||
// Don't create any links if ordered not to.
|
|
||||||
if (isset($params['nolinks']))
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach ($links AS $table => $fields) {
|
|
||||||
$special = array('controller', 'id');
|
|
||||||
$controller = Inflector::pluralize(Inflector::underscore($table));
|
|
||||||
$id = 'id';
|
|
||||||
extract(array_intersect_key($fields, array_flip($special)));
|
|
||||||
foreach ($records AS &$record) {
|
|
||||||
if (!isset($record[$table]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (array_diff_key($fields, array_flip($special)) AS $field) {
|
|
||||||
if (!isset($record[$table][$id]) || !isset($record[$table][$field]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// DEBUG PURPOSES ONLY!
|
|
||||||
//$params['linkrecord'][] = compact('table', 'field', 'id', 'controller', 'record');
|
|
||||||
$record[$table][$field] =
|
|
||||||
'<A HREF="' .
|
|
||||||
Router::url(array('controller' => $controller,
|
|
||||||
'action' => 'view',
|
|
||||||
$record[$table][$id])) .
|
|
||||||
'">' .
|
|
||||||
$record[$table][$field] .
|
|
||||||
'</A>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOutputHeader(&$params, &$model) {
|
|
||||||
if ($params['debug']) {
|
|
||||||
ob_start();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
header("Content-type: text/xml;charset=utf-8");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOutputSummary(&$params, &$model, $page, $total, $records) {
|
|
||||||
echo " <params><![CDATA[\n" . print_r($params, true) . "\n]]></params>\n";
|
|
||||||
echo " <page>$page</page>\n";
|
|
||||||
echo " <total>$total</total>\n";
|
|
||||||
echo " <records>$records</records>\n";
|
|
||||||
|
|
||||||
if (isset($params['userdata'])) {
|
|
||||||
foreach ($params['userdata'] AS $field => $value)
|
|
||||||
echo ' <userdata name="'.$field.'">' . "{$value}</userdata>\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOutputRecords(&$params, &$model, &$records) {
|
|
||||||
$id_field = 'jqGrid_id';
|
|
||||||
foreach ($records AS $record) {
|
|
||||||
$this->jqGridDataOutputRecord($params, $model, $record,
|
|
||||||
$record[$id_field], $params['fields']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOutputRecord(&$params, &$model, &$record, $id, $fields) {
|
|
||||||
echo " <row id='$id'>\n";
|
|
||||||
foreach ($fields AS $field) {
|
|
||||||
$this->jqGridDataOutputRecordField($params, $model, $record, $field);
|
|
||||||
}
|
|
||||||
echo " </row>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOutputRecordField(&$params, &$model, &$record, $field) {
|
|
||||||
if (preg_match("/\./", $field)) {
|
|
||||||
list($tbl, $col) = explode(".", $field);
|
|
||||||
$data = $record[$tbl][$col];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$data = $record[$model->alias][$field];
|
|
||||||
}
|
|
||||||
$this->jqGridDataOutputRecordCell($params, $model, $record, $field, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOutputRecordCell(&$params, &$model, &$record, $field, $data) {
|
|
||||||
// be sure to put text data in CDATA
|
|
||||||
if (preg_match("/^\d*$/", $data))
|
|
||||||
echo " <cell>$data</cell>\n";
|
|
||||||
else
|
|
||||||
echo " <cell><![CDATA[$data]]></cell>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataFinalize(&$params) {
|
|
||||||
if ($params['debug']) {
|
|
||||||
$xml = ob_get_contents();
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
$xml = preg_replace("/&/", "&", $xml);
|
|
||||||
$xml = preg_replace("/</", "<", $xml);
|
|
||||||
$xml = preg_replace("/>/", ">", $xml);
|
|
||||||
|
|
||||||
echo ("\n<PRE>\n$xml\n</PRE>\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
213
app_model.php
213
app_model.php
@@ -1,213 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* SVN FILE: $Id: app_model.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application model for Cake.
|
|
||||||
*
|
|
||||||
* This file is application-wide model file. You can put all
|
|
||||||
* application-wide model-related methods here.
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
|
||||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
*
|
|
||||||
* Licensed under The MIT License
|
|
||||||
* Redistributions of files must retain the above copyright notice.
|
|
||||||
*
|
|
||||||
* @filesource
|
|
||||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
|
||||||
* @package cake
|
|
||||||
* @subpackage cake.app
|
|
||||||
* @since CakePHP(tm) v 0.2.9
|
|
||||||
* @version $Revision: 7945 $
|
|
||||||
* @modifiedby $LastChangedBy: gwoo $
|
|
||||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Application model for Cake.
|
|
||||||
*
|
|
||||||
* Add your application-wide methods in the class below, your models
|
|
||||||
* will inherit them.
|
|
||||||
*
|
|
||||||
* @package cake
|
|
||||||
* @subpackage cake.app
|
|
||||||
*/
|
|
||||||
class AppModel extends Model {
|
|
||||||
|
|
||||||
var $actsAs = array('Containable', 'Linkable');
|
|
||||||
var $useNullForEmpty = true;
|
|
||||||
var $formatDateFields = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Enum Values
|
|
||||||
* Snippet v0.1.3
|
|
||||||
* http://cakeforge.org/snippet/detail.php?type=snippet&id=112
|
|
||||||
*
|
|
||||||
* Gets the enum values for MySQL 4 and 5 to use in selectTag()
|
|
||||||
*/
|
|
||||||
function getEnumValues($columnName=null, $tableName=null)
|
|
||||||
{
|
|
||||||
if ($columnName==null) { return array(); } //no field specified
|
|
||||||
|
|
||||||
if (!isset($tableName)) {
|
|
||||||
//Get the name of the table
|
|
||||||
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
|
||||||
$tableName = $db->fullTableName($this, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get the values for the specified column (database and version specific, needs testing)
|
|
||||||
$result = $this->query("SHOW COLUMNS FROM {$tableName} LIKE '{$columnName}'");
|
|
||||||
|
|
||||||
//figure out where in the result our Types are (this varies between mysql versions)
|
|
||||||
$types = null;
|
|
||||||
if ( isset( $result[0]['COLUMNS']['Type'] ) ) { //MySQL 5
|
|
||||||
$types = $result[0]['COLUMNS']['Type']; $default = $result[0]['COLUMNS']['Default'];
|
|
||||||
}
|
|
||||||
elseif ( isset( $result[0][0]['Type'] ) ) { //MySQL 4
|
|
||||||
$types = $result[0][0]['Type']; $default = $result[0][0]['Default'];
|
|
||||||
}
|
|
||||||
else { //types return not accounted for
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get the values
|
|
||||||
return array_flip(array_merge(array(''), // MySQL sets 0 to be the empty string
|
|
||||||
explode("','", strtoupper(preg_replace("/(enum)\('(.+?)'\)/","\\2", $types)))
|
|
||||||
));
|
|
||||||
} //end getEnumValues
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: nameToID
|
|
||||||
* - Returns the ID of the named item
|
|
||||||
*/
|
|
||||||
function nameToID($name) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$item = $this->find('first', array
|
|
||||||
('recursive' => -1,
|
|
||||||
'conditions' => compact('name'),
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
if ($item) {
|
|
||||||
$item = current($item);
|
|
||||||
return $item['id'];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: statMerge
|
|
||||||
* - Merges summary data from $b into $a
|
|
||||||
*/
|
|
||||||
|
|
||||||
function statsMerge (&$a, $b) {
|
|
||||||
if (!isset($b))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!isset($a)) {
|
|
||||||
$a = $b;
|
|
||||||
}
|
|
||||||
elseif (!is_array($a) && !is_array($b)) {
|
|
||||||
$a += $b;
|
|
||||||
}
|
|
||||||
elseif (is_array($a) && is_array($b)) {
|
|
||||||
foreach (array_intersect_key($a, $b) AS $k => $v)
|
|
||||||
{
|
|
||||||
if (preg_match("/^sp\./", $k))
|
|
||||||
$a[$k] .= '; ' . $b[$k];
|
|
||||||
else
|
|
||||||
$this->statsMerge($a[$k], $b[$k]);
|
|
||||||
}
|
|
||||||
$a = array_merge($a, array_diff_key($b, $a));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
die ("Can't yet merge array and non-array stats");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function recursive_array_replace($find, $replace, &$data) {
|
|
||||||
if (!isset($data))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (is_array($data)) {
|
|
||||||
foreach ($data as $key => &$value) {
|
|
||||||
$this->recursive_array_replace($find, $replace, $value);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($replace))
|
|
||||||
$data = preg_replace($find, $replace, $data);
|
|
||||||
elseif (preg_match($find, $data))
|
|
||||||
$data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function beforeSave() {
|
|
||||||
/* pr(array('class' => $this->name, */
|
|
||||||
/* 'alias' => $this->alias, */
|
|
||||||
/* 'function' => 'AppModel::beforeSave')); */
|
|
||||||
|
|
||||||
// Replace all empty strings with NULL.
|
|
||||||
// If a particular model doesn't like this, they'll have to
|
|
||||||
// override the behavior, or set useNullForEmpty to false.
|
|
||||||
if ($this->useNullForEmpty)
|
|
||||||
$this->recursive_array_replace("/^\s*$/", null, $this->data);
|
|
||||||
|
|
||||||
if ($this->formatDateFields) {
|
|
||||||
$alias = $this->alias;
|
|
||||||
|
|
||||||
foreach ($this->_schema AS $field => $info) {
|
|
||||||
if ($info['type'] == 'date' || $info['type'] == 'timestamp') {
|
|
||||||
if (isset($this->data[$alias][$field])) {
|
|
||||||
/* pr("Fix Date for '$alias'.'$field'; current value = " . */
|
|
||||||
/* "'{$this->data[$alias][$field]}'"); */
|
|
||||||
if ($this->data[$alias][$field] === 'CURRENT_TIMESTAMP')
|
|
||||||
// Seems CakePHP is broken for the default timestamp.
|
|
||||||
// It tries to automagically set the value to CURRENT_TIMESTAMP
|
|
||||||
// which is wholly rejected by MySQL. Just put it back to NULL
|
|
||||||
// and let the SQL engine deal with the defaults... it's not
|
|
||||||
// Cake's place to do this anyway :-/
|
|
||||||
$this->data[$alias][$field] = null;
|
|
||||||
else
|
|
||||||
$this->data[$alias][$field] =
|
|
||||||
$this->dateFormatBeforeSave($this->data[$alias][$field]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: dateFormatBeforeSave
|
|
||||||
* - convert dates to database format
|
|
||||||
*/
|
|
||||||
|
|
||||||
function dateFormatBeforeSave($dateString) {
|
|
||||||
/* $time = ''; */
|
|
||||||
/* if (preg_match('/(\d+(:\d+))/', $dateString, $match)) */
|
|
||||||
/* $time = ' '.$match[1]; */
|
|
||||||
/* $dateString = preg_replace('/(\d+(:\d+))/', '', $dateString); */
|
|
||||||
/* return date('Y-m-d', strtotime($dateString)) . $time; */
|
|
||||||
|
|
||||||
if (preg_match('/:/', $dateString))
|
|
||||||
return date('Y-m-d H:i:s', strtotime($dateString));
|
|
||||||
else
|
|
||||||
return date('Y-m-d', strtotime($dateString));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* SVN FILE: $Id: bootstrap.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
|
||||||
/**
|
|
||||||
* Short description for file.
|
|
||||||
*
|
|
||||||
* Long description for file
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
|
||||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
*
|
|
||||||
* Licensed under The MIT License
|
|
||||||
* Redistributions of files must retain the above copyright notice.
|
|
||||||
*
|
|
||||||
* @filesource
|
|
||||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
|
||||||
* @package cake
|
|
||||||
* @subpackage cake.app.config
|
|
||||||
* @since CakePHP(tm) v 0.10.8.2117
|
|
||||||
* @version $Revision: 7945 $
|
|
||||||
* @modifiedby $LastChangedBy: gwoo $
|
|
||||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php is loaded
|
|
||||||
* This is an application wide file to load any function that is not used within a class define.
|
|
||||||
* You can also use this to include or require any files in your application.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* $modelPaths = array('full path to models', 'second full path to models', 'etc...');
|
|
||||||
* $viewPaths = array('this path to views', 'second full path to views', 'etc...');
|
|
||||||
* $controllerPaths = array('this path to controllers', 'second full path to controllers', 'etc...');
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
//EOF
|
|
||||||
?>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
/* SVN FILE: $Id: routes.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
|
||||||
/**
|
|
||||||
* Short description for file.
|
|
||||||
*
|
|
||||||
* In this file, you set up routes to your controllers and their actions.
|
|
||||||
* Routes are very important mechanism that allows you to freely connect
|
|
||||||
* different urls to chosen controllers and their actions (functions).
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
|
||||||
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
*
|
|
||||||
* Licensed under The MIT License
|
|
||||||
* Redistributions of files must retain the above copyright notice.
|
|
||||||
*
|
|
||||||
* @filesource
|
|
||||||
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
|
||||||
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
|
||||||
* @package cake
|
|
||||||
* @subpackage cake.app.config
|
|
||||||
* @since CakePHP(tm) v 0.2.9
|
|
||||||
* @version $Revision: 7945 $
|
|
||||||
* @modifiedby $LastChangedBy: gwoo $
|
|
||||||
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
|
||||||
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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'));
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
function sideMenuLinks() {
|
|
||||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: index / asset / liability / equity / income / expense / all
|
|
||||||
* - Generate a chart of accounts
|
|
||||||
*/
|
|
||||||
|
|
||||||
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'); }
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtuals: jqGridData
|
|
||||||
* - With the application controller handling the jqGridData 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) {
|
|
||||||
return parent::jqGridDataTables($params, $model);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataTables(&$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 jqGridDataConditions(&$params, &$model) {
|
|
||||||
$conditions = parent::jqGridDataConditions($params, $model);
|
|
||||||
|
|
||||||
if (in_array($params['action'], array('asset', 'liability', 'equity', 'income', 'expense'))) {
|
|
||||||
$conditions[] = array('Account.type' => strtoupper($params['action']));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $conditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
|
||||||
$links['Account'] = array('name');
|
|
||||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: newledger
|
|
||||||
* - Close the current account ledger and create a new one,
|
|
||||||
* carrying forward any balance if necessary.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function newledger($id = null) {
|
|
||||||
if (!$this->Account->closeCurrentLedger($id)) {
|
|
||||||
$this->Session->setFlash(__('Unable to create new Ledger.', true));
|
|
||||||
}
|
|
||||||
if ($id)
|
|
||||||
$this->redirect(array('action'=>'view', $id));
|
|
||||||
else
|
|
||||||
$this->redirect(array('action'=>'index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: collected
|
|
||||||
* - Displays the items actually collected for the period
|
|
||||||
* e.g. How much was collected in rent from 4/1/09 - 5/1/09
|
|
||||||
*/
|
|
||||||
function collected($id = null) {
|
|
||||||
if (!$id) {
|
|
||||||
$this->Session->setFlash(__('Invalid Item.', true));
|
|
||||||
$this->redirect(array('action'=>'index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$payment_accounts = $this->Account->collectableAccounts();
|
|
||||||
//$payment_accounts[$this->Account->nameToID('Bank')] = 'Bank';
|
|
||||||
$default_accounts = array_diff_key($payment_accounts,
|
|
||||||
array($this->Account->concessionAccountID() => 1));
|
|
||||||
$this->set(compact('payment_accounts', 'default_accounts'));
|
|
||||||
|
|
||||||
$this->Account->recursive = -1;
|
|
||||||
$account = $this->Account->read(null, $id);
|
|
||||||
$account = $account['Account'];
|
|
||||||
|
|
||||||
$title = ($account['name'] . ': Collected Report');
|
|
||||||
$this->set(compact('account', 'title'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: deposit
|
|
||||||
* - Prepares the books for a bank deposit
|
|
||||||
*/
|
|
||||||
function deposit() {
|
|
||||||
if ($this->data) {
|
|
||||||
// Action the close based on provided data
|
|
||||||
//pr($this->data);
|
|
||||||
|
|
||||||
// Get data about each closed ledger.
|
|
||||||
$deposit = array('total' => 0, 'ledgers' => array());
|
|
||||||
foreach ($this->data['Tillable']['Ledger'] AS $ledger_id => $ledger) {
|
|
||||||
if (!$ledger['checked'])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$ledger_entries =
|
|
||||||
$this->Account->Ledger->find
|
|
||||||
('all',
|
|
||||||
array('link' => array
|
|
||||||
('Account' =>
|
|
||||||
array('fields' => array('name')),
|
|
||||||
|
|
||||||
'LedgerEntry' =>
|
|
||||||
array('fields' => array('id', 'amount'),
|
|
||||||
|
|
||||||
'MonetarySource' =>
|
|
||||||
array('fields' => array('name')),
|
|
||||||
|
|
||||||
'Customer' =>
|
|
||||||
array('fields' => array('name')),
|
|
||||||
|
|
||||||
//'Transaction' =>
|
|
||||||
//array('fields' => array('stamp')),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'fields' => false,
|
|
||||||
'conditions' => array(array('Ledger.id' => $ledger_id),
|
|
||||||
array('LedgerEntry.id IS NOT NULL'),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
$deposit['total'] += $ledger['amount'];
|
|
||||||
$deposit['ledgers'][] = array('id' => $ledger_id,
|
|
||||||
'name' => $ledger['account_name'],
|
|
||||||
'total' => $ledger['amount'],
|
|
||||||
'entries' => $ledger_entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the accounting work necessary to close the
|
|
||||||
// monetary ledgers and deposit into the bank account.
|
|
||||||
$this->Account->closeAndDeposit($deposit['ledgers'], $this->data['Deposit']['Account']['id']);
|
|
||||||
|
|
||||||
$title = 'Account: Deposit Slip';
|
|
||||||
$this->set(compact('title', 'deposit'));
|
|
||||||
$this->render('deposit_slip');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare a close page...
|
|
||||||
$tillable_account = $this->Account->relatedAccounts('tillable');
|
|
||||||
$depositable_account = $this->Account->relatedAccounts('depositable');
|
|
||||||
|
|
||||||
foreach ($tillable_account AS &$acct) {
|
|
||||||
$acct['Account']['stats'] = $this->Account->stats($acct['Account']['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$title = 'Account: Prepare Deposit';
|
|
||||||
$this->set(compact('title', 'tillable_account', 'depositable_account'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: view
|
|
||||||
* - Displays information about a specific account
|
|
||||||
*/
|
|
||||||
|
|
||||||
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')),
|
|
||||||
|
|
||||||
'Ledger' =>
|
|
||||||
array('Close' => array
|
|
||||||
('order' => array('Close.stamp' => 'DESC'))),
|
|
||||||
),
|
|
||||||
'conditions' => array(array('Account.id' => $id)),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get all ledger entries of the CURRENT ledger
|
|
||||||
$entries = $this->Account->findLedgerEntries($id);
|
|
||||||
$account['CurrentLedger']['LedgerEntry'] = $entries['Entries'];
|
|
||||||
|
|
||||||
// Summarize each ledger
|
|
||||||
foreach($account['Ledger'] AS &$ledger)
|
|
||||||
$ledger = array_merge($ledger,
|
|
||||||
$this->Account->Ledger->stats($ledger['id']));
|
|
||||||
|
|
||||||
// Obtain stats across ALL ledgers for the summary infobox
|
|
||||||
$stats = $this->Account->stats($id, true);
|
|
||||||
$stats = $stats['Ledger'];
|
|
||||||
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Operations', 'header' => true);
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'New Ledger', 'url' => array('action' => 'newledger', $id));
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Collected', 'url' => array('action' => 'collected', $id));
|
|
||||||
|
|
||||||
// Prepare to render
|
|
||||||
$title = 'Account: ' . $account['Account']['name'];
|
|
||||||
$this->set(compact('account', 'title', 'stats'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,485 +0,0 @@
|
|||||||
<?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
|
|
||||||
*/
|
|
||||||
function sideMenuLinks() {
|
|
||||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: index / current / past / all
|
|
||||||
* - Creates a list of customers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function index() { $this->current(); }
|
|
||||||
function current() { $this->jqGridView('Current Tenants', 'current'); }
|
|
||||||
function past() { $this->jqGridView('Past Tenants'); }
|
|
||||||
function all() { $this->jqGridView('All Customers'); }
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtuals: jqGridData
|
|
||||||
* - With the application controller handling the jqGridData 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) {
|
|
||||||
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());
|
|
||||||
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 jqGridDataConditions(&$params, &$model) {
|
|
||||||
$conditions = parent::jqGridDataConditions($params, $model);
|
|
||||||
|
|
||||||
if ($params['action'] === 'current') {
|
|
||||||
$conditions[] = 'CurrentLease.id IS NOT NULL';
|
|
||||||
}
|
|
||||||
elseif ($params['action'] === 'past') {
|
|
||||||
$conditions[] = 'CurrentLease.id IS NULL';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $conditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
|
||||||
$order = array();
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
|
||||||
|
|
||||||
if ($index !== 'PrimaryContact.last_name')
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model,
|
|
||||||
'PrimaryContact.last_name', $direction);
|
|
||||||
if ($index !== 'PrimaryContact.first_name')
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model,
|
|
||||||
'PrimaryContact.first_name', $direction);
|
|
||||||
if ($index !== 'Customer.id')
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model,
|
|
||||||
'Customer.id', $direction);
|
|
||||||
|
|
||||||
return $order;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
$links['Customer'] = array('name');
|
|
||||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: move_in
|
|
||||||
* - Sets up the move-in page for the given customer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function move_in($id = null) {
|
|
||||||
$customer = array();
|
|
||||||
$unit = array();
|
|
||||||
|
|
||||||
if (isset($id)) {
|
|
||||||
$this->Customer->recursive = -1;
|
|
||||||
$customer = current($this->Customer->read(null, $id));
|
|
||||||
}
|
|
||||||
$this->set(compact('customer', 'unit'));
|
|
||||||
|
|
||||||
$title = 'Customer Move-In';
|
|
||||||
$this->set(compact('title'));
|
|
||||||
$this->render('/leases/move');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: move_out
|
|
||||||
* - prepare to move a customer out of one of their units
|
|
||||||
*/
|
|
||||||
|
|
||||||
function move_out($id) {
|
|
||||||
|
|
||||||
$customer = $this->Customer->find
|
|
||||||
('first', array
|
|
||||||
('contain' => array
|
|
||||||
(// Models
|
|
||||||
'Lease' =>
|
|
||||||
array('conditions' => array('Lease.moveout_date' => null),
|
|
||||||
// Models
|
|
||||||
'Unit' =>
|
|
||||||
array('order' => array('sort_order'),
|
|
||||||
'fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'conditions' => array('Customer.id' => $id),
|
|
||||||
));
|
|
||||||
$this->set('customer', $lease['Customer']);
|
|
||||||
$this->set('unit', array());
|
|
||||||
|
|
||||||
$redirect = array('controller' => 'customers',
|
|
||||||
'action' => 'view',
|
|
||||||
$id);
|
|
||||||
|
|
||||||
$title = $customer['Customer']['name'] . ': Prepare Move-Out';
|
|
||||||
$this->set(compact('title', 'customer', 'redirect'));
|
|
||||||
$this->render('/leases/move');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: view
|
|
||||||
* - Displays information about a specific customer
|
|
||||||
*/
|
|
||||||
|
|
||||||
function view($id = null) {
|
|
||||||
if (!$id) {
|
|
||||||
$this->Session->setFlash(__('Invalid Item.', true));
|
|
||||||
$this->redirect(array('action'=>'index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$customer = $this->Customer->details($id);
|
|
||||||
|
|
||||||
$outstanding_balance = $customer['stats']['balance'];
|
|
||||||
$outstanding_deposit = $customer['deposits']['summary']['balance'];
|
|
||||||
|
|
||||||
// Figure out if this customer has any non-closed leases
|
|
||||||
$show_moveout = false;
|
|
||||||
$show_payment = false;
|
|
||||||
foreach ($customer['Lease'] AS $lease) {
|
|
||||||
if (!isset($lease['close_date']))
|
|
||||||
$show_payment = true;
|
|
||||||
if (!isset($lease['moveout_date']))
|
|
||||||
$show_moveout = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up dynamic menu items
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Operations', 'header' => true);
|
|
||||||
|
|
||||||
$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) {
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Payment',
|
|
||||||
'url' => array('action' => 'receipt',
|
|
||||||
$id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare to render.
|
|
||||||
$title = 'Customer: ' . $customer['Customer']['name'];
|
|
||||||
$this->set(compact('customer', 'title',
|
|
||||||
'outstanding_balance',
|
|
||||||
'outstanding_deposit'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: edit
|
|
||||||
* - Edit customer information
|
|
||||||
*/
|
|
||||||
|
|
||||||
function edit($id = null) {
|
|
||||||
if (isset($this->data)) {
|
|
||||||
// Check to see if the operation was cancelled.
|
|
||||||
if (isset($this->params['form']['cancel'])) {
|
|
||||||
if (isset($this->data['Customer']['id']))
|
|
||||||
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
|
|
||||||
else
|
|
||||||
$this->redirect(array('action'=>'index'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we have at least one contact
|
|
||||||
if (!isset($this->data['Contact']) || count($this->data['Contact']) == 0) {
|
|
||||||
$this->Session->setFlash("MUST SPECIFY AT LEAST ONE CONTACT", true);
|
|
||||||
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure there is a primary contact
|
|
||||||
if (!isset($this->data['Customer']['primary_contact_entry'])) {
|
|
||||||
$this->Session->setFlash("MUST SPECIFY A PRIMARY CONTACT", true);
|
|
||||||
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go through each customer and strip the bogus ID if new
|
|
||||||
foreach ($this->data['Contact'] AS &$contact) {
|
|
||||||
if (isset($contact['source']) && $contact['source'] === 'new')
|
|
||||||
unset($contact['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the customer and all associated data
|
|
||||||
if (!$this->Customer->saveCustomer($this->data['Customer']['id'],
|
|
||||||
$this->data,
|
|
||||||
$this->data['Customer']['primary_contact_entry'])) {
|
|
||||||
$this->Session->setFlash("CUSTOMER SAVE FAILED", true);
|
|
||||||
pr("CUSTOMER SAVE FAILED");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If existing customer, then view it. Otherwise, since
|
|
||||||
// this is a new customer, go to the move in screen.
|
|
||||||
if ($this->data['Customer']['id'])
|
|
||||||
$this->redirect(array('action'=>'view', $this->Customer->id));
|
|
||||||
else
|
|
||||||
$this->redirect(array('action'=>'move_in', $this->Customer->id));
|
|
||||||
|
|
||||||
// For debugging, only if the redirects above have been
|
|
||||||
// commented out, otherwise this section isn't reached.
|
|
||||||
$this->render('/empty');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($id) {
|
|
||||||
$this->data = $this->Customer->details($id);
|
|
||||||
$title = 'Customer: ' . $this->data['Customer']['name'] . " : Edit";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$title = "Enter New Customer";
|
|
||||||
$this->data = array('Contact' => array(), 'PrimaryContact' => null);
|
|
||||||
}
|
|
||||||
|
|
||||||
$contact_types = array_flip($this->Customer->ContactsCustomer->getEnumValues('type'));
|
|
||||||
unset($contact_types[0]);
|
|
||||||
$contact_types = array_combine($contact_types, $contact_types);
|
|
||||||
$this->set(compact('contact_types'));
|
|
||||||
|
|
||||||
$contacts = $this->Customer->Contact->contactList();
|
|
||||||
$this->set(compact('contacts'));
|
|
||||||
|
|
||||||
// Prepare to render.
|
|
||||||
//pr($this->data);
|
|
||||||
$this->set(compact('title'));
|
|
||||||
$this->render('edit');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: add
|
|
||||||
* - Add a new customer
|
|
||||||
*/
|
|
||||||
|
|
||||||
function add() {
|
|
||||||
$this->edit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: receipt
|
|
||||||
* - Sets up the receipt entry page for the given customer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function receipt($id = null) {
|
|
||||||
if (isset($id)) {
|
|
||||||
$this->Customer->recursive = -1;
|
|
||||||
$customer = $this->Customer->read(null, $id);
|
|
||||||
$customer = $customer['Customer'];
|
|
||||||
$unreconciled = $this->Customer->findUnreconciledLedgerEntries($id);
|
|
||||||
$charges = $unreconciled['debit'];
|
|
||||||
}
|
|
||||||
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'));
|
|
||||||
|
|
||||||
$title = ($customer['name'] . ': Payment Entry');
|
|
||||||
$this->set(compact('customer', 'charges', 'title'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: refund
|
|
||||||
* - Refunds customer charges
|
|
||||||
*/
|
|
||||||
|
|
||||||
function refund() {
|
|
||||||
$entries = $this->Customer->LedgerEntry->find
|
|
||||||
('all', array
|
|
||||||
('contain' => false,
|
|
||||||
'conditions' => array('LedgerEntry.id' =>
|
|
||||||
//array(199,200,201)
|
|
||||||
61
|
|
||||||
),
|
|
||||||
));
|
|
||||||
pr(compact('entries'));
|
|
||||||
|
|
||||||
$this->Customer->LedgerEntry->reverse($entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: unreconciledEntries
|
|
||||||
* - returns the list of unreconciled entries
|
|
||||||
*/
|
|
||||||
|
|
||||||
function unreconciled($id) {
|
|
||||||
|
|
||||||
//$this->layout = 'ajax';
|
|
||||||
$this->layout = null;
|
|
||||||
$this->autoLayout = false;
|
|
||||||
$this->autoRender = false;
|
|
||||||
Configure::write('debug', '0');
|
|
||||||
header("Content-type: text/xml;charset=utf-8");
|
|
||||||
|
|
||||||
App::import('Helper', 'Xml');
|
|
||||||
$xml = new XmlHelper();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,507 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
function sideMenuLinks() {
|
|
||||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: index / active / closed / all
|
|
||||||
* - 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'); }
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtuals: jqGridData
|
|
||||||
* - With the application controller handling the jqGridData 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) {
|
|
||||||
return array
|
|
||||||
('link' => array('Unit' => array('fields' => array('Unit.id', 'Unit.name')),
|
|
||||||
'Customer' => array('fields' => array('Customer.id', 'Customer.name'))));
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataTables(&$params, &$model) {
|
|
||||||
$link = $this->jqGridDataCountTables($params, $model);
|
|
||||||
$link['link']['LedgerEntry'] = array('fields' => array());
|
|
||||||
$link['link']['LedgerEntry']['Ledger'] = array('fields' => array());
|
|
||||||
$link['link']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
|
||||||
// INNER JOIN would be great, as it would ensure we're only looking
|
|
||||||
// at the ledger entries that we truly want. However, this also
|
|
||||||
// removes from the query any leases that do not yet have a ledger
|
|
||||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
|
||||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
|
||||||
// implemented with the 'joins' tag, and is not available through
|
|
||||||
// the Linkable behavior interface.
|
|
||||||
//$link['link']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
|
||||||
$link['link']['LedgerEntry']['Ledger']['Account']['conditions']
|
|
||||||
= array('Account.id' =>
|
|
||||||
$this->Lease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
|
||||||
return $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataFields(&$params, &$model) {
|
|
||||||
$db = &$model->getDataSource();
|
|
||||||
$fields = $db->fields($model, $model->alias);
|
|
||||||
$fields[] = ("SUM(IF(Account.id IS NULL, 0," .
|
|
||||||
" IF(LedgerEntry.debit_ledger_id = Account.id," .
|
|
||||||
" 1, -1))" .
|
|
||||||
" * IF(LedgerEntry.amount IS NULL, 0, LedgerEntry.amount))" .
|
|
||||||
" AS 'balance'");
|
|
||||||
return $fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataConditions(&$params, &$model) {
|
|
||||||
$conditions = parent::jqGridDataConditions($params, $model);
|
|
||||||
|
|
||||||
if ($params['action'] === 'active') {
|
|
||||||
$conditions[] = 'Lease.close_date IS NULL';
|
|
||||||
}
|
|
||||||
elseif ($params['action'] === 'closed') {
|
|
||||||
$conditions[] = 'Lease.close_date IS NOT NULL';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $conditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
|
||||||
// Do not sort by number, which is type varchar and
|
|
||||||
// sorts on an ascii basis. Sort by ID instead.
|
|
||||||
if ($index === 'Lease.number')
|
|
||||||
$index = 'Lease.id';
|
|
||||||
|
|
||||||
// Instead of sorting by name, sort by defined order
|
|
||||||
if ($index === 'Unit.name')
|
|
||||||
$index = 'Unit.sort_order';
|
|
||||||
|
|
||||||
$order = array();
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
|
||||||
|
|
||||||
// If sorting by anything other than id/number
|
|
||||||
// add sorting by id as a secondary condition.
|
|
||||||
if ($index !== 'Lease.id' && $index !== 'Lease.number')
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model,
|
|
||||||
'Lease.id', $direction);
|
|
||||||
|
|
||||||
return $order;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* function jqGridRecordsPostProcess(&$params, &$model, &$records) { */
|
|
||||||
/* foreach ($records AS &$record) { */
|
|
||||||
/* $record['Lease']['through_date'] */
|
|
||||||
/* = $this->Lease->rentChargeThrough($record['Lease']['id']); */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
/* parent::jqGridRecordsPostProcess($params, $model, $records); */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
|
||||||
$links['Lease'] = array('number');
|
|
||||||
$links['Unit'] = array('name');
|
|
||||||
$links['Customer'] = array('name');
|
|
||||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: move_in
|
|
||||||
* - execute a move in on a new lease
|
|
||||||
*/
|
|
||||||
|
|
||||||
function move_in() {
|
|
||||||
if (!$this->data)
|
|
||||||
die("Should have some data");
|
|
||||||
|
|
||||||
// Handle the move in based on the data given
|
|
||||||
//pr(array('Move-in data', $this->data));
|
|
||||||
|
|
||||||
$lid = $this->Lease->moveIn($this->data['Lease']['customer_id'],
|
|
||||||
$this->data['Lease']['unit_id'],
|
|
||||||
null, null,
|
|
||||||
$this->data['Lease']['movein_date'],
|
|
||||||
$this->data['Lease']['comment']
|
|
||||||
);
|
|
||||||
|
|
||||||
// Since this is a new lease, go to the invoice
|
|
||||||
// screen so we can start assessing charges.
|
|
||||||
$this->redirect(array('action'=>'invoice', $lid));
|
|
||||||
|
|
||||||
// For debugging, only if the redirect above have been
|
|
||||||
// commented out, otherwise this section isn't reached.
|
|
||||||
$this->render('/empty');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: move_out
|
|
||||||
* - prepare or execute a move out on a specific lease
|
|
||||||
*/
|
|
||||||
|
|
||||||
function move_out($id = null) {
|
|
||||||
if ($this->data) {
|
|
||||||
// Handle the move out based on the data given
|
|
||||||
//pr($this->data);
|
|
||||||
|
|
||||||
$this->Lease->moveOut($this->data['Lease']['id'],
|
|
||||||
'VACANT',
|
|
||||||
$this->data['Lease']['moveout_date'],
|
|
||||||
//true // Close this lease, if able
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->redirect($this->data['redirect']);
|
|
||||||
$this->autoRender = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($id))
|
|
||||||
die("Oh Nooooo!!");
|
|
||||||
|
|
||||||
$lease = $this->Lease->find
|
|
||||||
('first', array
|
|
||||||
('contain' => array
|
|
||||||
(// Models
|
|
||||||
'Unit' =>
|
|
||||||
array('order' => array('sort_order'),
|
|
||||||
'fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
|
|
||||||
'Customer' =>
|
|
||||||
array('fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'conditions' => array(array('Lease.id' => $id),
|
|
||||||
array('Lease.close_date' => null),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
$this->set('customer', $lease['Customer']);
|
|
||||||
$this->set('unit', $lease['Unit']);
|
|
||||||
$this->set('lease', $lease['Lease']);
|
|
||||||
|
|
||||||
$redirect = array('controller' => 'leases',
|
|
||||||
'action' => 'view',
|
|
||||||
$id);
|
|
||||||
|
|
||||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
|
||||||
$lease['Unit']['name'] . ': ' .
|
|
||||||
$lease['Customer']['name'] . ': Prepare Move-Out');
|
|
||||||
$this->set(compact('title', 'redirect'));
|
|
||||||
$this->render('/leases/move');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: apply_deposit
|
|
||||||
* - Applies the security deposit to charges. This is much
|
|
||||||
* like a receipt, but it's separated to keep it simple and
|
|
||||||
* to prevent feature overload on the receipt page.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function apply_deposit($id) {
|
|
||||||
$A = new Account();
|
|
||||||
|
|
||||||
$lease = $this->Lease->find
|
|
||||||
('first', array
|
|
||||||
('contain' => array
|
|
||||||
(// Models
|
|
||||||
'Unit' =>
|
|
||||||
array('order' => array('sort_order'),
|
|
||||||
'fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
|
|
||||||
'Customer' =>
|
|
||||||
array('fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'conditions' => array(array('Lease.id' => $id),
|
|
||||||
array('Lease.close_date' => null),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
// Get the lease balance, part of lease stats
|
|
||||||
$this->Lease->statsMerge($lease['Lease'],
|
|
||||||
array('stats' => $this->Lease->stats($id)));
|
|
||||||
|
|
||||||
// Determine the lease security deposit
|
|
||||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
|
||||||
$this->set(compact('deposit'));
|
|
||||||
$this->set('customer', $lease['Customer']);
|
|
||||||
$this->set('unit', $lease['Unit']);
|
|
||||||
$this->set('lease', $lease['Lease']);
|
|
||||||
$this->set('account', array('id' => $A->securityDepositAccountID()));
|
|
||||||
|
|
||||||
/* $redirect = array('controller' => 'leases', */
|
|
||||||
/* 'action' => 'view', */
|
|
||||||
/* $id); */
|
|
||||||
|
|
||||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
|
||||||
$lease['Unit']['name'] . ': ' .
|
|
||||||
$lease['Customer']['name'] . ': Utilize Security Deposit');
|
|
||||||
$this->set(compact('title', 'redirect'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: bad_debt
|
|
||||||
* - Writes off remaining charges on a lease.
|
|
||||||
* REVISIT <AP>: 20090710
|
|
||||||
* Should this be a customer function? What customer
|
|
||||||
* would have only one lease that results in bad debt.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function bad_debt($id) {
|
|
||||||
$A = new Account();
|
|
||||||
|
|
||||||
$lease = $this->Lease->find
|
|
||||||
('first', array
|
|
||||||
('contain' => array
|
|
||||||
(// Models
|
|
||||||
'Unit' =>
|
|
||||||
array('order' => array('sort_order'),
|
|
||||||
'fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
|
|
||||||
'Customer' =>
|
|
||||||
array('fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'conditions' => array(array('Lease.id' => $id),
|
|
||||||
array('Lease.close_date' => null),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
// Get the lease balance, part of lease stats
|
|
||||||
$this->Lease->statsMerge($lease['Lease'],
|
|
||||||
array('stats' => $this->Lease->stats($id)));
|
|
||||||
|
|
||||||
// Determine the lease security deposit
|
|
||||||
$deposit = $this->Lease->findSecurityDeposits($lease['Lease']['id']);
|
|
||||||
if ($deposit['summary']['balance'] > 0)
|
|
||||||
die("Still have un-utilized security deposit");
|
|
||||||
|
|
||||||
$this->set('customer', $lease['Customer']);
|
|
||||||
$this->set('unit', $lease['Unit']);
|
|
||||||
$this->set('lease', $lease['Lease']);
|
|
||||||
$this->set('account', array('id' => $A->badDebtAccountID()));
|
|
||||||
|
|
||||||
/* $redirect = array('controller' => 'leases', */
|
|
||||||
/* 'action' => 'view', */
|
|
||||||
/* $id); */
|
|
||||||
|
|
||||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
|
||||||
$lease['Unit']['name'] . ': ' .
|
|
||||||
$lease['Customer']['name'] . ': Write Off Bad Debt');
|
|
||||||
$this->set(compact('title', 'redirect'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: refund
|
|
||||||
* - Provides user with a refund
|
|
||||||
* REVISIT <AP>: 20090710
|
|
||||||
* Should this be a customer function?
|
|
||||||
*/
|
|
||||||
|
|
||||||
function refund($id) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: close
|
|
||||||
* - Closes a lease to any further action
|
|
||||||
*/
|
|
||||||
|
|
||||||
function close($id) {
|
|
||||||
// REVISIT <AP>: 20090708
|
|
||||||
// We should probably seek confirmation first...
|
|
||||||
$this->Lease->close($id);
|
|
||||||
$this->redirect(array('action'=>'view', $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: invoice
|
|
||||||
* - Sets up the invoice entry page for the given customer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function invoice($id = null, $type = null) {
|
|
||||||
|
|
||||||
$lease = $this->Lease->find
|
|
||||||
('first', array
|
|
||||||
('contain' => array
|
|
||||||
(// Models
|
|
||||||
'Unit' =>
|
|
||||||
array('order' => array('sort_order'),
|
|
||||||
'fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
|
|
||||||
'Customer' =>
|
|
||||||
array('fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'conditions' => array(array('Lease.id' => $id),
|
|
||||||
array('Lease.close_date' => null),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
$A = new Account();
|
|
||||||
$charge_accounts = $A->chargeAccounts();
|
|
||||||
$default_account = $A->rentAccountID();
|
|
||||||
$this->set(compact('charge_accounts', 'default_account'));
|
|
||||||
|
|
||||||
// REVISIT <AP> 20090705:
|
|
||||||
// Of course, the late charge should come from the late_schedule
|
|
||||||
$default_rent = $lease['Lease']['rent'];
|
|
||||||
$default_late = 10;
|
|
||||||
$this->set(compact('default_rent', 'default_late'));
|
|
||||||
|
|
||||||
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
|
||||||
$lease['Unit']['name'] . ': ' .
|
|
||||||
$lease['Customer']['name'] . ': Charge Entry');
|
|
||||||
$this->set(compact('title', 'lease', 'charge'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: view
|
|
||||||
* - Displays information about a specific lease
|
|
||||||
*/
|
|
||||||
|
|
||||||
function view($id = null) {
|
|
||||||
if (!$id) {
|
|
||||||
$this->Session->setFlash(__('Invalid Item.', true));
|
|
||||||
$this->redirect(array('action'=>'index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get details about the lease and its ledgers (no ledger entries yet)
|
|
||||||
$lease = $this->Lease->find
|
|
||||||
('first',
|
|
||||||
array('contain' =>
|
|
||||||
array(// Models
|
|
||||||
'LeaseType',
|
|
||||||
'Unit',
|
|
||||||
'Customer',
|
|
||||||
),
|
|
||||||
'conditions' => array(array('Lease.id' => $id)),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$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);
|
|
||||||
|
|
||||||
if (!isset($lease['Lease']['moveout_date']))
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Move-Out', 'url' => array('action' => 'move_out',
|
|
||||||
$id));
|
|
||||||
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Charges', 'url' => array('action' => 'invoice',
|
|
||||||
$id));
|
|
||||||
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Payments', 'url' => array('controller' => 'customers',
|
|
||||||
'action' => 'receipt',
|
|
||||||
$lease['Customer']['id']));
|
|
||||||
|
|
||||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit > 0 && $outstanding_balance > 0)
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Apply Deposit', 'url' => array('action' => 'apply_deposit',
|
|
||||||
$id));
|
|
||||||
|
|
||||||
if (isset($lease['Lease']['moveout_date']) &&
|
|
||||||
$outstanding_balance <= 0 &&
|
|
||||||
($outstanding_deposit - $outstanding_balance) > 0)
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Issue Refund', 'url' => array('action' => 'refund',
|
|
||||||
$id));
|
|
||||||
|
|
||||||
if (isset($lease['Lease']['moveout_date']) && $outstanding_deposit == 0 && $outstanding_balance > 0)
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Write-Off', 'url' => array('action' => 'bad_debt',
|
|
||||||
$id));
|
|
||||||
|
|
||||||
if ($this->Lease->closeable($id))
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Close', 'url' => array('action' => 'close',
|
|
||||||
$id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare to render
|
|
||||||
$title = 'Lease: #' . $lease['Lease']['id'];
|
|
||||||
$this->set(compact('lease', 'title',
|
|
||||||
'outstanding_deposit',
|
|
||||||
'outstanding_balance'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,467 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class LedgerEntriesController 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtuals: jqGridData
|
|
||||||
* - With the application controller handling the jqGridData 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) {
|
|
||||||
$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'] =
|
|
||||||
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'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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 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);
|
|
||||||
|
|
||||||
$conditions = parent::jqGridDataConditions($params, $model);
|
|
||||||
|
|
||||||
if ($params['action'] === 'collected') {
|
|
||||||
extract($params['custom']);
|
|
||||||
|
|
||||||
if (isset($collected_account_id))
|
|
||||||
$conditions[] = array('Account.id' => $params['custom']['collected_account_id']);
|
|
||||||
else
|
|
||||||
die("INTERNAL ERROR: COLLECTED ACCOUNT ID NOT SET");
|
|
||||||
|
|
||||||
if (!empty($collected_from_date))
|
|
||||||
$conditions[]
|
|
||||||
= array('ReceiptTransaction.stamp >=' =>
|
|
||||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_from_date));
|
|
||||||
|
|
||||||
if (!empty($collected_through_date))
|
|
||||||
$conditions[]
|
|
||||||
= array('ReceiptTransaction.stamp <=' =>
|
|
||||||
$this->LedgerEntry->Transaction->dateFormatBeforeSave($collected_through_date . ' 23:59:59'));
|
|
||||||
|
|
||||||
if (isset($collected_payment_accounts))
|
|
||||||
$conditions[] = array('MoneyAccount.id' => $collected_payment_accounts);
|
|
||||||
else
|
|
||||||
$conditions[] = array('NOT' => array(array('MoneyAccount.id' => null)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($params['action'] === 'ledger') {
|
|
||||||
$conditions[] = $model->ledgerContextConditions($ledger_id, $account_type);
|
|
||||||
}
|
|
||||||
if (isset($params['custom']['reconcile_id'])) {
|
|
||||||
$ftype = $params['custom']['account_ftype'];
|
|
||||||
//$ftype = $this->LedgerEntry->DebitLedger->Account->fundamentalOpposite($ftype);
|
|
||||||
$conditions[] = array('Reconciliation.'.$ftype.'_ledger_entry_id' => $params['custom']['reconcile_id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($params['custom']['account_id'])) {
|
|
||||||
$conditions[] =
|
|
||||||
array('OR' =>
|
|
||||||
array(array('CreditAccount.id' => $params['custom']['account_id']),
|
|
||||||
array('DebitAccount.id' => $params['custom']['account_id'])));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($params['custom']['customer_id'])) {
|
|
||||||
$conditions[] =
|
|
||||||
array('Customer.id' => $params['custom']['customer_id']);
|
|
||||||
|
|
||||||
/* $Account = new Account(); */
|
|
||||||
/* if (isset($params['custom']['account_ftype']) || */
|
|
||||||
/* isset($params['custom']['ledger_id'])) { */
|
|
||||||
/* $conditions[] = */
|
|
||||||
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
|
|
||||||
/* 'Account.id' => $Account->receiptAccountID())); */
|
|
||||||
/* } else { */
|
|
||||||
/* $conditions[] = */
|
|
||||||
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
|
|
||||||
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
|
|
||||||
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
|
|
||||||
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
|
|
||||||
/* )); */
|
|
||||||
/* } */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($params['custom']['lease_id'])) {
|
|
||||||
$conditions[] =
|
|
||||||
array('Lease.id' => $params['custom']['lease_id']);
|
|
||||||
|
|
||||||
/* $Account = new Account(); */
|
|
||||||
/* if (isset($params['custom']['account_ftype']) || */
|
|
||||||
/* isset($params['custom']['ledger_id'])) { */
|
|
||||||
/* $conditions[] = */
|
|
||||||
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
|
|
||||||
/* 'Account.id' => $Account->receiptAccountID())); */
|
|
||||||
/* } else { */
|
|
||||||
/* $conditions[] = */
|
|
||||||
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
|
|
||||||
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
|
|
||||||
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
|
|
||||||
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
|
|
||||||
/* )); */
|
|
||||||
/* } */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($params['custom']['transaction_id'])) {
|
|
||||||
$conditions[] =
|
|
||||||
array('Transaction.id' => $params['custom']['transaction_id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
/* if ($index === 'balance') */
|
|
||||||
/* return ($index .' '. $direction); */
|
|
||||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
|
||||||
|
|
||||||
if ($index === 'Transaction.stamp') {
|
|
||||||
$order[] = 'LedgerEntry.id ' . $direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
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'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::jqGridDataRecords($params, $model, $query);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: reverse the ledger entry
|
|
||||||
*/
|
|
||||||
|
|
||||||
function reverse($id) {
|
|
||||||
$this->LedgerEntry->reverse($id);
|
|
||||||
$this->redirect(array('action'=>'view', $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: view
|
|
||||||
* - Displays information about a specific entry
|
|
||||||
*/
|
|
||||||
|
|
||||||
function view($id = null) {
|
|
||||||
if (!$id) {
|
|
||||||
$this->Session->setFlash(__('Invalid Item.', true));
|
|
||||||
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the 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',
|
|
||||||
),
|
|
||||||
|
|
||||||
'fields' => array('LedgerEntry.*'),
|
|
||||||
|
|
||||||
'conditions' => array('LedgerEntry.id' => $id),
|
|
||||||
));
|
|
||||||
//pr($entry);
|
|
||||||
|
|
||||||
// Because 'DebitLedger' and 'CreditLedger' both relate to 'Account',
|
|
||||||
// CakePHP will not include them in the LedgerEntry->find (or so it
|
|
||||||
// seems). We'll have to break out each Account separately.
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare to render.
|
|
||||||
$title = "Double Ledger Entry #{$entry['LedgerEntry']['id']}";
|
|
||||||
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
function sideMenuLinks() {
|
|
||||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: index / current / closed / all
|
|
||||||
* - Generate a list of ledgers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function index() { $this->all(); }
|
|
||||||
function current() { $this->jqGridView('Current Ledgers'); }
|
|
||||||
function closed() { $this->jqGridView('Closed Ledgers'); }
|
|
||||||
function all() { $this->jqGridView('All Ledgers', 'all'); }
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtuals: jqGridData
|
|
||||||
* - With the application controller handling the jqGridData 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) {
|
|
||||||
return array('contain' => false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataTables(&$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 jqGridDataConditions(&$params, &$model) {
|
|
||||||
$conditions = parent::jqGridDataConditions($params, $model);
|
|
||||||
|
|
||||||
if ($params['action'] === 'current') {
|
|
||||||
$conditions[] = array('NOT' => array('Ledger.closed'));
|
|
||||||
}
|
|
||||||
elseif ($params['action'] === 'closed') {
|
|
||||||
$conditions[] = 'Ledger.closed';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $conditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
|
||||||
$id_sequence = false;
|
|
||||||
if ($index === 'id_sequence') {
|
|
||||||
$id_sequence = true;
|
|
||||||
$index = 'Ledger.account_id';
|
|
||||||
}
|
|
||||||
|
|
||||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
|
||||||
|
|
||||||
if ($id_sequence) {
|
|
||||||
$order[] = 'Ledger.sequence ' . $direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $order;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
|
||||||
$links['Ledger'] = array('id_sequence');
|
|
||||||
$links['Account'] = array('name');
|
|
||||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: view
|
|
||||||
* - Displays information about a specific ledger
|
|
||||||
*/
|
|
||||||
|
|
||||||
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)),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get ledger stats for our summary box
|
|
||||||
$stats = $this->Ledger->stats($id);
|
|
||||||
|
|
||||||
// OK, set our view variables and render!
|
|
||||||
$title = 'Ledger: #' . $ledger['Account']['id'] .'-'. $ledger['Ledger']['sequence'];
|
|
||||||
$this->set(compact('ledger', 'title', 'stats'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
function sideMenuLinks() {
|
|
||||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: index / all
|
|
||||||
* - Generate a listing of transactions
|
|
||||||
*/
|
|
||||||
|
|
||||||
function index() { $this->all(); }
|
|
||||||
function all() { $this->jqGridView('All Transactions', 'all'); }
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtuals: jqGridData
|
|
||||||
* - With the application controller handling the jqGridData 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' =>
|
|
||||||
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')),
|
|
||||||
),
|
|
||||||
|
|
||||||
'CreditLedger' => array
|
|
||||||
('fields' => array('CreditLedger.id', 'CreditLedger.sequence'),
|
|
||||||
'Account' => array
|
|
||||||
('fields' => array('Account.id', 'Account.name')),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'conditions' => array('Transaction.id' => $id),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Figure out the transaction total
|
|
||||||
$total = 0;
|
|
||||||
foreach($transaction['LedgerEntry'] AS $entry)
|
|
||||||
$total += $entry['amount'];
|
|
||||||
|
|
||||||
// OK, prepare to render.
|
|
||||||
$title = 'Transaction #' . $transaction['Transaction']['id'];
|
|
||||||
$this->set(compact('transaction', 'title', 'total'));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: postInvoice
|
|
||||||
* - handles the creation of a charge invoice
|
|
||||||
*/
|
|
||||||
|
|
||||||
function postInvoice() {
|
|
||||||
if (!$this->RequestHandler->isPost()) {
|
|
||||||
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->Transaction->addInvoice($this->data, null,
|
|
||||||
$this->data['Lease']['id'])) {
|
|
||||||
$this->Session->setFlash("INVOICE FAILED", true);
|
|
||||||
// REVISIT <AP> 20090706:
|
|
||||||
// Until we can work out the session problems,
|
|
||||||
// just die.
|
|
||||||
die("<H1>INVOICE FAILED</H1>");
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->layout = null;
|
|
||||||
$this->autoLayout = false;
|
|
||||||
$this->autoRender = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: postReceipt
|
|
||||||
* - handles the creation of a payment receipt
|
|
||||||
*/
|
|
||||||
|
|
||||||
function postReceipt() {
|
|
||||||
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']];
|
|
||||||
}
|
|
||||||
|
|
||||||
pr($this->data);
|
|
||||||
if (!$this->Transaction->addReceipt($this->data,
|
|
||||||
$this->data['Customer']['id'],
|
|
||||||
(isset($this->data['Lease']['id'])
|
|
||||||
? $this->data['Lease']['id']
|
|
||||||
: null ))) {
|
|
||||||
$this->Session->setFlash("RECEIPT FAILED", true);
|
|
||||||
// REVISIT <AP> 20090706:
|
|
||||||
// Until we can work out the session problems,
|
|
||||||
// just die.
|
|
||||||
die("<H1>RECEIPT FAILED</H1>");
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->layout = null;
|
|
||||||
$this->autoLayout = false;
|
|
||||||
$this->autoRender = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,278 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
function sideMenuLinks() {
|
|
||||||
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: index / unavailable / vacant / occupied / all
|
|
||||||
* - Generate a listing of units
|
|
||||||
*/
|
|
||||||
|
|
||||||
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'); }
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* virtuals: jqGridData
|
|
||||||
* - With the application controller handling the jqGridData 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) {
|
|
||||||
$link = array
|
|
||||||
('link' =>
|
|
||||||
array(// Models
|
|
||||||
'UnitSize' => array('fields' => array('id', 'name')),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($params['action'] === 'occupied')
|
|
||||||
$link['Lease'] = array('fields' => array(),
|
|
||||||
// Models
|
|
||||||
'Contact' => array('fields' => array('display_name'),
|
|
||||||
//'type' => 'LEFT',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataTables(&$params, &$model) {
|
|
||||||
$link = $this->jqGridDataCountTables($params, $model);
|
|
||||||
$link['link']['CurrentLease']['LedgerEntry'] = array('fields' => array());
|
|
||||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger'] = array('fields' => array());
|
|
||||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account'] = array('fields' => array());
|
|
||||||
// INNER JOIN would be great, as it would ensure we're only looking
|
|
||||||
// at the ledger entries that we truly want. However, this also
|
|
||||||
// removes from the query any leases that do not yet have a ledger
|
|
||||||
// entry in A/R. A solution would be to INNER JOIN these tables,
|
|
||||||
// and LEFT JOIN it to the rest. Grouping of JOINs, however, is
|
|
||||||
// implemented with the 'joins' tag, and is not available through
|
|
||||||
// the Linkable behavior interface.
|
|
||||||
//$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['type'] = 'INNER';
|
|
||||||
$link['link']['CurrentLease']['LedgerEntry']['Ledger']['Account']['conditions']
|
|
||||||
= array('Account.id' =>
|
|
||||||
$this->Unit->CurrentLease->LedgerEntry->Ledger->Account->accountReceivableAccountID());
|
|
||||||
return $link;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataFields(&$params, &$model) {
|
|
||||||
$db = &$model->getDataSource();
|
|
||||||
$fields = $db->fields($model, $model->alias);
|
|
||||||
$fields[] = ("SUM(IF(Account.id IS NULL, 0," .
|
|
||||||
" IF(LedgerEntry.debit_ledger_id = Account.id," .
|
|
||||||
" 1, -1))" .
|
|
||||||
" * LedgerEntry.amount) AS 'balance'");
|
|
||||||
return $fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataConditions(&$params, &$model) {
|
|
||||||
$conditions = parent::jqGridDataConditions($params, $model);
|
|
||||||
|
|
||||||
if ($params['action'] === 'unavailable') {
|
|
||||||
$conditions[] = $this->Unit->conditionUnavailable();
|
|
||||||
}
|
|
||||||
elseif ($params['action'] === 'vacant') {
|
|
||||||
$conditions[] = $this->Unit->conditionVacant();
|
|
||||||
}
|
|
||||||
elseif ($params['action'] === 'occupied') {
|
|
||||||
$conditions[] = $this->Unit->conditionOccupied();
|
|
||||||
}
|
|
||||||
elseif ($params['action'] === 'unoccupied') {
|
|
||||||
$conditions[] = array('NOT' => array($this->Unit->conditionOccupied()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $conditions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
|
||||||
// Instead of sorting by name, sort by defined order
|
|
||||||
if ($index === 'Unit.name')
|
|
||||||
$index = 'Unit.sort_order';
|
|
||||||
|
|
||||||
$order = array();
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model, $index, $direction);
|
|
||||||
|
|
||||||
// If sorting by anything other than name (defined order)
|
|
||||||
// add the sort-order as a secondary condition
|
|
||||||
if ($index !== 'Unit.name')
|
|
||||||
$order[] = parent::jqGridDataOrder($params, $model,
|
|
||||||
'Unit.sort_order', $direction);
|
|
||||||
|
|
||||||
return $order;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
|
||||||
$links['Unit'] = array('name');
|
|
||||||
$links['UnitSize'] = array('name');
|
|
||||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: move_in
|
|
||||||
* - Sets up the move-in page for the given unit.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function move_in($id = null) {
|
|
||||||
$customer = array();
|
|
||||||
$unit = array();
|
|
||||||
|
|
||||||
if (isset($id)) {
|
|
||||||
$this->Unit->recursive = -1;
|
|
||||||
$unit = current($this->Unit->read(null, $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
$title = 'Unit Move-In';
|
|
||||||
$this->set(compact('customer', 'unit', 'title'));
|
|
||||||
$this->render('/leases/move');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: move_out
|
|
||||||
* - prepare or execute a move out on a specific lease
|
|
||||||
*/
|
|
||||||
|
|
||||||
function move_out($id) {
|
|
||||||
|
|
||||||
$unit = $this->Unit->find
|
|
||||||
('first', array
|
|
||||||
('contain' => array
|
|
||||||
(// Models
|
|
||||||
'CurrentLease' =>
|
|
||||||
array(//'conditions' => array('Lease.moveout_date' => null),
|
|
||||||
// Models
|
|
||||||
'Customer' =>
|
|
||||||
array('fields' => array('id', 'name'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'conditions' => array('Unit.id' => $id),
|
|
||||||
));
|
|
||||||
$this->set('customer', $unit['CurrentLease']['Customer']);
|
|
||||||
$this->set('unit', $unit['Unit']);
|
|
||||||
$this->set('lease', $unit['CurrentLease']);
|
|
||||||
|
|
||||||
$redirect = array('controller' => 'units',
|
|
||||||
'action' => 'view',
|
|
||||||
$id);
|
|
||||||
|
|
||||||
$title = ('Lease #' . $unit['CurrentLease']['number'] . ': ' .
|
|
||||||
$unit['Unit']['name'] . ': ' .
|
|
||||||
$unit['CurrentLease']['Customer']['name'] . ': Prepare Move-Out');
|
|
||||||
$this->set(compact('title', 'redirect'));
|
|
||||||
$this->render('/leases/move');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* action: view
|
|
||||||
* - Displays information about a specific unit
|
|
||||||
*/
|
|
||||||
|
|
||||||
function view($id = null) {
|
|
||||||
if (!$id) {
|
|
||||||
$this->Session->setFlash(__('Invalid Item.', true));
|
|
||||||
$this->redirect(array('action'=>''));
|
|
||||||
}
|
|
||||||
|
|
||||||
$unit = $this->Unit->find
|
|
||||||
('first',
|
|
||||||
array('contain' =>
|
|
||||||
array(// Models
|
|
||||||
'UnitSize',
|
|
||||||
'Lease' => array('Customer'),
|
|
||||||
'CurrentLease' => array('Customer')
|
|
||||||
),
|
|
||||||
'conditions' => array('Unit.id' => $id),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Get the balance on each lease.
|
|
||||||
foreach ($unit['Lease'] AS &$lease) {
|
|
||||||
$stats = $this->Unit->Lease->stats($lease['id']);
|
|
||||||
$lease['balance'] = $stats['balance'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$outstanding_balance = 0;
|
|
||||||
$outstanding_deposit = 0;
|
|
||||||
if (isset($unit['CurrentLease']['id'])) {
|
|
||||||
// Figure out the outstanding balance of the current lease.
|
|
||||||
$stats = $this->Unit->stats($id);
|
|
||||||
$outstanding_balance =
|
|
||||||
$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'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up dynamic menu items
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Operations', 'header' => true);
|
|
||||||
|
|
||||||
if (isset($unit['CurrentLease']['id']) &&
|
|
||||||
!isset($unit['CurrentLease']['moveout_date'])) {
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Move-Out', 'url' => array('action' => 'move_out',
|
|
||||||
$id));
|
|
||||||
} else {
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Move-In', 'url' => array('action' => 'move_in',
|
|
||||||
$id));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($unit['CurrentLease']['id']) &&
|
|
||||||
!isset($unit['CurrentLease']['close_date'])) {
|
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Payment', 'url' => array('controller' => 'customers',
|
|
||||||
'action' => 'receipt',
|
|
||||||
$unit['CurrentLease']['customer_id']));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare to render.
|
|
||||||
$title = 'Unit ' . $unit['Unit']['name'];
|
|
||||||
$this->set(compact('unit', 'title',
|
|
||||||
'outstanding_balance',
|
|
||||||
'outstanding_deposit'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1513
db/schema.sql
Normal file
1513
db/schema.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,850 +0,0 @@
|
|||||||
<?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',
|
|
||||||
// REVISIT <AP> 20090702:
|
|
||||||
// I would prefer this statement, which has no
|
|
||||||
// engine specific code. However, it doesn't
|
|
||||||
// work with the Linkable behavior. I need to
|
|
||||||
// look into that, just not right now.
|
|
||||||
//'conditions' => array('CurrentLedger.close_id' => null),
|
|
||||||
'conditions' => array('CurrentLedger.close_id IS NULL'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'Ledger',
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: type
|
|
||||||
* - Returns the type of this account
|
|
||||||
*/
|
|
||||||
function type($id) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$account = $this->find('first', array
|
|
||||||
('recursive' => -1,
|
|
||||||
'fields' => array('type'),
|
|
||||||
'conditions' => array(array('Account.id' => $id)),
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
|
|
||||||
return $account['Account']['type'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: fundamentalType
|
|
||||||
* - Returns the fundmental type of the account, credit or debit
|
|
||||||
*/
|
|
||||||
function fundamentalType($id_or_type) {
|
|
||||||
if (is_numeric($id_or_type))
|
|
||||||
$type = $this->type($id_or_type);
|
|
||||||
else
|
|
||||||
$type = $id_or_type;
|
|
||||||
|
|
||||||
// Asset and Expense accounts are debit accounts
|
|
||||||
if (in_array(strtoupper($type), array('ASSET', 'EXPENSE')))
|
|
||||||
return 'debit';
|
|
||||||
|
|
||||||
// Otherwise, it's a credit account
|
|
||||||
return 'credit';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: fundamentalOpposite
|
|
||||||
* - Returns the opposite fundmental type of the account, credit or debit
|
|
||||||
*/
|
|
||||||
function fundamentalOpposite($id_or_type) {
|
|
||||||
if (in_array(strtolower($id_or_type), array('credit', 'debit')))
|
|
||||||
$fund = $id_or_type;
|
|
||||||
else
|
|
||||||
$fund = $this->fundamentalType($id_or_type);
|
|
||||||
|
|
||||||
if ($fund == 'debit')
|
|
||||||
return 'credit';
|
|
||||||
|
|
||||||
return 'debit';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: name
|
|
||||||
* - Returns the name of this account
|
|
||||||
*/
|
|
||||||
function name($id) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$account = $this->find('first', array
|
|
||||||
('recursive' => -1,
|
|
||||||
'fields' => array('name'),
|
|
||||||
'conditions' => array(array('Account.id' => $id)),
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
|
|
||||||
return $account['Account']['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: Account IDs
|
|
||||||
* - Returns the ID of the desired account
|
|
||||||
*/
|
|
||||||
|
|
||||||
function securityDepositAccountID() { return $this->nameToID('Security Deposit'); }
|
|
||||||
function rentAccountID() { return $this->nameToID('Rent'); }
|
|
||||||
function lateChargeAccountID() { return $this->nameToID('Late Charge'); }
|
|
||||||
function nsfAccountID() { return $this->nameToID('NSF'); }
|
|
||||||
function nsfChargeAccountID() { return $this->nameToID('NSF Charge'); }
|
|
||||||
function taxAccountID() { return $this->nameToID('Tax'); }
|
|
||||||
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
|
|
||||||
function cashAccountID() { return $this->nameToID('Cash'); }
|
|
||||||
function checkAccountID() { return $this->nameToID('Check'); }
|
|
||||||
function moneyOrderAccountID() { return $this->nameToID('Money Order'); }
|
|
||||||
function concessionAccountID() { return $this->nameToID('Concession'); }
|
|
||||||
function pettyCashAccountID() { return $this->nameToID('Petty Cash'); }
|
|
||||||
function invoiceAccountID() { return $this->nameToID('Invoice'); }
|
|
||||||
function receiptAccountID() { return $this->nameToID('Receipt'); }
|
|
||||||
function badDebtAccountID() { return $this->nameToID('Bad Debt'); }
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: fundamentalAccounts
|
|
||||||
* - Returns an array of accounts by their fundamental type
|
|
||||||
*/
|
|
||||||
|
|
||||||
function fundamentalAccounts($ftype) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$account = $this->find('all', array
|
|
||||||
('contain' => array('CurrentLedger'),
|
|
||||||
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
|
||||||
'conditions' => array('Account.type' => strtoupper($ftype))
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
|
|
||||||
return $account;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: relatedAccounts
|
|
||||||
* - Returns an array of accounts related by similar attributes
|
|
||||||
*/
|
|
||||||
|
|
||||||
function relatedAccounts($attribute, $extra = null) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$account = $this->find('all', array
|
|
||||||
('contain' => array('CurrentLedger'),
|
|
||||||
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
|
||||||
'conditions' => array('Account.'.$attribute => true),
|
|
||||||
'order' => array('Account.name'),
|
|
||||||
) + (isset($extra) ? $extra : array())
|
|
||||||
);
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
|
|
||||||
return $account;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: chargeAccounts
|
|
||||||
* - Returns an array of accounts suitable for charges
|
|
||||||
*/
|
|
||||||
|
|
||||||
function chargeAccounts() {
|
|
||||||
// Get all accounts that support charges
|
|
||||||
$accounts = $this->relatedAccounts('chargeable', array('order' => 'name'));
|
|
||||||
|
|
||||||
// Rearrange to be of the form (id => name)
|
|
||||||
$charge_accounts = array();
|
|
||||||
foreach ($accounts AS $acct) {
|
|
||||||
$charge_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $charge_accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: paymentAccounts
|
|
||||||
* - Returns an array of accounts suitable for payments
|
|
||||||
*/
|
|
||||||
|
|
||||||
function paymentAccounts() {
|
|
||||||
// Get all accounts that support payments
|
|
||||||
$accounts = $this->relatedAccounts('payable', array('order' => 'name'));
|
|
||||||
|
|
||||||
// Rearrange to be of the form (id => name)
|
|
||||||
$payment_accounts = array();
|
|
||||||
foreach ($accounts AS $acct) {
|
|
||||||
$payment_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $payment_accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: collectableAccounts
|
|
||||||
* - Returns an array of accounts suitable to show income collection
|
|
||||||
*/
|
|
||||||
|
|
||||||
function collectableAccounts() {
|
|
||||||
$accounts = $this->paymentAccounts();
|
|
||||||
|
|
||||||
foreach(array($this->nsfAccountID(),
|
|
||||||
$this->securityDepositAccountID())
|
|
||||||
AS $account_id) {
|
|
||||||
$accounts[$account_id] = $this->name($account_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: currentLedgerID
|
|
||||||
* - Returns the current ledger ID of the account
|
|
||||||
*/
|
|
||||||
function currentLedgerID($id) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$item = $this->find('first', array
|
|
||||||
('contain' => 'CurrentLedger',
|
|
||||||
'conditions' => array('Account.id' => $id),
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
return $item['CurrentLedger']['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: ledgers
|
|
||||||
* - Returns an array of ledger ids from the given account
|
|
||||||
*/
|
|
||||||
function ledgers($id, $all = false) {
|
|
||||||
if ($all) {
|
|
||||||
$contain = array('Ledger' => array('fields' => array('Ledger.id')));
|
|
||||||
} else {
|
|
||||||
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$account = $this->find('first', array
|
|
||||||
('contain' => $contain,
|
|
||||||
'fields' => array(),
|
|
||||||
'conditions' => array(array('Account.id' => $id)),
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
|
|
||||||
if ($all) {
|
|
||||||
$ledger_ids = array();
|
|
||||||
foreach ($account['Ledger'] AS $ledger)
|
|
||||||
array_push($ledger_ids, $ledger['id']);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$ledger_ids = array($account['CurrentLedger']['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pr(array('function' => 'Account::ledgers', */
|
|
||||||
/* 'args' => compact('id', 'all'), */
|
|
||||||
/* 'return' => $ledger_ids)); */
|
|
||||||
|
|
||||||
return $ledger_ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: closeCurrentLedger
|
|
||||||
* - Closes the current account ledger, and opens a new one
|
|
||||||
* with the old balance carried forward.
|
|
||||||
*/
|
|
||||||
function closeCurrentLedger($id = null, $close_id = null) {
|
|
||||||
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
|
||||||
|
|
||||||
if (!$close_id) {
|
|
||||||
$close = new Close();
|
|
||||||
$close->create();
|
|
||||||
if (!$close->save(array('stamp' => null), false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$close_id = $close->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$account = $this->find('all', array
|
|
||||||
('contain' => $contain,
|
|
||||||
'fields' => array(),
|
|
||||||
'conditions' =>
|
|
||||||
$id ? array(array('Account.id' => $id)) : array()
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
//pr(compact('id', 'account'));
|
|
||||||
|
|
||||||
foreach ($account AS $acct) {
|
|
||||||
if (!$this->Ledger->closeLedger($acct['CurrentLedger']['id'], $close_id))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: findLedgerEntries
|
|
||||||
* - 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: stats
|
|
||||||
* - Returns summary data from the requested account.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function stats($id = null, $all = false, $cond = 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))
|
|
||||||
));
|
|
||||||
|
|
||||||
$stats = array();
|
|
||||||
if ($all) {
|
|
||||||
foreach ($account['Ledger'] AS $ledger)
|
|
||||||
$this->statsMerge($stats['Ledger'],
|
|
||||||
$this->Ledger->stats($ledger['id'], $cond));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$stats['Ledger'] =
|
|
||||||
$this->Ledger->stats($account['CurrentLedger']['id'], $cond);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,406 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* LinkableBehavior
|
|
||||||
* Light-weight approach for data mining on deep relations between models.
|
|
||||||
* Join tables based on model relations to easily enable right to left find operations.
|
|
||||||
*
|
|
||||||
* Can be used as a alternative to the ContainableBehavior:
|
|
||||||
* - On data fetching only in right to left operations,
|
|
||||||
* wich means that in "one to many" relations (hasMany, hasAndBelongsToMany)
|
|
||||||
* should only be used from the "many to one" tables. i.e:
|
|
||||||
* To fetch all Users assigneds to a Project with ProjectAssignment,
|
|
||||||
* $Project->find('all', array('link' => 'User', 'conditions' => 'project_id = 1'))
|
|
||||||
* - Won't produce the desired result as data came from users table will be lost.
|
|
||||||
* $User->find('all', array('link' => 'Project', 'conditions' => 'project_id = 1'))
|
|
||||||
* - Will fetch all users related to the specified project in one query
|
|
||||||
*
|
|
||||||
* - On data mining as a much lighter approach - can reduce 300+ query find operations
|
|
||||||
* in one single query with joins; "or your money back!" ;-)
|
|
||||||
*
|
|
||||||
* - Has the 'fields' param enabled to make it easy to replace Containable usage,
|
|
||||||
* only change the 'contain' param to 'link'.
|
|
||||||
*
|
|
||||||
* Linkable Behavior. Taking it easy in your DB.
|
|
||||||
* RafaelBandeira <rafaelbandeira3(at)gmail(dot)com>
|
|
||||||
*
|
|
||||||
* Licensed under The MIT License
|
|
||||||
* Redistributions of files must retain the above copyright notice.
|
|
||||||
*
|
|
||||||
* @version 1.0;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
* NOTE TO <AP> 20090615:
|
|
||||||
* This structure should be linkable (it was my test case).
|
|
||||||
|
|
||||||
$entry = $this->LedgerEntry->find
|
|
||||||
('first',
|
|
||||||
array('link' => array('DebitLedger' =>
|
|
||||||
array(
|
|
||||||
'fields' => array('id'),
|
|
||||||
'alias' => 'MyDebitLedger',
|
|
||||||
'Account' =>
|
|
||||||
array(
|
|
||||||
'fields' => array('id'),
|
|
||||||
'alias' => 'MyDebitAccount'),
|
|
||||||
),
|
|
||||||
|
|
||||||
'MyCreditLedger' =>
|
|
||||||
array(
|
|
||||||
'fields' => array('id'),
|
|
||||||
'class' => 'CreditLedger',
|
|
||||||
'MyCreditAccount' =>
|
|
||||||
array(
|
|
||||||
'fields' => array('id'),
|
|
||||||
'class' => 'Account'),
|
|
||||||
),
|
|
||||||
|
|
||||||
'MyOtherLedger' =>
|
|
||||||
array(
|
|
||||||
'fields' => array('id'),
|
|
||||||
'class' => 'Ledger',
|
|
||||||
'alias' => 'AliasToMyOtherLedger',
|
|
||||||
'Account' =>
|
|
||||||
array(
|
|
||||||
'fields' => array('id'),
|
|
||||||
'alias' => 'AliasToMyOtherAccount'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'conditions' => array('LedgerEntry.id' => $id),
|
|
||||||
));
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
class LinkableBehavior extends ModelBehavior {
|
|
||||||
|
|
||||||
protected $_key = 'link';
|
|
||||||
|
|
||||||
protected $_options = array(
|
|
||||||
'type' => true, 'table' => true, 'alias' => true, 'joins' => true,
|
|
||||||
'conditions' => true, 'fields' => true, 'reference' => true,
|
|
||||||
'class' => true, 'defaults' => true, 'linkalias' => true,
|
|
||||||
);
|
|
||||||
|
|
||||||
protected $_defaults = array('type' => 'LEFT');
|
|
||||||
|
|
||||||
function pr($lev, $mixed) {
|
|
||||||
if ($lev >= 5)
|
|
||||||
return;
|
|
||||||
|
|
||||||
pr($mixed);
|
|
||||||
return;
|
|
||||||
|
|
||||||
$trace = debug_backtrace(false);
|
|
||||||
//array_shift($trace);
|
|
||||||
$calls = array();
|
|
||||||
foreach ($trace AS $call) {
|
|
||||||
$call = array_intersect_key($call,
|
|
||||||
array('file'=>1,
|
|
||||||
'line'=>1,
|
|
||||||
//'class'=>1,
|
|
||||||
'function'=>1,
|
|
||||||
));
|
|
||||||
$calls[] = implode("; ", $call);
|
|
||||||
}
|
|
||||||
pr(array('debug' => $mixed, 'stack' => $calls));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a function for made recursive str_replaces in an array
|
|
||||||
* NOTE: The palacement of this function is terrible, but I don't
|
|
||||||
* know if I really want to go down this path or not.
|
|
||||||
*/
|
|
||||||
function recursive_array_replace($find, $replace, &$data) {
|
|
||||||
if (!isset($data))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (is_array($data)) {
|
|
||||||
foreach ($data as $key => $value) {
|
|
||||||
if (is_array($value)) {
|
|
||||||
$this->recursive_array_replace($find, $replace, $data[$key]);
|
|
||||||
} else {
|
|
||||||
$data[$key] = str_replace($find, $replace, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$data = str_replace($find, $replace, $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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['fields']) || $query['fields'] === true) {
|
|
||||||
//$query['fields'] = array_keys($Model->_schema);
|
|
||||||
$query['fields'] = $Model->getDataSource()->fields($Model);
|
|
||||||
} elseif (!is_array($query['fields'])) {
|
|
||||||
$query['fields'] = array($query['fields']);
|
|
||||||
}
|
|
||||||
$query = am(array('joins' => array()), $query, array('recursive' => -1));
|
|
||||||
$iterators[] = $query[$this->_key];
|
|
||||||
$cont = 0;
|
|
||||||
do {
|
|
||||||
$iterator = $iterators[$cont];
|
|
||||||
$defaults = $optionsDefaults;
|
|
||||||
if (isset($iterator['defaults'])) {
|
|
||||||
$defaults = array_merge($defaults, $iterator['defaults']);
|
|
||||||
unset($iterator['defaults']);
|
|
||||||
}
|
|
||||||
$iterations = Set::normalize($iterator);
|
|
||||||
$this->pr(25,
|
|
||||||
array('checkpoint' => 'Iterations',
|
|
||||||
compact('iterations'),
|
|
||||||
));
|
|
||||||
foreach ($iterations as $alias => $options) {
|
|
||||||
if (is_null($options)) {
|
|
||||||
$options = array();
|
|
||||||
}
|
|
||||||
$options = am($defaults, compact('alias'), $options);
|
|
||||||
if (empty($options['alias'])) {
|
|
||||||
throw new InvalidArgumentException(sprintf('%s::%s must receive aliased links', get_class($this), __FUNCTION__));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($options['class']))
|
|
||||||
$options['class'] = $alias;
|
|
||||||
|
|
||||||
if (!isset($options['conditions']))
|
|
||||||
$options['conditions'] = array();
|
|
||||||
elseif (!is_array($options['conditions']))
|
|
||||||
$options['conditions'] = array($options['conditions']);
|
|
||||||
|
|
||||||
$this->pr(20,
|
|
||||||
array('checkpoint' => 'Begin Model Work',
|
|
||||||
compact('alias', 'options'),
|
|
||||||
));
|
|
||||||
|
|
||||||
$modelClass = $options['class'];
|
|
||||||
$modelAlias = $options['alias'];
|
|
||||||
$referenceClass = $options['reference']['class'];
|
|
||||||
$referenceAlias = $options['reference']['alias'];
|
|
||||||
|
|
||||||
$_Model =& ClassRegistry::init($modelClass); // the incoming model to be linked in query
|
|
||||||
$Reference =& ClassRegistry::init($referenceClass); // the already in query model that links to $_Model
|
|
||||||
$this->pr(12,
|
|
||||||
array('checkpoint' => 'Aliases Established',
|
|
||||||
'Model' => ($modelAlias .' : '. $modelClass .
|
|
||||||
' ('. $_Model->alias .' : '. $_Model->name .')'),
|
|
||||||
'Reference' => ($referenceAlias .' : '. $referenceClass .
|
|
||||||
' ('. $Reference->alias .' : '. $Reference->name .')'),
|
|
||||||
));
|
|
||||||
|
|
||||||
$db =& $_Model->getDataSource();
|
|
||||||
|
|
||||||
$associatedThroughReference = 0;
|
|
||||||
$association = null;
|
|
||||||
|
|
||||||
// Figure out how these two models are related, creating
|
|
||||||
// a relationship if one doesn't otherwise already exists.
|
|
||||||
if (($associations = $Reference->getAssociated()) &&
|
|
||||||
isset($associations[$_Model->alias])) {
|
|
||||||
$this->pr(12, array('checkpoint' => "Reference defines association to _Model"));
|
|
||||||
$associatedThroughReference = 1;
|
|
||||||
$type = $associations[$_Model->alias];
|
|
||||||
$association = $Reference->{$type}[$_Model->alias];
|
|
||||||
}
|
|
||||||
elseif (($associations = $_Model->getAssociated()) &&
|
|
||||||
isset($associations[$Reference->alias])) {
|
|
||||||
$this->pr(12, array('checkpoint' => "_Model defines association to Reference"));
|
|
||||||
$type = $associations[$Reference->alias];
|
|
||||||
$association = $_Model->{$type}[$Reference->alias];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// No relationship... make our best effort to create one.
|
|
||||||
$this->pr(12, array('checkpoint' => "No assocation between _Model and Reference"));
|
|
||||||
$type = 'belongsTo';
|
|
||||||
$_Model->bind($Reference->alias);
|
|
||||||
// Grab the association now, since we'll unbind in a moment.
|
|
||||||
$association = $_Model->{$type}[$Reference->alias];
|
|
||||||
$_Model->unbindModel(array('belongsTo' => array($Reference->alias)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine which model holds the foreign key
|
|
||||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference) {
|
|
||||||
$primaryAlias = $referenceAlias;
|
|
||||||
$foreignAlias = $modelAlias;
|
|
||||||
$primaryModel = $Reference;
|
|
||||||
$foreignModel = $_Model;
|
|
||||||
} else {
|
|
||||||
$primaryAlias = $modelAlias;
|
|
||||||
$foreignAlias = $referenceAlias;
|
|
||||||
$primaryModel = $_Model;
|
|
||||||
$foreignModel = $Reference;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($associatedThroughReference)
|
|
||||||
$associationAlias = $referenceAlias;
|
|
||||||
else
|
|
||||||
$associationAlias = $modelAlias;
|
|
||||||
|
|
||||||
$this->recursive_array_replace("%{MODEL_ALIAS}",
|
|
||||||
$associationAlias,
|
|
||||||
$association['conditions']);
|
|
||||||
|
|
||||||
$this->pr(15,
|
|
||||||
array('checkpoint' => 'Models Established - Check Associations',
|
|
||||||
'primaryModel' => $primaryAlias .' : '. $primaryModel->name,
|
|
||||||
'foreignModel' => $foreignAlias .' : '. $foreignModel->name,
|
|
||||||
compact('type', 'association'),
|
|
||||||
));
|
|
||||||
|
|
||||||
if ($type === 'hasAndBelongsToMany') {
|
|
||||||
if (isset($association['with']))
|
|
||||||
$linkClass = $association['with'];
|
|
||||||
else
|
|
||||||
$linkClass = Inflector::classify($association['joinTable']);
|
|
||||||
|
|
||||||
$Link =& $_Model->{$linkClass};
|
|
||||||
|
|
||||||
if (isset($options['linkalias']))
|
|
||||||
$linkAlias = $options['linkalias'];
|
|
||||||
else
|
|
||||||
$linkAlias = $Link->alias;
|
|
||||||
|
|
||||||
$this->pr(17,
|
|
||||||
array('checkpoint' => 'Linking HABTM',
|
|
||||||
compact('linkClass', 'linkAlias'),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Get the foreign key fields (for the link table) directly from
|
|
||||||
// the defined model associations, if they exists. This is the
|
|
||||||
// users direct specification, and therefore definitive if present.
|
|
||||||
$modelLink = $Link->escapeField($association['foreignKey'], $linkAlias);
|
|
||||||
$referenceLink = $Link->escapeField($association['associationForeignKey'], $linkAlias);
|
|
||||||
|
|
||||||
// If we haven't figured out the foreign keys, see if there is a
|
|
||||||
// model for the link table, and if it has the appropriate
|
|
||||||
// associations with the two tables we're trying to join.
|
|
||||||
if (empty($modelLink) && isset($Link->belongsTo[$_Model->alias]))
|
|
||||||
$modelLink = $Link->escapeField($Link->belongsTo[$_Model->alias]['foreignKey'], $linkAlias);
|
|
||||||
if (empty($referenceLink) && isset($Link->belongsTo[$Reference->alias]))
|
|
||||||
$referenceLink = $Link->escapeField($Link->belongsTo[$Reference->alias]['foreignKey'], $linkAlias);
|
|
||||||
|
|
||||||
// We're running quite thin here. None of the models spell
|
|
||||||
// out the appropriate linkages. We'll have to SWAG it.
|
|
||||||
if (empty($modelLink))
|
|
||||||
$modelLink = $Link->escapeField(Inflector::underscore($_Model->alias) . '_id', $linkAlias);
|
|
||||||
if (empty($referenceLink))
|
|
||||||
$referenceLink = $Link->escapeField(Inflector::underscore($Reference->alias) . '_id', $linkAlias);
|
|
||||||
|
|
||||||
// Get the primary key from the tables we're joining.
|
|
||||||
$referenceKey = $Reference->escapeField(null, $referenceAlias);
|
|
||||||
$modelKey = $_Model->escapeField(null, $modelAlias);
|
|
||||||
|
|
||||||
// Join the linkage table to our model. We'll use an inner join,
|
|
||||||
// as the whole purpose of the linkage table is to make this
|
|
||||||
// connection. As we are embedding this join, the INNER will not
|
|
||||||
// cause any problem with the overall query, should the user not
|
|
||||||
// be concerned with whether or not the join has any results.
|
|
||||||
// They control that with the 'type' parameter which will be at
|
|
||||||
// the top level join.
|
|
||||||
$options['joins'][] = array('type' => 'INNER',
|
|
||||||
'alias' => $modelAlias,
|
|
||||||
'conditions' => "{$modelKey} = {$modelLink}",
|
|
||||||
'table' => $db->fullTableName($_Model, true));
|
|
||||||
|
|
||||||
// Now for the top level join. This will be added into the list
|
|
||||||
// of joins down below, outside of the HABTM specific code.
|
|
||||||
$options['class'] = $linkClass;
|
|
||||||
$options['alias'] = $linkAlias;
|
|
||||||
$options['table'] = $Link->getDataSource()->fullTableName($Link);
|
|
||||||
$options['conditions'][] = "{$referenceLink} = {$referenceKey}";
|
|
||||||
}
|
|
||||||
elseif (isset($association['foreignKey']) && $association['foreignKey']) {
|
|
||||||
$foreignKey = $primaryModel->escapeField($association['foreignKey'], $primaryAlias);
|
|
||||||
$primaryKey = $foreignModel->escapeField($foreignModel->primaryKey, $foreignAlias);
|
|
||||||
|
|
||||||
$this->pr(17,
|
|
||||||
array('checkpoint' => 'Linking due to foreignKey',
|
|
||||||
compact('foreignKey', 'primaryKey'),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Only differentiating to help show the logical flow.
|
|
||||||
// Either way works and this test can be tossed out
|
|
||||||
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference)
|
|
||||||
$options['conditions'][] = "{$primaryKey} = {$foreignKey}";
|
|
||||||
else
|
|
||||||
$options['conditions'][] = "{$foreignKey} = {$primaryKey}";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->pr(17,
|
|
||||||
array('checkpoint' => 'Linking with no logic (expecting user defined)',
|
|
||||||
));
|
|
||||||
|
|
||||||
// No Foreign Key... nothing we can do.
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->pr(19,
|
|
||||||
array('checkpoint' => 'Conditions',
|
|
||||||
array('options[conditions]' => $options['conditions'],
|
|
||||||
'association[conditions]' => $association['conditions'],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
// The user may have specified conditions directly in the model
|
|
||||||
// for this join. Make sure to adhere to those conditions.
|
|
||||||
if (isset($association['conditions']) && is_array($association['conditions']))
|
|
||||||
$options['conditions'] = array_merge($options['conditions'], $association['conditions']);
|
|
||||||
elseif (!empty($association['conditions']))
|
|
||||||
$options['conditions'][] = $association['conditions'];
|
|
||||||
|
|
||||||
$this->pr(19,
|
|
||||||
array('checkpoint' => 'Conditions2',
|
|
||||||
array('options[conditions]' => $options['conditions'],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
|
|
||||||
if (empty($options['table'])) {
|
|
||||||
$options['table'] = $db->fullTableName($_Model, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($options['fields']) || !is_array($options['fields']))
|
|
||||||
$options['fields'] = $db->fields($_Model, $modelAlias);
|
|
||||||
elseif (!empty($options['fields']))
|
|
||||||
$options['fields'] = $db->fields($_Model, $modelAlias, $options['fields']);
|
|
||||||
|
|
||||||
$query['fields'] = array_merge($query['fields'], $options['fields'],
|
|
||||||
(empty($association['fields'])
|
|
||||||
? array() : $db->fields($_Model, $modelAlias, $association['fields'])));
|
|
||||||
|
|
||||||
$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));
|
|
||||||
|
|
||||||
$this->pr(19,
|
|
||||||
array('checkpoint' => 'Model Join Complete',
|
|
||||||
compact('options', 'modelClass', 'modelAlias', 'query'),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
++$cont;
|
|
||||||
$notDone = isset($iterators[$cont]);
|
|
||||||
} while ($notDone);
|
|
||||||
}
|
|
||||||
$this->pr(20,
|
|
||||||
array('function' => 'Linkable::beforeFind',
|
|
||||||
'return' => compact('query'),
|
|
||||||
));
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
class Close extends AppModel {
|
|
||||||
|
|
||||||
var $belongsTo = array(
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'Ledger',
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
<?php
|
|
||||||
class Customer extends AppModel {
|
|
||||||
|
|
||||||
var $name = 'Customer';
|
|
||||||
var $validate = array(
|
|
||||||
'id' => array('numeric'),
|
|
||||||
'name' => array('notempty'),
|
|
||||||
);
|
|
||||||
|
|
||||||
var $belongsTo = array(
|
|
||||||
'PrimaryContact' => array(
|
|
||||||
'className' => 'Contact',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'CurrentLease' => array(
|
|
||||||
'className' => 'Lease',
|
|
||||||
'conditions' => 'CurrentLease.close_date IS NULL',
|
|
||||||
),
|
|
||||||
'Lease',
|
|
||||||
'LedgerEntry',
|
|
||||||
'ContactsCustomer',
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasAndBelongsToMany = array(
|
|
||||||
'Contact',
|
|
||||||
'Transaction' => array(
|
|
||||||
'joinTable' => 'ledger_entries',
|
|
||||||
'foreignKey' => 'customer_id',
|
|
||||||
'associationForeignKey' => 'transaction_id',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: accountId
|
|
||||||
* - Returns the account ID for the given customer
|
|
||||||
*/
|
|
||||||
function accountId($id) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$customer = $this->find('first',
|
|
||||||
array('contain' => false,
|
|
||||||
'fields' => array('account_id'),
|
|
||||||
'conditions' => array(array('Customer.id' => $id))));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
|
|
||||||
return $customer['Customer']['account_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: leaseIds
|
|
||||||
* - Returns the lease IDs for the given customer
|
|
||||||
*/
|
|
||||||
function leaseIds($id) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$customer = $this->find('first',
|
|
||||||
array('contain' =>
|
|
||||||
array('Lease' => array('fields' => array('id'))),
|
|
||||||
'fields' => array(),
|
|
||||||
'conditions' => array(array('Customer.id' => $id))));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
|
|
||||||
$ids = array();
|
|
||||||
foreach ($customer['Lease'] AS $lease)
|
|
||||||
$ids[] = $lease['id'];
|
|
||||||
|
|
||||||
return $ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: findSecurityDeposits
|
|
||||||
* - Returns an array of security deposit entries
|
|
||||||
*/
|
|
||||||
function findSecurityDeposits($id, $link = null) {
|
|
||||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
|
||||||
/* 'args' => compact('id', 'link'), */
|
|
||||||
/* )); */
|
|
||||||
|
|
||||||
$A = new Account();
|
|
||||||
$entries = $A->findLedgerEntries
|
|
||||||
($A->securityDepositAccountID(),
|
|
||||||
true, array('LedgerEntry.customer_id' => $id), $link);
|
|
||||||
|
|
||||||
/* pr(array('function' => 'Customer::findSecurityDeposits', */
|
|
||||||
/* 'args' => compact('id', 'link'), */
|
|
||||||
/* 'vars' => compact('customer'), */
|
|
||||||
/* '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) {
|
|
||||||
$A = new Account();
|
|
||||||
$unreconciled = $A->findUnreconciledLedgerEntries
|
|
||||||
($A->accountReceivableAccountID(),
|
|
||||||
$fundamental_type,
|
|
||||||
array('LedgerEntry.customer_id' => $id));
|
|
||||||
|
|
||||||
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) {
|
|
||||||
$A = new Account();
|
|
||||||
$reconciled = $A->reconcileNewLedgerEntry
|
|
||||||
($A->accountReceivableAccountID(),
|
|
||||||
$fundamental_type,
|
|
||||||
$amount,
|
|
||||||
array('LedgerEntry.customer_id' => $id));
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: saveCustomer
|
|
||||||
* - Saves the customer and related data
|
|
||||||
*/
|
|
||||||
|
|
||||||
function saveCustomer($id, $data, $primary_contact_entry) {
|
|
||||||
|
|
||||||
// Go through each contact, and create new ones as needed
|
|
||||||
foreach ($data['Contact'] AS &$contact) {
|
|
||||||
if (isset($contact['id']))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$I = new Contact();
|
|
||||||
$I->create();
|
|
||||||
if (!$I->save($contact, false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$contact['id'] = $I->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the primary contact ID based on caller selection
|
|
||||||
$data['Customer']['primary_contact_id']
|
|
||||||
= $data['Contact'][$primary_contact_entry]['id'];
|
|
||||||
|
|
||||||
// Provide a default customer name if not specified
|
|
||||||
if (!$data['Customer']['name']) {
|
|
||||||
$this->Contact->recursive = -1;
|
|
||||||
$pcontact = $this->Contact->read(null, $data['Customer']['primary_contact_id']);
|
|
||||||
$data['Customer']['name'] = $pcontact['Contact']['display_name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the customer data
|
|
||||||
$this->create();
|
|
||||||
if ($id)
|
|
||||||
$this->id = $id;
|
|
||||||
if (!$this->save($data, false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$id = $this->id;
|
|
||||||
|
|
||||||
// Remove all associated Customer Contacts, as it ensures
|
|
||||||
// any entries deleted by the user actually get deleted
|
|
||||||
// in the system. We'll recreate the needed ones anyway.
|
|
||||||
// REVISIT <AP>: 20090706
|
|
||||||
// Appears that $this->save() is already doing the
|
|
||||||
// delete. I would have thought this would only happen
|
|
||||||
// on a saveAll??
|
|
||||||
/* $this->ContactsCustomer->deleteAll */
|
|
||||||
/* (array('customer_id' => $id), false); */
|
|
||||||
|
|
||||||
// At this point, since we've saved data to customer,
|
|
||||||
// we'll proceed forward as much as possible, even
|
|
||||||
// if we encounter an error. For now, we'll assume
|
|
||||||
// the operation will succeed.
|
|
||||||
$ret = true;
|
|
||||||
|
|
||||||
// Go through each entry of this customer method
|
|
||||||
foreach ($data['Contact'] AS &$contact) {
|
|
||||||
// Update the ContactsCustomer to reflect the appropriate IDs
|
|
||||||
$contact['ContactsCustomer']['customer_id'] = $id;
|
|
||||||
$contact['ContactsCustomer']['contact_id'] = $contact['id'];
|
|
||||||
|
|
||||||
// Save the relationship between customer and contact
|
|
||||||
$CM = new ContactsCustomer();
|
|
||||||
if (!$CM->save($contact['ContactsCustomer'], false)) {
|
|
||||||
$ret = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the result
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: stats
|
|
||||||
* - Returns summary data from the requested customer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function stats($id = null) {
|
|
||||||
if (!$id)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$A = new Account();
|
|
||||||
$stats = $A->stats($A->accountReceivableAccountID(), true,
|
|
||||||
array('LedgerEntry.customer_id' => $id));
|
|
||||||
|
|
||||||
// Pull to the top level and return
|
|
||||||
$stats = $stats['Ledger'];
|
|
||||||
return $stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
506
models/lease.php
506
models/lease.php
@@ -1,506 +0,0 @@
|
|||||||
<?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',
|
|
||||||
'Customer',
|
|
||||||
'LateSchedule',
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'LedgerEntry',
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* 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
|
|
||||||
* - Returns an array of security deposit entries
|
|
||||||
*/
|
|
||||||
function findSecurityDeposits($id, $link = null) {
|
|
||||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
|
||||||
/* 'args' => compact('id', 'link'), */
|
|
||||||
/* )); */
|
|
||||||
|
|
||||||
$A = new Account();
|
|
||||||
$entries = $A->findLedgerEntries
|
|
||||||
($A->securityDepositAccountID(),
|
|
||||||
true, array('LedgerEntry.lease_id' => $id), $link);
|
|
||||||
|
|
||||||
/* pr(array('function' => 'Lease::findSecurityDeposits', */
|
|
||||||
/* 'args' => compact('id', 'link'), */
|
|
||||||
/* 'vars' => compact('lease'), */
|
|
||||||
/* '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) {
|
|
||||||
$A = new Account();
|
|
||||||
return $A->findUnreconciledLedgerEntries
|
|
||||||
($this->accountId($id), $fundamental_type, array('LedgerEntry.lease_id' => $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* 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) {
|
|
||||||
$A = new Account();
|
|
||||||
return $A->reconcileNewLedgerEntry
|
|
||||||
($this->accountId($id), $fundamental_type, $amount, array('LedgerEntry.lease_id' => $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: rentLastCharges
|
|
||||||
* - Returns a list of rent charges from this lease that
|
|
||||||
* do not have sequential followup charges. Under normal
|
|
||||||
* circumstances, there would only be one entry, which is
|
|
||||||
* the most recent rent charge. However, it's possible
|
|
||||||
* that there are several, indicating a problem with lease.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function rentLastCharges($id) {
|
|
||||||
$A = new Account();
|
|
||||||
$entries = $this->find
|
|
||||||
('all',
|
|
||||||
array('link' =>
|
|
||||||
array(// Models
|
|
||||||
'LedgerEntry' => array
|
|
||||||
('Ledger' => array
|
|
||||||
('fields' => array(),
|
|
||||||
'Account' => array
|
|
||||||
('fields' => array(),
|
|
||||||
'Ledger' => array
|
|
||||||
('alias' => 'Lx',
|
|
||||||
'fields' => array(),
|
|
||||||
'LedgerEntry' => array
|
|
||||||
('alias' => 'LEx',
|
|
||||||
'fields' => array(),
|
|
||||||
'conditions' => array
|
|
||||||
('LEx.effective_date = DATE_ADD(LedgerEntry.through_date, INTERVAL 1 day)',
|
|
||||||
'LEx.lease_id = LedgerEntry.lease_id',
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
),
|
|
||||||
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
|
|
||||||
'fields' => array(),
|
|
||||||
'conditions' => array(array('Lease.id' => $id),
|
|
||||||
array('Account.id' => $A->rentAccountID()),
|
|
||||||
array('LEx.id' => null),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return $entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: rentChargeGaps
|
|
||||||
* - Checks for gaps in rent charges
|
|
||||||
*/
|
|
||||||
|
|
||||||
function rentChargeGaps($id) {
|
|
||||||
$entries = $this->rentLastCharges($id);
|
|
||||||
if ($entries && count($entries) > 1)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: rentChargeThrough
|
|
||||||
* - Determines the date that rent has been charged through
|
|
||||||
* Returns one of:
|
|
||||||
* null: There are gaps in the charges
|
|
||||||
* false: There are not yet any charges
|
|
||||||
* date: The date rent has been charged through
|
|
||||||
*/
|
|
||||||
|
|
||||||
function rentChargeThrough($id) {
|
|
||||||
$entries = $this->rentLastCharges($id);
|
|
||||||
if (!$entries)
|
|
||||||
return false;
|
|
||||||
if (count($entries) != 1)
|
|
||||||
return null;
|
|
||||||
return $entries[0]['LedgerEntry']['through_date'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: rentPaidThrough
|
|
||||||
* - Determines the date of the first unpaid rent
|
|
||||||
*/
|
|
||||||
|
|
||||||
function rentPaidThrough($id) {
|
|
||||||
|
|
||||||
// Income / Receipt / Money
|
|
||||||
// debit: A/R credit: Income <-- this entry
|
|
||||||
// debit: Receipt credit: A/R <-- ReceiptLedgerEntry, below
|
|
||||||
// debit: Money credit: Receipt <-- MoneyLedgerEntry, below
|
|
||||||
|
|
||||||
$query = array
|
|
||||||
('link' => array
|
|
||||||
(
|
|
||||||
'CreditLedger' =>
|
|
||||||
array('fields' => array(),
|
|
||||||
'Account' =>
|
|
||||||
array('fields' => array(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// We're searching for the Receipt<->A/R entries,
|
|
||||||
// which are debits on the A/R account. Find the
|
|
||||||
// reconciling entries to that A/R debit.
|
|
||||||
'DebitReconciliationLedgerEntry' =>
|
|
||||||
array('alias' => 'ReceiptLedgerEntry',
|
|
||||||
'fields' => array(),
|
|
||||||
|
|
||||||
// Finally, the Money (Cash/Check/etc) Entry is the one
|
|
||||||
// which reconciles our ReceiptLedgerEntry debit
|
|
||||||
'DebitReconciliationLedgerEntry' =>
|
|
||||||
array('alias' => 'MoneyLedgerEntry',
|
|
||||||
'linkalias' => 'MoneyLedgerEntryR',
|
|
||||||
'fields' => array('SUM(COALESCE(MoneyLedgerEntryR.amount,0)) AS paid'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'fields' => array('LedgerEntry.amount',
|
|
||||||
'DATE_SUB(LedgerEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
|
||||||
),
|
|
||||||
|
|
||||||
'group' => 'LedgerEntry.id HAVING paid <> LedgerEntry.amount',
|
|
||||||
|
|
||||||
'conditions' => array(array('LedgerEntry.lease_id' => $id),
|
|
||||||
array('Account.id' => $this->LedgerEntry->Ledger->Account->rentAccountID()),
|
|
||||||
),
|
|
||||||
'order' => array('LedgerEntry.effective_date',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
$rent = $this->LedgerEntry->find('first', $query);
|
|
||||||
if ($rent)
|
|
||||||
return $rent[0]['paid_through'];
|
|
||||||
|
|
||||||
$query['fields'] = 'LedgerEntry.through_date';
|
|
||||||
$query['order'] = 'LedgerEntry.through_date DESC';
|
|
||||||
$query['group'] = 'LedgerEntry.id';
|
|
||||||
$rent = $this->LedgerEntry->find('first', $query);
|
|
||||||
if ($rent)
|
|
||||||
return $rent['LedgerEntry']['through_date'];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: moveIn
|
|
||||||
* - Moves the specified customer into the specified lease
|
|
||||||
*/
|
|
||||||
|
|
||||||
function moveIn($customer_id, $unit_id,
|
|
||||||
$deposit = null, $rent = null,
|
|
||||||
$stamp = null, $comment = null) {
|
|
||||||
|
|
||||||
$lt = $this->LeaseType->find('first',
|
|
||||||
array('conditions' =>
|
|
||||||
array('code' => 'SL')));
|
|
||||||
|
|
||||||
// Use NOW if not given a movein date
|
|
||||||
if (!isset($stamp))
|
|
||||||
$stamp = date('Y-m-d G:i:s');
|
|
||||||
|
|
||||||
if (!$comment)
|
|
||||||
$comment = null;
|
|
||||||
|
|
||||||
if (!isset($deposit) || !isset($rent)) {
|
|
||||||
$rates = $this->Unit->find
|
|
||||||
('first',
|
|
||||||
array('contain' =>
|
|
||||||
array('UnitSize' =>
|
|
||||||
array('deposit', 'rent'),
|
|
||||||
),
|
|
||||||
'fields' => array('deposit', 'rent'),
|
|
||||||
'conditions' => array('Unit.id' => $unit_id),
|
|
||||||
));
|
|
||||||
|
|
||||||
$deposit =
|
|
||||||
(isset($deposit)
|
|
||||||
? $deposit
|
|
||||||
: (isset($rates['Unit']['deposit'])
|
|
||||||
? $rates['Unit']['deposit']
|
|
||||||
: (isset($rates['UnitSize']['deposit'])
|
|
||||||
? $rates['UnitSize']['deposit']
|
|
||||||
: 0)));
|
|
||||||
|
|
||||||
$rent =
|
|
||||||
(isset($rent)
|
|
||||||
? $rent
|
|
||||||
: (isset($rates['Unit']['rent'])
|
|
||||||
? $rates['Unit']['rent']
|
|
||||||
: (isset($rates['UnitSize']['rent'])
|
|
||||||
? $rates['UnitSize']['rent']
|
|
||||||
: 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Save this new lease.
|
|
||||||
$this->create();
|
|
||||||
if (!$this->save(array('lease_type_id' => $lt['LeaseType']['id'],
|
|
||||||
'unit_id' => $unit_id,
|
|
||||||
'customer_id' => $customer_id,
|
|
||||||
'lease_date' => $stamp,
|
|
||||||
'movein_date' => $stamp,
|
|
||||||
'deposit' => $deposit,
|
|
||||||
'rent' => $rent,
|
|
||||||
'comment' => $comment), false)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the lease number to be the same as the lease ID
|
|
||||||
$this->id;
|
|
||||||
$this->saveField('number', $this->id);
|
|
||||||
|
|
||||||
// Update the unit status
|
|
||||||
$this->Unit->updateStatus($unit_id, 'OCCUPIED');
|
|
||||||
|
|
||||||
// REVISIT <AP>: 20090702
|
|
||||||
// We need to assess the security deposit charge,
|
|
||||||
// and probably rent as well. Rent, however, will
|
|
||||||
// require user parameters to indicate whether it
|
|
||||||
// was waived, pro-rated, etc.
|
|
||||||
|
|
||||||
// Return the new lease ID
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: moveOut
|
|
||||||
* - Moves the customer out of the specified lease
|
|
||||||
*/
|
|
||||||
|
|
||||||
function moveOut($id, $status = 'VACANT',
|
|
||||||
$stamp = null, $close = false) {
|
|
||||||
// Use NOW if not given a moveout date
|
|
||||||
if (!isset($stamp))
|
|
||||||
$stamp = date('Y-m-d G:i:s');
|
|
||||||
|
|
||||||
// Reset the data
|
|
||||||
$this->create();
|
|
||||||
$this->id = $id;
|
|
||||||
|
|
||||||
// Set the customer move-out date
|
|
||||||
$this->data['Lease']['moveout_date'] = $stamp;
|
|
||||||
|
|
||||||
// Save it!
|
|
||||||
$this->save($this->data, false);
|
|
||||||
|
|
||||||
// Close the lease, if so requested
|
|
||||||
if ($close)
|
|
||||||
$this->close($id, $stamp);
|
|
||||||
|
|
||||||
// Finally, update the unit status
|
|
||||||
$this->recursive = -1;
|
|
||||||
$this->read();
|
|
||||||
$this->Unit->updateStatus($this->data['Lease']['unit_id'], $status);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: close
|
|
||||||
* - Closes the lease to further action
|
|
||||||
*/
|
|
||||||
|
|
||||||
function close($id, $stamp = null) {
|
|
||||||
if (!$this->closeable($id))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Reset the data
|
|
||||||
$this->create();
|
|
||||||
$this->id = $id;
|
|
||||||
|
|
||||||
// Use NOW if not given a moveout date
|
|
||||||
if (!isset($stamp))
|
|
||||||
$stamp = date('Y-m-d G:i:s');
|
|
||||||
|
|
||||||
// Set the close date
|
|
||||||
$this->data['Lease']['close_date'] = $stamp;
|
|
||||||
|
|
||||||
// Save it!
|
|
||||||
$this->save($this->data, false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: closeable
|
|
||||||
* - Indicates whether or not the lease can be closed
|
|
||||||
*/
|
|
||||||
|
|
||||||
function closeable($id) {
|
|
||||||
$this->recursive = -1;
|
|
||||||
$this->read(null, $id);
|
|
||||||
|
|
||||||
// We can't close a lease that's still in use
|
|
||||||
if (!isset($this->data['Lease']['moveout_date']))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// We can't close a lease that's already closed
|
|
||||||
if (isset($this->data['Lease']['close_date']))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$deposits = $this->findSecurityDeposits($id);
|
|
||||||
$stats = $this->stats($id);
|
|
||||||
|
|
||||||
// A lease can only be closed if there are no outstanding
|
|
||||||
// security deposits, and if the account balance is zero.
|
|
||||||
if ($deposits['summary']['balance'] != 0 || $stats['balance'] != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Apparently this lease meets all the criteria!
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: addCharge
|
|
||||||
* - Adds an additional charge to the lease
|
|
||||||
*/
|
|
||||||
|
|
||||||
function addCharge($id, $charge) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: stats
|
|
||||||
* - Returns summary data from the requested lease.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function stats($id = null) {
|
|
||||||
if (!$id)
|
|
||||||
return 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
<?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',
|
|
||||||
);
|
|
||||||
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: accountID
|
|
||||||
* - Returns the account ID for the given ledger
|
|
||||||
*/
|
|
||||||
function accountID($id) {
|
|
||||||
$this->cacheQueries = true;
|
|
||||||
$item = $this->find('first', array
|
|
||||||
('contain' => 'Account.id',
|
|
||||||
'conditions' => array('Ledger.id' => $id),
|
|
||||||
));
|
|
||||||
$this->cacheQueries = false;
|
|
||||||
return $item['Account']['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: currentLedgerID
|
|
||||||
* - Returns the current ledger ID of the account for the given ledger.
|
|
||||||
*/
|
|
||||||
function currentLedgerID($id) {
|
|
||||||
return $this->Account->currentLedgerID($this->accountID($id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: closeLedger
|
|
||||||
* - Closes the current ledger, and returns a fresh one
|
|
||||||
*/
|
|
||||||
function closeLedger($id, $close_id) {
|
|
||||||
$this->recursive = -1;
|
|
||||||
|
|
||||||
$stamp = date('Y-m-d G:i:s');
|
|
||||||
$this->id = $id;
|
|
||||||
$this->read();
|
|
||||||
$this->data['Ledger']['close_id'] = $close_id;
|
|
||||||
$this->save($this->data, false);
|
|
||||||
|
|
||||||
$stats = $this->stats($id);
|
|
||||||
|
|
||||||
$this->read();
|
|
||||||
$this->data['Ledger']['id'] = null;
|
|
||||||
$this->data['Ledger']['close_id'] = null;
|
|
||||||
$this->data['Ledger']['prior_ledger_id'] = $id;
|
|
||||||
$this->data['Ledger']['comment'] = null;
|
|
||||||
++$this->data['Ledger']['sequence'];
|
|
||||||
$this->id = null;
|
|
||||||
$this->save($this->data, false);
|
|
||||||
//pr($this->data);
|
|
||||||
|
|
||||||
if ($stats['balance'] == 0)
|
|
||||||
return $this->id;
|
|
||||||
|
|
||||||
$this->read();
|
|
||||||
$ftype = $this->Account->fundamentalType($this->data['Ledger']['account_id']);
|
|
||||||
$otype = $this->Account->fundamentalOpposite($ftype);
|
|
||||||
|
|
||||||
// Create a transaction for balance transfer
|
|
||||||
$transaction = new Transaction();
|
|
||||||
$transaction->create();
|
|
||||||
if (!$transaction->save(array(), false)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an entry to carry the balance forward
|
|
||||||
$carry_entry_data = array
|
|
||||||
($ftype.'_ledger_id' => $this->id,
|
|
||||||
$otype.'_ledger_id' => $id,
|
|
||||||
'transaction_id' => $transaction->id,
|
|
||||||
'amount' => $stats['balance'],
|
|
||||||
'comment' => "Ledger Balance Forward",
|
|
||||||
);
|
|
||||||
|
|
||||||
$carry_entry = new LedgerEntry();
|
|
||||||
$carry_entry->create();
|
|
||||||
if (!$carry_entry->save($carry_entry_data, false)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: findLedgerEntries
|
|
||||||
* - Returns an array of ledger entries that belong to a given
|
|
||||||
* ledger. There is extra work done... see the LedgerEntry model.
|
|
||||||
*/
|
|
||||||
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'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
$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->findInLedgerContext($id, $account_type, $cond, $link);
|
|
||||||
/* pr(array('function' => 'Ledger::findLedgerEntries', */
|
|
||||||
/* 'args' => compact('id', 'account_type', 'cond', 'link'), */
|
|
||||||
/* 'vars' => compact('ledger'), */
|
|
||||||
/* 'return' => compact('entries'), */
|
|
||||||
/* )); */
|
|
||||||
return $entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: stats
|
|
||||||
* - Returns summary data from the requested ledger.
|
|
||||||
*/
|
|
||||||
function stats($id, $cond = null) {
|
|
||||||
if (!isset($cond))
|
|
||||||
$cond = array();
|
|
||||||
$cond[] = array('Ledger.id' => $id);
|
|
||||||
|
|
||||||
$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',
|
|
||||||
));
|
|
||||||
|
|
||||||
// The fields are all tucked into the [0] index,
|
|
||||||
// and the rest of the array is useless (empty).
|
|
||||||
$stats = $stats[0];
|
|
||||||
|
|
||||||
// Make sure we have a non-null balance
|
|
||||||
if (!isset($stats['balance']))
|
|
||||||
$stats['balance'] = 0;
|
|
||||||
|
|
||||||
return $stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,525 +0,0 @@
|
|||||||
<?php
|
|
||||||
class LedgerEntry extends AppModel {
|
|
||||||
|
|
||||||
var $name = 'LedgerEntry';
|
|
||||||
var $validate = array(
|
|
||||||
'id' => array('numeric'),
|
|
||||||
'transaction_id' => array('numeric'),
|
|
||||||
'amount' => array('money')
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'DebitReconciliation' => array(
|
|
||||||
'className' => 'Reconciliation',
|
|
||||||
'foreignKey' => 'debit_ledger_entry_id',
|
|
||||||
),
|
|
||||||
'CreditReconciliation' => array(
|
|
||||||
'className' => 'Reconciliation',
|
|
||||||
'foreignKey' => 'credit_ledger_entry_id',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
var $belongsTo = array(
|
|
||||||
'MonetarySource',
|
|
||||||
'Transaction',
|
|
||||||
'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(
|
|
||||||
'className' => 'LedgerEntry',
|
|
||||||
'joinTable' => 'reconciliations',
|
|
||||||
'foreignKey' => 'credit_ledger_entry_id',
|
|
||||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
|
||||||
),
|
|
||||||
// STUPID CakePHP bug screws up when using Containable
|
|
||||||
// and CLASS contains CLASS. This extra HABTM give the
|
|
||||||
// option of multiple depths on one CLASS, since there
|
|
||||||
// isn't an alias specification for Containable.
|
|
||||||
'DebitReconciliationLedgerEntry2' => array(
|
|
||||||
'className' => 'LedgerEntry',
|
|
||||||
'joinTable' => 'reconciliations',
|
|
||||||
'foreignKey' => 'credit_ledger_entry_id',
|
|
||||||
'associationForeignKey' => 'debit_ledger_entry_id',
|
|
||||||
),
|
|
||||||
'CreditReconciliationLedgerEntry' => array(
|
|
||||||
'className' => 'LedgerEntry',
|
|
||||||
'joinTable' => 'reconciliations',
|
|
||||||
'foreignKey' => 'debit_ledger_entry_id',
|
|
||||||
'associationForeignKey' => 'credit_ledger_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 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");
|
|
||||||
|
|
||||||
if (isset($account_type)) {
|
|
||||||
if (in_array($account_type, array('ASSET', 'EXPENSE')))
|
|
||||||
$ledger_type = 'debit';
|
|
||||||
else
|
|
||||||
$ledger_type = 'credit';
|
|
||||||
|
|
||||||
$fields[] = ("(IF(LedgerEntry.{$ledger_type}_ledger_id = $ledger_id," .
|
|
||||||
" 1, -1) * LedgerEntry.amount) AS balance");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) {
|
|
||||||
$fields = array('id', '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");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 findInLedgerContext($ledger_id, $account_type, $cond = null, $link = null) {
|
|
||||||
if (!isset($link))
|
|
||||||
$link = array('Transaction');
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* 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')));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: stats
|
|
||||||
* - Returns summary data from the requested ledger entry
|
|
||||||
*/
|
|
||||||
function stats($id) {
|
|
||||||
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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')
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -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 +0,0 @@
|
|||||||
<?php
|
|
||||||
class Site extends AppModel {
|
|
||||||
|
|
||||||
var $name = 'Site';
|
|
||||||
var $validate = array(
|
|
||||||
'id' => array('numeric'),
|
|
||||||
'name' => array('notempty')
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'SiteArea',
|
|
||||||
'SiteOption',
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
<?php
|
|
||||||
class Transaction extends AppModel {
|
|
||||||
|
|
||||||
var $name = 'Transaction';
|
|
||||||
|
|
||||||
var $validate = array(
|
|
||||||
'stamp' => array('date')
|
|
||||||
);
|
|
||||||
|
|
||||||
var $belongsTo = array(
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'LedgerEntry',
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: addInvoice
|
|
||||||
* - Adds a new invoice transaction
|
|
||||||
*/
|
|
||||||
|
|
||||||
function addInvoice($data, $customer_id, $lease_id = null) {
|
|
||||||
// Create some models for convenience
|
|
||||||
$A = new Account();
|
|
||||||
|
|
||||||
//pr(compact('data', 'customer_id', 'lease_id'));
|
|
||||||
|
|
||||||
// Assume this will succeed
|
|
||||||
$ret = true;
|
|
||||||
|
|
||||||
// Determine the total charges on the invoice
|
|
||||||
$grand_total = 0;
|
|
||||||
foreach ($data['LedgerEntry'] AS $entry)
|
|
||||||
$grand_total += $entry['amount'];
|
|
||||||
|
|
||||||
// Go through the entered charges
|
|
||||||
$invoice_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
|
|
||||||
foreach ($data['LedgerEntry'] AS $entry) {
|
|
||||||
//pr(compact('entry'));
|
|
||||||
// Create the receipt entry, and reconcile the credit side
|
|
||||||
// of the double-entry (which should be A/R) as a payment.
|
|
||||||
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
|
|
||||||
($invoice_transaction,
|
|
||||||
array_intersect_key($entry, array('MonetarySource'=>1))
|
|
||||||
+ array_intersect_key($entry, array('account_id'=>1)),
|
|
||||||
array('debit_ledger_id' => $A->currentLedgerID($A->accountReceivableAccountID()),
|
|
||||||
'credit_ledger_id' => $A->currentLedgerID($entry['account_id']),
|
|
||||||
'customer_id' => $customer_id,
|
|
||||||
'lease_id' => $lease_id)
|
|
||||||
+ $entry
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($ids['error'])
|
|
||||||
$ret = false;
|
|
||||||
|
|
||||||
$invoice_transaction = array_intersect_key($ids, array('transaction_id'=>1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: addReceipt
|
|
||||||
* - Adds a new receipt transaction
|
|
||||||
*/
|
|
||||||
|
|
||||||
function addReceipt($data, $customer_id, $lease_id = null) {
|
|
||||||
// Create some models for convenience
|
|
||||||
$A = new Account();
|
|
||||||
|
|
||||||
// Assume this will succeed
|
|
||||||
$ret = true;
|
|
||||||
|
|
||||||
// Go through the entered payments
|
|
||||||
$receipt_transaction = array_intersect_key($data, array('Transaction'=>1, 'transaction_id'=>1));
|
|
||||||
foreach ($data['LedgerEntry'] AS $entry) {
|
|
||||||
// Create the receipt entry, and reconcile the credit side
|
|
||||||
// of the double-entry (which should be A/R) as a payment.
|
|
||||||
$ids = $this->LedgerEntry->Ledger->Account->postLedgerEntry
|
|
||||||
($receipt_transaction,
|
|
||||||
array_intersect_key($entry, array('MonetarySource'=>1))
|
|
||||||
+ array_intersect_key($entry, array('account_id'=>1)),
|
|
||||||
array('debit_ledger_id' => $A->currentLedgerID($entry['account_id']),
|
|
||||||
'credit_ledger_id' => $A->currentLedgerID($A->receiptAccountID()),
|
|
||||||
'customer_id' => $customer_id,
|
|
||||||
'lease_id' => $lease_id)
|
|
||||||
+ $entry,
|
|
||||||
'receipt');
|
|
||||||
|
|
||||||
if ($ids['error'])
|
|
||||||
$ret = false;
|
|
||||||
|
|
||||||
$receipt_transaction = array_intersect_key($ids,
|
|
||||||
array('transaction_id'=>1,
|
|
||||||
'split_transaction_id'=>1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
119
models/unit.php
119
models/unit.php
@@ -1,119 +0,0 @@
|
|||||||
<?php
|
|
||||||
class Unit extends AppModel {
|
|
||||||
|
|
||||||
var $name = 'Unit';
|
|
||||||
var $validate = array(
|
|
||||||
'id' => array('numeric'),
|
|
||||||
'unit_size_id' => array('numeric'),
|
|
||||||
'name' => array('notempty'),
|
|
||||||
'sort_order' => array('numeric'),
|
|
||||||
'walk_order' => array('numeric'),
|
|
||||||
'deposit' => array('money'),
|
|
||||||
'amount' => array('money')
|
|
||||||
);
|
|
||||||
|
|
||||||
var $belongsTo = array(
|
|
||||||
'UnitSize',
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasOne = array(
|
|
||||||
'CurrentLease' => array(
|
|
||||||
'className' => 'Lease',
|
|
||||||
'conditions' => 'CurrentLease.moveout_date IS NULL',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'Lease',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* helpers: status enumerations
|
|
||||||
*/
|
|
||||||
|
|
||||||
function statusEnums() {
|
|
||||||
static $status_enums;
|
|
||||||
if (!isset($status_enums))
|
|
||||||
$status_enums = $this->getEnumValues('status');
|
|
||||||
return $status_enums;
|
|
||||||
}
|
|
||||||
|
|
||||||
function activeStatusEnums() {
|
|
||||||
return array_diff_key($this->statusEnums(), array(''=>1, 'DELETED'=>1));
|
|
||||||
}
|
|
||||||
|
|
||||||
function statusValue($enum) {
|
|
||||||
$enums = $this->statusEnums();
|
|
||||||
return $enums[$enum];
|
|
||||||
}
|
|
||||||
|
|
||||||
function occupiedEnumValue() {
|
|
||||||
return statusValue('OCCUPIED');
|
|
||||||
}
|
|
||||||
|
|
||||||
function conditionOccupied() {
|
|
||||||
return ('Unit.status >= ' . $this->statusValue('OCCUPIED'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function conditionVacant() {
|
|
||||||
return ('Unit.status BETWEEN ' .
|
|
||||||
($this->statusValue('UNAVAILABLE')+1) .
|
|
||||||
' AND ' .
|
|
||||||
($this->statusValue('OCCUPIED')-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
function conditionUnavailable() {
|
|
||||||
return ('Unit.status <= ' . $this->statusValue('UNAVAILABLE'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
**************************************************************************
|
|
||||||
* function: 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: stats
|
|
||||||
* - Returns summary data from the requested customer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function stats($id = null) {
|
|
||||||
if (!$id)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Get the basic information necessary
|
|
||||||
$unit = $this->find('first',
|
|
||||||
array('contain' => array
|
|
||||||
('Lease' => array
|
|
||||||
('fields' => array('Lease.id')),
|
|
||||||
|
|
||||||
'CurrentLease' => array
|
|
||||||
('fields' => array('CurrentLease.id'))),
|
|
||||||
|
|
||||||
'conditions' => array
|
|
||||||
(array('Unit.id' => $id)),
|
|
||||||
));
|
|
||||||
|
|
||||||
// Get the stats for the current lease
|
|
||||||
$stats['CurrentLease'] = $this->Lease->stats($unit['CurrentLease']['id']);
|
|
||||||
|
|
||||||
// Sum the stats for all leases together
|
|
||||||
foreach ($unit['Lease'] AS $lease) {
|
|
||||||
$this->statsMerge($stats['Lease'], $this->Lease->stats($lease['id']));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the collection
|
|
||||||
return $stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<?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(
|
|
||||||
'UnitType',
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'Unit',
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
class UnitType extends AppModel {
|
|
||||||
|
|
||||||
var $name = 'UnitType';
|
|
||||||
var $validate = array(
|
|
||||||
'id' => array('numeric'),
|
|
||||||
'code' => array('notempty'),
|
|
||||||
'name' => array('notempty')
|
|
||||||
);
|
|
||||||
|
|
||||||
var $hasMany = array(
|
|
||||||
'UnitSize',
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
25
site/.htaccess
Normal file
25
site/.htaccess
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
RewriteEngine on
|
||||||
|
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
|
||||||
1242
site/app_controller.php
Normal file
1242
site/app_controller.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -37,5 +37,14 @@ App::import('Core', 'Helper');
|
|||||||
* @subpackage cake.cake
|
* @subpackage cake.cake
|
||||||
*/
|
*/
|
||||||
class AppHelper extends Helper {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
518
site/app_model.php
Normal file
518
site/app_model.php
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
<?php
|
||||||
|
/* SVN FILE: $Id: app_model.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application model for Cake.
|
||||||
|
*
|
||||||
|
* This file is application-wide model file. You can put all
|
||||||
|
* application-wide model-related methods here.
|
||||||
|
*
|
||||||
|
* PHP versions 4 and 5
|
||||||
|
*
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||||
|
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @filesource
|
||||||
|
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||||
|
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||||
|
* @package cake
|
||||||
|
* @subpackage cake.app
|
||||||
|
* @since CakePHP(tm) v 0.2.9
|
||||||
|
* @version $Revision: 7945 $
|
||||||
|
* @modifiedby $LastChangedBy: gwoo $
|
||||||
|
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application model for Cake.
|
||||||
|
*
|
||||||
|
* Add your application-wide methods in the class below, your models
|
||||||
|
* will inherit them.
|
||||||
|
*
|
||||||
|
* @package cake
|
||||||
|
* @subpackage cake.app
|
||||||
|
*/
|
||||||
|
class AppModel extends Model {
|
||||||
|
|
||||||
|
var $actsAs = array('Containable', 'Linkable');
|
||||||
|
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
|
||||||
|
* http://cakeforge.org/snippet/detail.php?type=snippet&id=112
|
||||||
|
*
|
||||||
|
* Gets the enum values for MySQL 4 and 5 to use in selectTag()
|
||||||
|
*/
|
||||||
|
function getEnumValues($columnName=null, $tableName=null)
|
||||||
|
{
|
||||||
|
if ($columnName==null) { return array(); } //no field specified
|
||||||
|
|
||||||
|
if (!isset($tableName)) {
|
||||||
|
//Get the name of the table
|
||||||
|
$db =& ConnectionManager::getDataSource($this->useDbConfig);
|
||||||
|
$tableName = $db->fullTableName($this, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the values for the specified column (database and version specific, needs testing)
|
||||||
|
$result = $this->query("SHOW COLUMNS FROM {$tableName} LIKE '{$columnName}'");
|
||||||
|
|
||||||
|
//figure out where in the result our Types are (this varies between mysql versions)
|
||||||
|
$types = null;
|
||||||
|
if ( isset( $result[0]['COLUMNS']['Type'] ) ) { //MySQL 5
|
||||||
|
$types = $result[0]['COLUMNS']['Type']; $default = $result[0]['COLUMNS']['Default'];
|
||||||
|
}
|
||||||
|
elseif ( isset( $result[0][0]['Type'] ) ) { //MySQL 4
|
||||||
|
$types = $result[0][0]['Type']; $default = $result[0][0]['Default'];
|
||||||
|
}
|
||||||
|
else { //types return not accounted for
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Get the values
|
||||||
|
return array_flip(array_merge(array(''), // MySQL sets 0 to be the empty string
|
||||||
|
explode("','", strtoupper(preg_replace("/(enum)\('(.+?)'\)/","\\2", $types)))
|
||||||
|
));
|
||||||
|
} //end getEnumValues
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: queryInit
|
||||||
|
* - Initializes the query fields
|
||||||
|
*/
|
||||||
|
function queryInit(&$query, $link = true) {
|
||||||
|
if (!isset($query))
|
||||||
|
$query = array();
|
||||||
|
if (!isset($query['conditions']))
|
||||||
|
$query['conditions'] = array();
|
||||||
|
if (!isset($query['group']))
|
||||||
|
$query['group'] = null;
|
||||||
|
if (!isset($query['fields']))
|
||||||
|
$query['fields'] = null;
|
||||||
|
if ($link && !isset($query['link']))
|
||||||
|
$query['link'] = array();
|
||||||
|
if (!$link && !isset($query['contain']))
|
||||||
|
$query['contain'] = array();
|
||||||
|
|
||||||
|
// In case caller expects query to come back
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: nameToID
|
||||||
|
* - Returns the ID of the named item
|
||||||
|
*/
|
||||||
|
function nameToID($name) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$item = $this->find('first', array
|
||||||
|
('recursive' => -1,
|
||||||
|
'conditions' => compact('name'),
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
if ($item) {
|
||||||
|
$item = current($item);
|
||||||
|
return $item['id'];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: statMerge
|
||||||
|
* - Merges summary data from $b into $a
|
||||||
|
*/
|
||||||
|
|
||||||
|
function statsMerge (&$a, $b) {
|
||||||
|
if (!isset($b))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!isset($a)) {
|
||||||
|
$a = $b;
|
||||||
|
}
|
||||||
|
elseif (!is_array($a) && !is_array($b)) {
|
||||||
|
$a += $b;
|
||||||
|
}
|
||||||
|
elseif (is_array($a) && is_array($b)) {
|
||||||
|
foreach (array_intersect_key($a, $b) AS $k => $v)
|
||||||
|
{
|
||||||
|
if (preg_match("/^sp\./", $k))
|
||||||
|
$a[$k] .= '; ' . $b[$k];
|
||||||
|
else
|
||||||
|
$this->statsMerge($a[$k], $b[$k]);
|
||||||
|
}
|
||||||
|
$a = array_merge($a, array_diff_key($b, $a));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
die ("Can't yet merge array and non-array stats");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (is_array($data)) {
|
||||||
|
foreach ($data as $key => &$value) {
|
||||||
|
$this->recursive_array_replace($find, $replace, $value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($replace))
|
||||||
|
$data = preg_replace($find, $replace, $data);
|
||||||
|
elseif (preg_match($find, $data))
|
||||||
|
$data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeSave() {
|
||||||
|
/* pr(array('class' => $this->name, */
|
||||||
|
/* 'alias' => $this->alias, */
|
||||||
|
/* 'function' => 'AppModel::beforeSave')); */
|
||||||
|
|
||||||
|
// Replace all empty strings with NULL.
|
||||||
|
// If a particular model doesn't like this, they'll have to
|
||||||
|
// override the behavior, or set useNullForEmpty to false.
|
||||||
|
if ($this->useNullForEmpty)
|
||||||
|
$this->recursive_array_replace("/^\s*$/", null, $this->data);
|
||||||
|
|
||||||
|
if ($this->formatDateFields) {
|
||||||
|
$alias = $this->alias;
|
||||||
|
|
||||||
|
foreach ($this->_schema AS $field => $info) {
|
||||||
|
if ($info['type'] == 'date' || $info['type'] == 'timestamp') {
|
||||||
|
if (isset($this->data[$alias][$field])) {
|
||||||
|
/* pr("Fix Date for '$alias'.'$field'; current value = " . */
|
||||||
|
/* "'{$this->data[$alias][$field]}'"); */
|
||||||
|
if ($this->data[$alias][$field] === 'CURRENT_TIMESTAMP')
|
||||||
|
// Seems CakePHP is broken for the default timestamp.
|
||||||
|
// It tries to automagically set the value to CURRENT_TIMESTAMP
|
||||||
|
// which is wholly rejected by MySQL. Just put it back to NULL
|
||||||
|
// and let the SQL engine deal with the defaults... it's not
|
||||||
|
// Cake's place to do this anyway :-/
|
||||||
|
$this->data[$alias][$field] = null;
|
||||||
|
else
|
||||||
|
$this->data[$alias][$field] =
|
||||||
|
$this->dateFormatBeforeSave($this->data[$alias][$field]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: dateFormatBeforeSave
|
||||||
|
* - convert dates to database format
|
||||||
|
*/
|
||||||
|
|
||||||
|
function dateFormatBeforeSave($dateString) {
|
||||||
|
/* $time = ''; */
|
||||||
|
/* if (preg_match('/(\d+(:\d+))/', $dateString, $match)) */
|
||||||
|
/* $time = ' '.$match[1]; */
|
||||||
|
/* $dateString = preg_replace('/(\d+(:\d+))/', '', $dateString); */
|
||||||
|
/* return date('Y-m-d', strtotime($dateString)) . $time; */
|
||||||
|
|
||||||
|
if (preg_match('/:/', $dateString))
|
||||||
|
return date('Y-m-d H:i:s', strtotime($dateString));
|
||||||
|
else
|
||||||
|
return date('Y-m-d', strtotime($dateString));
|
||||||
|
}
|
||||||
|
|
||||||
|
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!
|
||||||
137
site/config/bootstrap.php
Normal file
137
site/config/bootstrap.php
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
/* SVN FILE: $Id: bootstrap.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||||
|
/**
|
||||||
|
* Short description for file.
|
||||||
|
*
|
||||||
|
* Long description for file
|
||||||
|
*
|
||||||
|
* PHP versions 4 and 5
|
||||||
|
*
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||||
|
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @filesource
|
||||||
|
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||||
|
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||||
|
* @package cake
|
||||||
|
* @subpackage cake.app.config
|
||||||
|
* @since CakePHP(tm) v 0.10.8.2117
|
||||||
|
* @version $Revision: 7945 $
|
||||||
|
* @modifiedby $LastChangedBy: gwoo $
|
||||||
|
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* This file is loaded automatically by the app/webroot/index.php file after the core bootstrap.php is loaded
|
||||||
|
* This is an application wide file to load any function that is not used within a class define.
|
||||||
|
* You can also use this to include or require any files in your application.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
function _box($type) {
|
||||||
|
static $box = array('type' => null, 'test' => array());
|
||||||
|
if (!isset($box['type']) && !isset($box['test'][$type])) {
|
||||||
|
$r = Router::requestRoute();
|
||||||
|
/* if (!preg_match("/gridData/", $_SERVER['REQUEST_URI'])) { */
|
||||||
|
/* print("<PRE>Route:\n");print_r($r);print("\n</PRE>\n"); */
|
||||||
|
/* } */
|
||||||
|
$box['test'][$type] = !empty($r[3]["${type}_route"]);
|
||||||
|
if ($box['test'][$type])
|
||||||
|
$box['type'] = $type;
|
||||||
|
}
|
||||||
|
return $box['type'] == $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sandbox() { return _box('sand'); }
|
||||||
|
function devbox() { return _box('dev'); }
|
||||||
|
|
||||||
|
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)
|
||||||
|
*
|
||||||
|
* $modelPaths = array('full path to models', 'second full path to models', 'etc...');
|
||||||
|
* $viewPaths = array('this path to views', 'second full path to views', 'etc...');
|
||||||
|
* $controllerPaths = array('this path to controllers', 'second full path to controllers', 'etc...');
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
//EOF
|
||||||
|
?>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
* In production mode, flash messages redirect after a time interval.
|
* In production mode, flash messages redirect after a time interval.
|
||||||
* In development mode, you need to click the flash message to continue.
|
* In development mode, you need to click the flash message to continue.
|
||||||
*/
|
*/
|
||||||
Configure::write('debug', 2);
|
Configure::write('debug', 0);
|
||||||
/**
|
/**
|
||||||
* Application wide charset encoding
|
* Application wide charset encoding
|
||||||
*/
|
*/
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
/**
|
/**
|
||||||
* The name of CakePHP's session cookie.
|
* The name of CakePHP's session cookie.
|
||||||
*/
|
*/
|
||||||
Configure::write('Session.cookie', 'CAKEPHP');
|
Configure::write('Session.cookie', 'PMGR');
|
||||||
/**
|
/**
|
||||||
* Session time out time (in seconds).
|
* Session time out time (in seconds).
|
||||||
* Actual value depends on 'Security.level' setting.
|
* Actual value depends on 'Security.level' setting.
|
||||||
@@ -10,5 +10,12 @@ class DATABASE_CONFIG {
|
|||||||
'database' => 'property_manager',
|
'database' => 'property_manager',
|
||||||
'prefix' => 'pmgr_',
|
'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('.*[nrlm]ese', '.*deer', '.*fish', '.*measles', '.*ois', '.*pox');
|
||||||
*/
|
*/
|
||||||
$uninflectedPlural = array();
|
$uninflectedPlural = array('.*cash');
|
||||||
/**
|
/**
|
||||||
* This is a key => value array of plural irregular words.
|
* This is a key => value array of plural irregular words.
|
||||||
* If key matches then the value is returned.
|
* If key matches then the value is returned.
|
||||||
75
site/config/routes.php
Normal file
75
site/config/routes.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
/* SVN FILE: $Id: routes.php 7945 2008-12-19 02:16:01Z gwoo $ */
|
||||||
|
/**
|
||||||
|
* Short description for file.
|
||||||
|
*
|
||||||
|
* In this file, you set up routes to your controllers and their actions.
|
||||||
|
* Routes are very important mechanism that allows you to freely connect
|
||||||
|
* different urls to chosen controllers and their actions (functions).
|
||||||
|
*
|
||||||
|
* PHP versions 4 and 5
|
||||||
|
*
|
||||||
|
* CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
|
||||||
|
* Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @filesource
|
||||||
|
* @copyright Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
|
||||||
|
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
|
||||||
|
* @package cake
|
||||||
|
* @subpackage cake.app.config
|
||||||
|
* @since CakePHP(tm) v 0.2.9
|
||||||
|
* @version $Revision: 7945 $
|
||||||
|
* @modifiedby $LastChangedBy: gwoo $
|
||||||
|
* @lastmodified $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
$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('/', $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));
|
||||||
|
/* Unfortunately, for some reason we need an extra route to solve
|
||||||
|
* a bug with form generation. When $this->data is set by the
|
||||||
|
* controller, and a URL is generated by the FormHelper, this
|
||||||
|
* route is required to ensure the form action is correct. An
|
||||||
|
* example of a broken page is for /customers/edit/XX. It appears
|
||||||
|
* the page location uses the route above, it's only URL generation
|
||||||
|
* that seems to be broken.
|
||||||
|
*/
|
||||||
|
Router::connect('/sand/:controller/:action/:id/*',
|
||||||
|
array('sand_route' => true,'action' => null, 'id'=>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));
|
||||||
|
/* Unfortunately, for some reason we need an extra route to solve
|
||||||
|
* a bug with form generation. When $this->data is set by the
|
||||||
|
* controller, and a URL is generated by the FormHelper, this
|
||||||
|
* route is required to ensure the form action is correct. An
|
||||||
|
* example of a broken page is for /customers/edit/XX. It appears
|
||||||
|
* the page location uses the route above, it's only URL generation
|
||||||
|
* that seems to be broken.
|
||||||
|
*/
|
||||||
|
Router::connect('/dev/:controller/:action/:id/*',
|
||||||
|
array('dev_route' => true,'action' => null, 'id'=>null));
|
||||||
|
|
||||||
|
?>
|
||||||
210
site/controllers/accounts_controller.php
Normal file
210
site/controllers/accounts_controller.php
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class AccountsController extends AppController {
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* override: addGridViewSideMenuLinks
|
||||||
|
* - Adds grid view navigation side menu 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / asset / liability / equity / income / expense / all
|
||||||
|
* - Generate a chart of accounts
|
||||||
|
*/
|
||||||
|
|
||||||
|
function index() { $this->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: gridData
|
||||||
|
* - With the application controller handling the gridData action,
|
||||||
|
* these virtual functions ensure that the correct data is passed
|
||||||
|
* to jqGrid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function gridDataSetup(&$params) {
|
||||||
|
parent::gridDataSetup($params);
|
||||||
|
if (!isset($params['action']))
|
||||||
|
$params['action'] = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
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 gridDataTables(&$params, &$model) {
|
||||||
|
return array
|
||||||
|
('link' =>
|
||||||
|
array(// Models
|
||||||
|
'CurrentLedger' => array
|
||||||
|
(// Models
|
||||||
|
'LedgerEntry'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataFields(&$params, &$model) {
|
||||||
|
$fields = parent::gridDataFields($params, $model);
|
||||||
|
return array_merge($fields,
|
||||||
|
$this->Account->Ledger->LedgerEntry->debitCreditFields(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||||
|
$links['Account'] = array('name');
|
||||||
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: newledger
|
||||||
|
* - Close the current account ledger and create a new one,
|
||||||
|
* carrying forward any balance if necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function newledger($id = null) {
|
||||||
|
$result = $this->Account->closeCurrentLedgers($id);
|
||||||
|
|
||||||
|
if ($result['error']) {
|
||||||
|
pr(compact('result'));
|
||||||
|
die("Unable to create new ledger.");
|
||||||
|
$this->Session->setFlash(__('Unable to create new Ledger.', true));
|
||||||
|
}
|
||||||
|
if ($id)
|
||||||
|
$this->redirect(array('action'=>'view', $id));
|
||||||
|
else
|
||||||
|
$this->redirect(array('action'=>'index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: collected
|
||||||
|
* - Displays the items actually collected for the period
|
||||||
|
* e.g. How much was collected in rent from 4/1/09 - 5/1/09
|
||||||
|
*/
|
||||||
|
function collected($id = null) {
|
||||||
|
if (!$id) {
|
||||||
|
$this->Session->setFlash(__('Invalid Item.', true));
|
||||||
|
$this->redirect(array('action'=>'index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->Account->recursive = -1;
|
||||||
|
$account = $this->Account->read(null, $id);
|
||||||
|
$account = $account['Account'];
|
||||||
|
|
||||||
|
$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: view
|
||||||
|
* - Displays information about a specific account
|
||||||
|
*/
|
||||||
|
|
||||||
|
function view($id = null) {
|
||||||
|
$account = $this->Account->find
|
||||||
|
('first',
|
||||||
|
array('contain' =>
|
||||||
|
array(// Models
|
||||||
|
'CurrentLedger' =>
|
||||||
|
array('fields' => array('id', 'sequence', 'name')),
|
||||||
|
|
||||||
|
'Ledger' =>
|
||||||
|
array('CloseTransaction' => array
|
||||||
|
('order' => array('CloseTransaction.stamp' => 'DESC'))),
|
||||||
|
),
|
||||||
|
'conditions' => array(array('Account.id' => $id),
|
||||||
|
array('Account.level >=' =>
|
||||||
|
$this->Permission->level('controller.accounts')),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
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->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'];
|
||||||
|
$this->set(compact('account', 'title', 'stats'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,19 +2,7 @@
|
|||||||
|
|
||||||
class ContactsController extends AppController {
|
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 index() { $this->all(); }
|
||||||
function all() { $this->jqGridView('All Contacts', 'all'); }
|
function all() { $this->gridView('All Contacts', 'all'); }
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
**************************************************************************
|
**************************************************************************
|
||||||
**************************************************************************
|
**************************************************************************
|
||||||
* virtuals: jqGridData
|
* virtuals: gridData
|
||||||
* - With the application controller handling the jqGridData action,
|
* - With the application controller handling the gridData action,
|
||||||
* these virtual functions ensure that the correct data is passed
|
* these virtual functions ensure that the correct data is passed
|
||||||
* to jqGrid.
|
* to jqGrid.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function jqGridDataOrder(&$params, &$model, $index, $direction) {
|
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||||
$order = parent::jqGridDataOrder($params, $model, $index, $direction);
|
$config = parent::gridDataFilterTablesConfig($params, $model, $table);
|
||||||
if ($index === 'Contact.last_name') {
|
|
||||||
$order[] = 'Contact.first_name ' . $direction;
|
// Special case for Customer; We need the Contact/Customer relationship
|
||||||
}
|
if ($table == 'Customer')
|
||||||
if ($index === 'Contact.first_name') {
|
$config = array('fields' => array('ContactsCustomer.type',
|
||||||
$order[] = 'Contact.last_name ' . $direction;
|
'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[] = 'Contact.last_name ' . $direction;
|
||||||
|
$order[] = 'Contact.first_name ' . $direction;
|
||||||
|
|
||||||
return $order;
|
return $order;
|
||||||
}
|
}
|
||||||
|
|
||||||
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
|
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||||
$links['Contact'] = array('id');
|
$links['Contact'] = array('display_name');
|
||||||
return parent::jqGridRecordLinks($params, $model, $records, $links);
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -78,13 +81,9 @@ class ContactsController extends AppController {
|
|||||||
));
|
));
|
||||||
|
|
||||||
// Set up dynamic menu items
|
// Set up dynamic menu items
|
||||||
$this->sidemenu_links[] =
|
$this->addSideMenuLink('Edit',
|
||||||
array('name' => 'Operations', 'header' => true);
|
array('action' => 'edit', $id), null,
|
||||||
|
'ACTION');
|
||||||
$this->sidemenu_links[] =
|
|
||||||
array('name' => 'Edit',
|
|
||||||
'url' => array('action' => 'edit',
|
|
||||||
$id));
|
|
||||||
|
|
||||||
// Prepare to render.
|
// Prepare to render.
|
||||||
$title = 'Contact: ' . $contact['Contact']['display_name'];
|
$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
|
// Now that the work is done, let the user view the updated contact
|
||||||
$this->redirect(array('action'=>'view', $this->data['Contact']['id']));
|
$this->redirect(array('action'=>'view', $this->data['Contact']['id']));
|
||||||
//$this->render('/empty');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($id) {
|
if ($id) {
|
||||||
584
site/controllers/customers_controller.php
Normal file
584
site/controllers/customers_controller.php
Normal file
@@ -0,0 +1,584 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class CustomersController extends AppController {
|
||||||
|
|
||||||
|
var $components = array('RequestHandler');
|
||||||
|
|
||||||
|
// DEBUG FUNCTION ONLY!
|
||||||
|
// Call without id to update ALL customers
|
||||||
|
function force_update($id = null) {
|
||||||
|
$this->Customer->update($id);
|
||||||
|
$this->redirect(array('action'=>'index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* override: addGridViewSideMenuLinks
|
||||||
|
* - Adds grid view navigation side menu 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); */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / current / past / all
|
||||||
|
* - Creates a list of customers
|
||||||
|
*/
|
||||||
|
|
||||||
|
function index() { $this->current(); }
|
||||||
|
function current() { $this->gridView('Current Tenants', 'current'); }
|
||||||
|
function past() { $this->gridView('Past Tenants'); }
|
||||||
|
function all() { $this->gridView('All Customers'); }
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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(// Models
|
||||||
|
'PrimaryContact',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 gridDataFields(&$params, &$model) {
|
||||||
|
$fields = parent::gridDataFields($params, $model);
|
||||||
|
return array_merge($fields,
|
||||||
|
$this->Customer->StatementEntry->chargeDisbursementFields(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataConditions(&$params, &$model) {
|
||||||
|
$conditions = parent::gridDataConditions($params, $model);
|
||||||
|
|
||||||
|
if ($params['action'] === 'current') {
|
||||||
|
$conditions[] = array('Customer.current_lease_count >' => 0);
|
||||||
|
}
|
||||||
|
elseif ($params['action'] === 'past') {
|
||||||
|
$conditions[] = array('Customer.current_lease_count' => 0);
|
||||||
|
$conditions[] = array('Customer.past_lease_count >' => 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||||
|
$config = parent::gridDataFilterTablesConfig($params, $model, $table);
|
||||||
|
|
||||||
|
// 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 gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||||
|
$links['Customer'] = array('name');
|
||||||
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: move_in
|
||||||
|
* - Sets up the move-in page for the given customer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function move_in($id = null, $unit_id = null) {
|
||||||
|
$customer = array();
|
||||||
|
$unit = array();
|
||||||
|
|
||||||
|
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';
|
||||||
|
$this->set(compact('title'));
|
||||||
|
$this->render('/leases/move');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: move_out
|
||||||
|
* - prepare to move a customer out of one of their units
|
||||||
|
*/
|
||||||
|
|
||||||
|
function move_out($id) {
|
||||||
|
|
||||||
|
$customer = $this->Customer->find
|
||||||
|
('first', array
|
||||||
|
('contain' => array
|
||||||
|
(// Models
|
||||||
|
'Lease' =>
|
||||||
|
array('conditions' => array('Lease.moveout_date' => null),
|
||||||
|
// Models
|
||||||
|
'Unit' =>
|
||||||
|
array('order' => array('sort_order'),
|
||||||
|
'fields' => array('id', 'name'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
'conditions' => array('Customer.id' => $id),
|
||||||
|
));
|
||||||
|
$this->set('customer', $lease['Customer']);
|
||||||
|
$this->set('unit', array());
|
||||||
|
|
||||||
|
$redirect = array('controller' => 'customers',
|
||||||
|
'action' => 'view',
|
||||||
|
$id);
|
||||||
|
|
||||||
|
$title = $customer['Customer']['name'] . ': Prepare Move-Out';
|
||||||
|
$this->set(compact('title', 'customer', 'redirect'));
|
||||||
|
$this->render('/leases/move');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: view
|
||||||
|
* - Displays information about a specific customer
|
||||||
|
*/
|
||||||
|
|
||||||
|
function view($id = null) {
|
||||||
|
if (!$id) {
|
||||||
|
$this->Session->setFlash(__('Invalid Item.', true));
|
||||||
|
$this->redirect(array('action'=>'index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
));
|
||||||
|
//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; $moveout_lease_id = null;
|
||||||
|
$show_payment = false; $payment_lease_id = null;
|
||||||
|
foreach ($customer['Lease'] AS $lease) {
|
||||||
|
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 ($show_moveout)
|
||||||
|
$moveout_lease_id = null;
|
||||||
|
else
|
||||||
|
$moveout_lease_id = $lease['id'];
|
||||||
|
$show_moveout = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up dynamic menu items
|
||||||
|
|
||||||
|
if ($show_payment || $outstanding_balance > 0)
|
||||||
|
$this->addSideMenuLink('New Receipt',
|
||||||
|
array('action' => 'receipt', $id), null,
|
||||||
|
'ACTION');
|
||||||
|
|
||||||
|
if ($show_payment) {
|
||||||
|
/* $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');
|
||||||
|
|
||||||
|
if ($this->admin())
|
||||||
|
$this->addSideMenuLink('Merge',
|
||||||
|
array('action' => 'merge', $id), null,
|
||||||
|
'ACTION');
|
||||||
|
|
||||||
|
// Prepare to render.
|
||||||
|
$title = 'Customer: ' . $customer['Customer']['name'];
|
||||||
|
$this->set(compact('customer', 'title',
|
||||||
|
'outstanding_balance',
|
||||||
|
'outstanding_deposit'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: edit
|
||||||
|
* - Edit customer information
|
||||||
|
*/
|
||||||
|
|
||||||
|
function edit($id = null) {
|
||||||
|
if (isset($this->data)) {
|
||||||
|
// Check to see if the operation was cancelled.
|
||||||
|
if (isset($this->params['form']['cancel'])) {
|
||||||
|
if (isset($this->data['Customer']['id']))
|
||||||
|
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
|
||||||
|
|
||||||
|
$this->redirect(array('action'=>'index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through each customer and strip the bogus ID if new
|
||||||
|
foreach ($this->data['Contact'] AS &$contact) {
|
||||||
|
if (isset($contact['source']) && $contact['source'] === 'new')
|
||||||
|
unset($contact['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the customer and all associated data
|
||||||
|
if (!$this->Customer->saveCustomer($this->data['Customer']['id'],
|
||||||
|
$this->data,
|
||||||
|
$this->data['Customer']['primary_contact_entry'])) {
|
||||||
|
$this->Session->setFlash("CUSTOMER SAVE FAILED", true);
|
||||||
|
pr("CUSTOMER SAVE FAILED");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If existing customer, then view it.
|
||||||
|
if ($this->data['Customer']['id'])
|
||||||
|
$this->redirect(array('action'=>'view', $this->Customer->id));
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// 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 {
|
||||||
|
$title = "Enter New Customer";
|
||||||
|
$this->data = array('Contact' => array(), 'PrimaryContact' => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$contact_types = array_flip($this->Customer->ContactsCustomer->getEnumValues('type'));
|
||||||
|
unset($contact_types[0]);
|
||||||
|
$contact_types = array_combine($contact_types, $contact_types);
|
||||||
|
$this->set(compact('contact_types'));
|
||||||
|
|
||||||
|
$contacts = $this->Customer->Contact->contactList();
|
||||||
|
$this->set(compact('contacts'));
|
||||||
|
|
||||||
|
// Prepare to render.
|
||||||
|
//pr($this->data);
|
||||||
|
$this->set(compact('title'));
|
||||||
|
$this->render('edit');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: add
|
||||||
|
* - Add a new customer
|
||||||
|
*/
|
||||||
|
|
||||||
|
function add($unit_id = null) {
|
||||||
|
$this->set('movein', array('Unit' => array('id' => $unit_id)));
|
||||||
|
$this->edit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: merge
|
||||||
|
* - Merges two customers
|
||||||
|
*/
|
||||||
|
|
||||||
|
function merge($id = null) {
|
||||||
|
if ($id) {
|
||||||
|
$this->Customer->recursive = -1;
|
||||||
|
$customer = $this->Customer->read(null, $id);
|
||||||
|
$customer = $customer['Customer'];
|
||||||
|
if (empty($customer))
|
||||||
|
$this->INTERNAL_ERROR("Customer $id does not exist");
|
||||||
|
$this->set('dst_customer', $customer);
|
||||||
|
$this->set('dst_name', $customer['name']);
|
||||||
|
$this->set('dst_id', $id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->INTERNAL_ERROR("Merge called with invalid customer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeFinal() {
|
||||||
|
if (!$this->RequestHandler->isPost()) {
|
||||||
|
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$post = $this->params['form'];
|
||||||
|
$this->Customer->merge($post['dst-id'], $post['src-id'],
|
||||||
|
unserialize($post['contact-ids']));
|
||||||
|
$this->redirect(array('action'=>'view', $post['dst-id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: receipt
|
||||||
|
* - Sets up the receipt entry page for the given customer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function receipt($id = null) {
|
||||||
|
if (isset($id)) {
|
||||||
|
$this->Customer->recursive = -1;
|
||||||
|
$customer = $this->Customer->read(null, $id);
|
||||||
|
$customer = $customer['Customer'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$customer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$TT = new TenderType();
|
||||||
|
$payment_types = $TT->paymentTypes();
|
||||||
|
$default_type = $TT->defaultPaymentType();
|
||||||
|
$this->set(compact('payment_types', 'default_type'));
|
||||||
|
|
||||||
|
$title = ($customer['name'] . ': Receipt Entry');
|
||||||
|
$this->set(compact('customer', 'title'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: refund
|
||||||
|
* - Refunds customer charges
|
||||||
|
*/
|
||||||
|
|
||||||
|
function refund($id) {
|
||||||
|
$customer = $this->Customer->find
|
||||||
|
('first', array
|
||||||
|
('contain' => false,
|
||||||
|
'conditions' => array(array('Customer.id' => $id),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
if (empty($customer)) {
|
||||||
|
$this->redirect(array('action'=>'view', $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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: bad_debt
|
||||||
|
* - Sets up the write-off entry page, so that the
|
||||||
|
* user can write off remaining charges of a customer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function bad_debt($id) {
|
||||||
|
$this->Customer->id = $id;
|
||||||
|
$customer = $this->Customer->find
|
||||||
|
('first', array
|
||||||
|
('contain' => false,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Make sure we have a valid customer to write off
|
||||||
|
if (empty($customer))
|
||||||
|
$this->redirect(array('action' => 'index'));
|
||||||
|
|
||||||
|
// Get the customer balance
|
||||||
|
$balance = $this->Customer->balance($id);
|
||||||
|
|
||||||
|
// 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'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
590
site/controllers/leases_controller.php
Normal file
590
site/controllers/leases_controller.php
Normal file
@@ -0,0 +1,590 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class LeasesController extends AppController {
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* override: addGridViewSideMenuLinks
|
||||||
|
* - Adds grid view navigation side menu 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / active / closed / all
|
||||||
|
* - Generate a listing of leases
|
||||||
|
*/
|
||||||
|
|
||||||
|
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: gridData
|
||||||
|
* - With the application controller handling the gridData action,
|
||||||
|
* these virtual functions ensure that the correct data is passed
|
||||||
|
* to jqGrid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function gridDataSetup(&$params) {
|
||||||
|
parent::gridDataSetup($params);
|
||||||
|
if (!isset($params['action']))
|
||||||
|
$params['action'] = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataCountTables(&$params, &$model) {
|
||||||
|
return array
|
||||||
|
('link' => array('Unit' => array('fields' => array('id', 'name')),
|
||||||
|
'Customer' => array('fields' => array('id', 'name'))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataTables(&$params, &$model) {
|
||||||
|
$link = $this->gridDataCountTables($params, $model);
|
||||||
|
$link['link']['StatementEntry'] = array('fields' => array());
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataFields(&$params, &$model) {
|
||||||
|
$fields = parent::gridDataFields($params, $model);
|
||||||
|
$fields[] = ("IF(" . $this->Lease->conditionDelinquent() . "," .
|
||||||
|
" 'DELINQUENT', 'CURRENT') AS 'status'");
|
||||||
|
return array_merge($fields,
|
||||||
|
$this->Lease->StatementEntry->chargeDisbursementFields(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataConditions(&$params, &$model) {
|
||||||
|
$conditions = parent::gridDataConditions($params, $model);
|
||||||
|
|
||||||
|
if ($params['action'] === 'active') {
|
||||||
|
$conditions[] = 'Lease.close_date IS NULL';
|
||||||
|
}
|
||||||
|
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 gridDataOrder(&$params, &$model, $index, $direction) {
|
||||||
|
// Do not sort by number, which is type varchar and
|
||||||
|
// sorts on an ascii basis. Sort by ID instead.
|
||||||
|
if ($index === 'Lease.number')
|
||||||
|
$index = 'Lease.id';
|
||||||
|
|
||||||
|
// Instead of sorting by name, sort by defined order
|
||||||
|
if ($index === 'Unit.name')
|
||||||
|
$index = 'Unit.sort_order';
|
||||||
|
|
||||||
|
$order = array();
|
||||||
|
$order[] = parent::gridDataOrder($params, $model, $index, $direction);
|
||||||
|
|
||||||
|
// If sorting by anything other than id/number
|
||||||
|
// add sorting by id as a secondary condition.
|
||||||
|
if ($index !== 'Lease.id' && $index !== 'Lease.number')
|
||||||
|
$order[] = parent::gridDataOrder($params, $model,
|
||||||
|
'Lease.id', $direction);
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function gridDataPostProcess(&$params, &$model, &$records) { */
|
||||||
|
/* foreach ($records AS &$record) { */
|
||||||
|
/* $record['Lease']['through_date'] */
|
||||||
|
/* = $this->Lease->rentChargeThrough($record['Lease']['id']); */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
/* parent::gridDataPostProcess($params, $model, $records); */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||||
|
$links['Lease'] = array('number');
|
||||||
|
$links['Unit'] = array('name');
|
||||||
|
$links['Customer'] = array('name');
|
||||||
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: move_in
|
||||||
|
* - execute a move in on a new lease
|
||||||
|
*/
|
||||||
|
|
||||||
|
function move_in() {
|
||||||
|
if (!$this->data)
|
||||||
|
die("Should have some data");
|
||||||
|
|
||||||
|
// Handle the move in based on the data given
|
||||||
|
//pr(array('Move-in data', $this->data));
|
||||||
|
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'],
|
||||||
|
$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, 'move-in'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: move_out
|
||||||
|
* - prepare or execute a move out on a specific lease
|
||||||
|
*/
|
||||||
|
|
||||||
|
function move_out($id = null) {
|
||||||
|
if ($this->data) {
|
||||||
|
// Handle the move out based on the data given
|
||||||
|
$this->Lease->moveOut($this->data['Lease']['id'],
|
||||||
|
'VACANT',
|
||||||
|
$this->data['Lease']['moveout_date']
|
||||||
|
);
|
||||||
|
|
||||||
|
$lease = $this->Lease->find
|
||||||
|
('first', array
|
||||||
|
('contain' => array('Customer.id'),
|
||||||
|
'conditions' => array(array('Lease.id' => $this->data['Lease']['id'])),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->redirect(array('controller' => 'customers',
|
||||||
|
'action' => 'view',
|
||||||
|
$lease['Customer']['id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($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),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
$this->set('customer', $lease['Customer']);
|
||||||
|
$this->set('unit', $lease['Unit']);
|
||||||
|
$this->set('lease', $lease['Lease']);
|
||||||
|
|
||||||
|
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||||
|
$lease['Unit']['name'] . ': ' .
|
||||||
|
$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: refund
|
||||||
|
* - Provides lease customer with a refund
|
||||||
|
*/
|
||||||
|
|
||||||
|
function refund($id) {
|
||||||
|
$lease = $this->Lease->find
|
||||||
|
('first', array
|
||||||
|
('contain' => array
|
||||||
|
(// Models
|
||||||
|
'Unit' => array('fields' => array('id', 'name')),
|
||||||
|
'Customer' => array('fields' => array('id', 'name')),
|
||||||
|
),
|
||||||
|
|
||||||
|
'conditions' => array(array('Lease.id' => $id),
|
||||||
|
// Make sure lease is not closed...
|
||||||
|
array('Lease.close_date' => null),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
if (empty($lease)) {
|
||||||
|
$this->redirect(array('action'=>'view', $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the lease balance, bailing if the customer owes money
|
||||||
|
$balance = $this->Lease->balance($id);
|
||||||
|
if ($balance >= 0) {
|
||||||
|
$this->redirect(array('action'=>'view', $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The refund will be for a positive amount
|
||||||
|
$balance *= -1;
|
||||||
|
|
||||||
|
// Get the accounts capable of paying the refund
|
||||||
|
$refundAccounts = $this->Lease->StatementEntry->Account->refundAccounts();
|
||||||
|
$defaultAccount = current($refundAccounts);
|
||||||
|
$this->set(compact('refundAccounts', 'defaultAccount'));
|
||||||
|
|
||||||
|
// Prepare to render
|
||||||
|
$title = ('Lease #' . $lease['Lease']['number'] . ': ' .
|
||||||
|
$lease['Unit']['name'] . ': ' .
|
||||||
|
$lease['Customer']['name'] . ': Refund');
|
||||||
|
$this->set(compact('title', 'lease', 'balance'));
|
||||||
|
$this->render('/transactions/refund');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: bad_debt
|
||||||
|
* - Sets up the write-off entry page, so that the
|
||||||
|
* user can write off remaining charges on a lease.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function bad_debt($id) {
|
||||||
|
$this->Lease->id = $id;
|
||||||
|
$lease = $this->Lease->find
|
||||||
|
('first', array
|
||||||
|
('contain' => array
|
||||||
|
(// Models
|
||||||
|
'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
|
||||||
|
$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', 'lease', 'balance'));
|
||||||
|
$this->render('/transactions/bad_debt');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: close
|
||||||
|
* - 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: invoice
|
||||||
|
* - Sets up the invoice entry page for the given customer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function invoice($id = null, $type = null) {
|
||||||
|
|
||||||
|
$lease = $this->Lease->find
|
||||||
|
('first', array
|
||||||
|
('contain' => array
|
||||||
|
(// Models
|
||||||
|
'Unit' =>
|
||||||
|
array('order' => array('sort_order'),
|
||||||
|
'fields' => array('id', 'name'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'Customer' =>
|
||||||
|
array('fields' => array('id', 'name'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
'conditions' => array(array('Lease.id' => $id),
|
||||||
|
array('Lease.close_date' => null),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
$A = new Account();
|
||||||
|
$charge_accounts = $A->invoiceAccounts();
|
||||||
|
$default_account = $A->rentAccountID();
|
||||||
|
$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_late = 10;
|
||||||
|
$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'] . ': ' .
|
||||||
|
$lease['Customer']['name'] . ': Charge Entry');
|
||||||
|
$this->set(compact('title', 'lease', 'charge'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: overview
|
||||||
|
* - Displays lease up information
|
||||||
|
*/
|
||||||
|
function overview($months = 12) {
|
||||||
|
|
||||||
|
$overview = array('months' => array());
|
||||||
|
|
||||||
|
for ($month = 0; $month < $months; ++$month) {
|
||||||
|
//for ($month = 12; $month >= 0; --$month) {
|
||||||
|
$this_month = "(DATE(NOW() - INTERVAL $month MONTH - INTERVAL DAY(NOW())-1 DAY))";
|
||||||
|
$next_month = "($this_month + INTERVAL 1 MONTH)";
|
||||||
|
|
||||||
|
$row = $this->Lease->find
|
||||||
|
('first', array('link' => array(),
|
||||||
|
'fields' => array("MONTHNAME($this_month) AS month",
|
||||||
|
"YEAR($this_month) AS year"),
|
||||||
|
));
|
||||||
|
|
||||||
|
$mname = $row[0]['month'] .', '. $row[0]['year'];
|
||||||
|
$overview['months'][$mname] = array('name' => $mname);
|
||||||
|
|
||||||
|
foreach(array('start' => array('before' => $this_month, 'after' => $this_month),
|
||||||
|
'finish' => array('before' => $next_month, 'after' => $next_month),
|
||||||
|
'peak' => array('before' => $next_month, 'after' => $this_month))
|
||||||
|
AS $type => $parm) {
|
||||||
|
$count = $this->Lease->find
|
||||||
|
('count',
|
||||||
|
array('link' => array(),
|
||||||
|
'conditions' => array("movein_date < {$parm['before']}",
|
||||||
|
"(moveout_date IS NULL OR moveout_date >= {$parm['after']})",
|
||||||
|
),
|
||||||
|
));
|
||||||
|
$overview['months'][$mname][$type] = $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(array('movein', 'moveout') AS $mvinout) {
|
||||||
|
$count = $this->Lease->find
|
||||||
|
('count',
|
||||||
|
array('link' => array(),
|
||||||
|
'conditions' => array("{$mvinout}_date < $next_month",
|
||||||
|
"{$mvinout}_date >= $this_month")
|
||||||
|
));
|
||||||
|
$overview['months'][$mname][$mvinout] = $count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the Reports menu section
|
||||||
|
$this->sideMenuAreaActivate('REPORT');
|
||||||
|
|
||||||
|
// Prepare to render.
|
||||||
|
$this->set('title', 'Lease Up Report');
|
||||||
|
$this->set(compact('overview'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: view
|
||||||
|
* - Displays information about a specific lease
|
||||||
|
*/
|
||||||
|
|
||||||
|
function view($id = null) {
|
||||||
|
if (!$id) {
|
||||||
|
$this->Session->setFlash(__('Invalid Item.', true));
|
||||||
|
$this->redirect(array('action'=>'index'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get details about the lease and its ledgers (no ledger entries yet)
|
||||||
|
$lease = $this->Lease->find
|
||||||
|
('first',
|
||||||
|
array('contain' =>
|
||||||
|
array(// Models
|
||||||
|
'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]);
|
||||||
|
|
||||||
|
// 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->addSideMenuLink('Move-Out',
|
||||||
|
array('action' => 'move_out', $id), null,
|
||||||
|
'ACTION');
|
||||||
|
|
||||||
|
if (!isset($lease['Lease']['close_date']))
|
||||||
|
$this->addSideMenuLink('New Invoice',
|
||||||
|
array('action' => 'invoice', $id), null,
|
||||||
|
'ACTION');
|
||||||
|
|
||||||
|
$this->addSideMenuLink('New Receipt',
|
||||||
|
array('controller' => 'customers',
|
||||||
|
'action' => 'receipt',
|
||||||
|
$lease['Customer']['id']), null,
|
||||||
|
'ACTION');
|
||||||
|
|
||||||
|
// 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 ($outstanding_balance < 0) */
|
||||||
|
/* $this->addSideMenuLink('Issue Refund', */
|
||||||
|
/* array('action' => 'refund', $id), null, */
|
||||||
|
/* 'ACTION'); */
|
||||||
|
|
||||||
|
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->addSideMenuLink('Close',
|
||||||
|
array('action' => 'close', $id), null,
|
||||||
|
'ACTION');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare to render
|
||||||
|
$title = 'Lease: #' . $lease['Lease']['id'];
|
||||||
|
$this->set(compact('lease', 'title',
|
||||||
|
'outstanding_deposit',
|
||||||
|
'outstanding_balance'));
|
||||||
|
}
|
||||||
|
}
|
||||||
204
site/controllers/ledger_entries_controller.php
Normal file
204
site/controllers/ledger_entries_controller.php
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class LedgerEntriesController extends AppController {
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / current / past / all
|
||||||
|
* - Creates a list of ledger entries
|
||||||
|
*/
|
||||||
|
|
||||||
|
function index() { $this->gridView('All Ledger Entries'); }
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* virtuals: gridData
|
||||||
|
* - With the application controller handling the gridData action,
|
||||||
|
* these virtual functions ensure that the correct data is passed
|
||||||
|
* to jqGrid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function gridDataTables(&$params, &$model) {
|
||||||
|
$link =
|
||||||
|
array(// Models
|
||||||
|
'Transaction' =>
|
||||||
|
array('fields' => array('id', 'stamp'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'Ledger' =>
|
||||||
|
array('fields' => array('id', 'sequence'),
|
||||||
|
'Account' =>
|
||||||
|
array('fields' => array('id', 'name', 'type'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
'Tender' =>
|
||||||
|
array('fields' => array('id', 'name', 'nsf_transaction_id'),
|
||||||
|
),
|
||||||
|
|
||||||
|
/* 'DebitEntry', */
|
||||||
|
/* 'CreditEntry', */
|
||||||
|
);
|
||||||
|
|
||||||
|
return array('link' => $link);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataFields(&$params, &$model) {
|
||||||
|
$fields = parent::gridDataFields($params, $model);
|
||||||
|
return array_merge($fields,
|
||||||
|
$this->LedgerEntry->debitCreditFields());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Customer needs to be added beneath Transaction
|
||||||
|
if ($table == 'Customer')
|
||||||
|
return 'Transaction';
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataFilterTablesConfig(&$params, &$model, $table) {
|
||||||
|
$config = parent::gridDataFilterTablesConfig($params, $model, $table);
|
||||||
|
|
||||||
|
// Customer is special in that its linked in by Transaction
|
||||||
|
// Therefore, the actual table used for the join is 'Transaction',
|
||||||
|
// not 'Customer', and so we need to specify Customer here.
|
||||||
|
if ($table == 'Customer')
|
||||||
|
$config = array('Customer' => $config);
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataFilterConditionsStatement(&$params, &$model, $table, $key, $value) {
|
||||||
|
//pr(compact('table', 'key', 'value'));
|
||||||
|
if ($table == 'Account' && $value['value_present'] && $value['value'] === '-AR-')
|
||||||
|
$value = $this->LedgerEntry->Ledger->Account->accountReceivableAccountID();
|
||||||
|
return parent::gridDataFilterConditionsStatement($params, $model, $table, $key, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function gridDataOrder(&$params, &$model, $index, $direction) {
|
||||||
|
/* if ($index === 'balance') */
|
||||||
|
/* return ($index .' '. $direction); */
|
||||||
|
$order = parent::gridDataOrder($params, $model, $index, $direction);
|
||||||
|
|
||||||
|
if ($index === 'Transaction.stamp') {
|
||||||
|
$order[] = 'LedgerEntry.id ' . $direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
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['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');
|
||||||
|
}
|
||||||
|
$links['Tender'] = array('name');
|
||||||
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: view
|
||||||
|
* - Displays information about a specific entry
|
||||||
|
*/
|
||||||
|
|
||||||
|
function view($id = null) {
|
||||||
|
$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'));
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
if (empty($entry['DebitEntry']))
|
||||||
|
$entry['MatchingEntry'] = $entry['CreditEntry'][0];
|
||||||
|
else
|
||||||
|
$entry['MatchingEntry'] = $entry['DebitEntry'][0];
|
||||||
|
|
||||||
|
if (empty($entry['DebitDoubleEntry']['id']))
|
||||||
|
$entry['DoubleEntry'] = $entry['CreditDoubleEntry'];
|
||||||
|
else
|
||||||
|
$entry['DoubleEntry'] = $entry['DebitDoubleEntry'];
|
||||||
|
|
||||||
|
// 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 = "Ledger Entry #{$entry['LedgerEntry']['id']}";
|
||||||
|
$this->set(compact('entry', 'title'));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
152
site/controllers/ledgers_controller.php
Normal file
152
site/controllers/ledgers_controller.php
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class LedgersController extends AppController {
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* override: addGridViewSideMenuLinks
|
||||||
|
* - Adds grid view navigation side menu 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / current / closed / all
|
||||||
|
* - Generate a list of ledgers
|
||||||
|
*/
|
||||||
|
|
||||||
|
function index() { $this->all(); }
|
||||||
|
function current() { $this->gridView('Current Ledgers'); }
|
||||||
|
function closed() { $this->gridView('Closed Ledgers'); }
|
||||||
|
function all() { $this->gridView('All Ledgers', 'all'); }
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* virtuals: gridData
|
||||||
|
* - With the application controller handling the gridData action,
|
||||||
|
* these virtual functions ensure that the correct data is passed
|
||||||
|
* to jqGrid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function gridDataSetup(&$params) {
|
||||||
|
parent::gridDataSetup($params);
|
||||||
|
if (!isset($params['action']))
|
||||||
|
$params['action'] = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataCountTables(&$params, &$model) {
|
||||||
|
return array
|
||||||
|
('link' =>
|
||||||
|
array(// Models
|
||||||
|
'Account',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataTables(&$params, &$model) {
|
||||||
|
$tables = $this->gridDataCountTables($params, $model);
|
||||||
|
$tables['link'][] = 'LedgerEntry';
|
||||||
|
$tables['link'][] = 'CloseTransaction';
|
||||||
|
return $tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
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('Ledger.close_transaction_id' => null);
|
||||||
|
}
|
||||||
|
elseif ($params['action'] === 'closed') {
|
||||||
|
$conditions[] = array('Ledger.close_transaction_id !=' => null);
|
||||||
|
}
|
||||||
|
|
||||||
|
$conditions[] = array('Account.level >=' =>
|
||||||
|
$this->Permission->level('controller.accounts'));
|
||||||
|
|
||||||
|
return $conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
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[] = 'Account.name ' . $direction;
|
||||||
|
$order[] = 'Ledger.sequence ' . $direction;
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: view
|
||||||
|
* - Displays information about a specific ledger
|
||||||
|
*/
|
||||||
|
|
||||||
|
function view($id = null) {
|
||||||
|
$ledger = $this->Ledger->find
|
||||||
|
('first',
|
||||||
|
array('contain' =>
|
||||||
|
array(// Models
|
||||||
|
'Account',
|
||||||
|
),
|
||||||
|
'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);
|
||||||
|
|
||||||
|
// OK, set our view variables and render!
|
||||||
|
$title = 'Ledger: #' . $ledger['Account']['id'] .'-'. $ledger['Ledger']['sequence'];
|
||||||
|
$this->set(compact('ledger', 'title', 'stats'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,28 +15,28 @@ class MapsController extends AppController {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function index() { $this->all(); }
|
function index() { $this->all(); }
|
||||||
function all() { $this->jqGridView('All Maps', 'all'); }
|
function all() { $this->gridView('All Maps', 'all'); }
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
**************************************************************************
|
**************************************************************************
|
||||||
**************************************************************************
|
**************************************************************************
|
||||||
* virtuals: jqGridData
|
* virtuals: gridData
|
||||||
* - With the application controller handling the jqGridData action,
|
* - With the application controller handling the gridData action,
|
||||||
* these virtual functions ensure that the correct data is passed
|
* these virtual functions ensure that the correct data is passed
|
||||||
* to jqGrid.
|
* to jqGrid.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function jqGridDataTables(&$params, &$model) {
|
function gridDataTables(&$params, &$model) {
|
||||||
return array
|
return array
|
||||||
('link' => array('SiteArea' => array('fields' => array('SiteArea.id', 'SiteArea.name')),
|
('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');
|
$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->Session->setFlash(__('Invalid Item.', true));
|
||||||
$this->redirect(array('action'=>'index'));
|
$this->redirect(array('action'=>'index'));
|
||||||
}
|
}
|
||||||
|
$this->sideMenuEnable('SITE', $this->op_area);
|
||||||
$this->set('info', $this->mapInfo($id, $requested_width));
|
$this->set('info', $this->mapInfo($id, $requested_width));
|
||||||
$this->set('title', "Site Map");
|
$this->set('title', "Site Map");
|
||||||
}
|
}
|
||||||
@@ -85,11 +86,35 @@ class MapsController extends AppController {
|
|||||||
'units' => array());
|
'units' => array());
|
||||||
|
|
||||||
// Find all of the map/unit information from this SiteArea
|
// Find all of the map/unit information from this SiteArea
|
||||||
$this->Map->recursive = 2;
|
$map = $this->Map->find('first', array('contain' => false,
|
||||||
$this->Map->SiteArea->unbindModel(array('hasOne' => array('Map')));
|
'conditions' => array('id' => $id)));
|
||||||
$map = $this->Map->read(null, $id);
|
|
||||||
//pr($map);
|
$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
|
* The preference would be to leave all things "screen" related
|
||||||
* to reside in the view. However, two separate views need this
|
* to reside in the view. However, two separate views need this
|
||||||
@@ -113,7 +138,7 @@ class MapsController extends AppController {
|
|||||||
$info['depth'] = $bottom * $screen_adjustment_factor;
|
$info['depth'] = $bottom * $screen_adjustment_factor;
|
||||||
|
|
||||||
// Go through each unit in the map, calculating the map location
|
// 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;
|
$lft = $unit['MapsUnit']['pt_left'] + $boundary_adjustment;
|
||||||
$top = $unit['MapsUnit']['pt_top'] + $boundary_adjustment;
|
$top = $unit['MapsUnit']['pt_top'] + $boundary_adjustment;
|
||||||
|
|
||||||
@@ -132,10 +157,9 @@ class MapsController extends AppController {
|
|||||||
$width *= $screen_adjustment_factor;
|
$width *= $screen_adjustment_factor;
|
||||||
$depth *= $screen_adjustment_factor;
|
$depth *= $screen_adjustment_factor;
|
||||||
|
|
||||||
//$info['units'][$unit['id']] =
|
|
||||||
$info['units'][] =
|
$info['units'][] =
|
||||||
array( 'id' => $unit['id'],
|
array( 'id' => $unit['Unit']['id'],
|
||||||
'name' => $unit['name'],
|
'name' => $unit['Unit']['name'],
|
||||||
'left' => $lft,
|
'left' => $lft,
|
||||||
'right' => $lft + $width,
|
'right' => $lft + $width,
|
||||||
'top' => $top,
|
'top' => $top,
|
||||||
@@ -143,11 +167,15 @@ class MapsController extends AppController {
|
|||||||
'width' => $width,
|
'width' => $width,
|
||||||
'depth' => $depth,
|
'depth' => $depth,
|
||||||
'n-s' => $unit['MapsUnit']['transpose'] ? 0 : 1,
|
'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;
|
return $info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,8 +188,10 @@ class MapsController extends AppController {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function legend($id = null, $requested_width = 400) {
|
function legend($id = null, $requested_width = 400) {
|
||||||
$status = $this->Map->Unit->activeStatusEnums();
|
$status = array_keys($this->Map->Unit->activeStatusEnums());
|
||||||
//pr($status);
|
$occupied_key = array_search('OCCUPIED', $status);
|
||||||
|
array_splice($status, $occupied_key+1, 0, array('LATE'));
|
||||||
|
|
||||||
$rows = 2;
|
$rows = 2;
|
||||||
$cols = (int)((count($status) + $rows - 1) / $rows);
|
$cols = (int)((count($status) + $rows - 1) / $rows);
|
||||||
|
|
||||||
@@ -191,7 +221,7 @@ class MapsController extends AppController {
|
|||||||
$item_width *= $screen_adjustment_factor;
|
$item_width *= $screen_adjustment_factor;
|
||||||
$item_depth *= $screen_adjustment_factor;
|
$item_depth *= $screen_adjustment_factor;
|
||||||
|
|
||||||
foreach ($status AS $code => $value) {
|
foreach ($status AS $code) {
|
||||||
$info['units'][] = array('name' => $code,
|
$info['units'][] = array('name' => $code,
|
||||||
'status' => $code,
|
'status' => $code,
|
||||||
'width' => $item_width,
|
'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']['DIRTY']['bg'] = array('red' => 128, 'green' => 192, 'blue' => 192);
|
||||||
$info['palate']['unit']['VACANT']['bg'] = array('red' => 0, 'green' => 255, 'blue' => 128);
|
$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']['OCCUPIED']['bg'] = array('red' => 0, 'green' => 128, 'blue' => 255);
|
||||||
$info['palate']['unit']['LATE']['bg'] = array('red' => 255, 'green' => 64, 'blue' => 64);
|
$info['palate']['unit']['LATE']['bg'] = array('red' => 255, 'green' => 192, 'blue' => 192);
|
||||||
$info['palate']['unit']['LOCKED']['bg'] = array('red' => 255, 'green' => 128, 'blue' => 128);
|
$info['palate']['unit']['LOCKED']['bg'] = array('red' => 255, 'green' => 64, 'blue' => 64);
|
||||||
$info['palate']['unit']['LIENED']['bg'] = array('red' => 255, 'green' => 192, 'blue' => 192);
|
$info['palate']['unit']['LIENED']['bg'] = array('red' => 255, 'green' => 0, 'blue' => 128);
|
||||||
|
|
||||||
// Determine text color to go with each background
|
// Determine text color to go with each background
|
||||||
foreach ($info['palate']['unit'] AS &$code) {
|
foreach ($info['palate']['unit'] AS &$code) {
|
||||||
364
site/controllers/statement_entries_controller.php
Normal file
364
site/controllers/statement_entries_controller.php
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class StatementEntriesController extends AppController {
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / current / past / all
|
||||||
|
* - Creates a list of statement entries
|
||||||
|
*/
|
||||||
|
|
||||||
|
function index() { $this->gridView('All Statement Entries'); }
|
||||||
|
function unpaid() { $this->gridView('Unpaid Charges', 'unreconciled'); }
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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)));
|
||||||
|
|
||||||
|
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?)
|
||||||
|
if ($index != 'Transaction.stamp' &&
|
||||||
|
$index != 'StatementEntry.effective_date') {
|
||||||
|
$order[] = 'Transaction.stamp ' . $direction;
|
||||||
|
$order[] = 'StatementEntry.effective_date ' . $direction;
|
||||||
|
}
|
||||||
|
$order[] = 'StatementEntry.id ' . $direction;
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataCountExecute(&$params, &$model, $query) {
|
||||||
|
if ($params['action'] === 'unreconciled') {
|
||||||
|
// REVISIT <AP> 20100413:
|
||||||
|
// This is a lame solution, as it runs the same queries twice
|
||||||
|
// (and causes code duplication). However, I'm not in the mood
|
||||||
|
// to flush out an actual "count" solution at the moment, and I
|
||||||
|
// also don't want to cache the results in $params (although
|
||||||
|
// that is probably the most sensible solution). So, I'll just
|
||||||
|
// calculate the reconciled set both times and live with the
|
||||||
|
// performance and maintenance penalty
|
||||||
|
$lquery = array('conditions' => $query['conditions']);
|
||||||
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $lquery, true);
|
||||||
|
return count($set['entries']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::gridDataCountExecute($params, $model, $query);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataRecordsExecute(&$params, &$model, $query) {
|
||||||
|
|
||||||
|
if ($params['action'] === 'unreconciled') {
|
||||||
|
$lquery = array('conditions' => $query['conditions']);
|
||||||
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $lquery, true);
|
||||||
|
|
||||||
|
$entries = array();
|
||||||
|
foreach ($set['entries'] AS $entry)
|
||||||
|
$entries[] = $entry['StatementEntry']['id'];
|
||||||
|
|
||||||
|
$query['conditions'] = array('StatementEntry.id' => $entries);
|
||||||
|
$params['userdata']['balance'] = $set['summary']['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: chargesbymonth
|
||||||
|
* - Displays charges by month
|
||||||
|
*/
|
||||||
|
|
||||||
|
function chargesbymonth($months = 12) {
|
||||||
|
$result = $this->StatementEntry->find
|
||||||
|
('all',
|
||||||
|
array('link' => array('Account' => array('fields' => 'name')),
|
||||||
|
'fields' => array_merge(array('MONTHNAME(effective_date) AS month',
|
||||||
|
'YEAR(effective_date) AS year'),
|
||||||
|
$this->StatementEntry->chargeDisbursementFields(true)),
|
||||||
|
'conditions' => array('Account.type' => 'INCOME',
|
||||||
|
'effective_date >= DATE(NOW() - INTERVAL '.($months-1).' MONTH - INTERVAL DAY(NOW())-1 DAY)',
|
||||||
|
'effective_date <= NOW()',
|
||||||
|
),
|
||||||
|
'group' => array('YEAR(effective_date)', 'MONTH(effective_date)', 'Account.id'),
|
||||||
|
'order' => array('YEAR(effective_date) DESC', 'MONTH(effective_date) DESC', 'Account.name'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$overview = array('months' => array(), 'charges' => 0);
|
||||||
|
foreach ($result AS $row) {
|
||||||
|
$mname = $row[0]['month'] .', '. $row[0]['year'];
|
||||||
|
if (empty($overview['months'][$mname]))
|
||||||
|
$overview['months'][$mname] = array('name' => $mname,
|
||||||
|
'subs' => array(),
|
||||||
|
'charges' => 0);
|
||||||
|
$month = &$overview['months'][$mname];
|
||||||
|
$month['subs'][] = array('name' => $row['Account']['name'],
|
||||||
|
'charges' => $row[0]['balance']);
|
||||||
|
|
||||||
|
$month['charges'] += $row[0]['balance'];
|
||||||
|
$overview['charges'] += $row[0]['balance'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the Reports menu section
|
||||||
|
$this->sideMenuAreaActivate('REPORT');
|
||||||
|
|
||||||
|
// Prepare to render.
|
||||||
|
$this->set('months', $months);
|
||||||
|
$this->set('title', 'Monthly Charges');
|
||||||
|
$this->set(compact('overview'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
519
site/controllers/transactions_controller.php
Normal file
519
site/controllers/transactions_controller.php
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class TransactionsController extends AppController {
|
||||||
|
|
||||||
|
var $components = array('RequestHandler');
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* override: addGridViewSideMenuLinks
|
||||||
|
* - Adds grid view navigation side menu 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / all
|
||||||
|
* - Generate a listing of transactions
|
||||||
|
*/
|
||||||
|
|
||||||
|
function index() { $this->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');
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridView($title, $action = null, $element = null) {
|
||||||
|
if ($title != 'Deposits')
|
||||||
|
$this->set('include', array('Customer'));
|
||||||
|
parent::gridView($title, $action, $element);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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(// Models
|
||||||
|
'Account' => array('fields' => array()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataTables(&$params, &$model) {
|
||||||
|
$link = $this->gridDataCountTables($params, $model);
|
||||||
|
$link['link']['StatementEntry'] = array('fields' => array());
|
||||||
|
$link['link']['DepositTender'] = array('fields' => array());
|
||||||
|
$link['link']['Customer'] = array('fields' => array('id', 'name'));
|
||||||
|
return $link;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataFields(&$params, &$model) {
|
||||||
|
$fields = parent::gridDataFields($params, $model);
|
||||||
|
//$fields[] = 'COUNT(StatementEntry.id) AS entries';
|
||||||
|
$fields[] = ("IF(Transaction.type = 'DEPOSIT'," .
|
||||||
|
" COUNT(DepositTender.id)," .
|
||||||
|
" COUNT(StatementEntry.id)) AS entries");
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataConditions(&$params, &$model) {
|
||||||
|
$conditions = parent::gridDataConditions($params, $model);
|
||||||
|
|
||||||
|
if (in_array($params['action'], array('invoice', 'receipt', 'deposit')))
|
||||||
|
$conditions[] = array('Transaction.type' => strtoupper($params['action']));
|
||||||
|
|
||||||
|
return $conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||||
|
$links['Transaction'] = array('id', 'action' => ($params['action'] == 'deposit'
|
||||||
|
? 'deposit_slip' : 'view'));
|
||||||
|
$links['Customer'] = array('name');
|
||||||
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: postInvoice
|
||||||
|
* - handles the creation of a charge invoice
|
||||||
|
*/
|
||||||
|
|
||||||
|
function postInvoice($redirect = true) {
|
||||||
|
if (!$this->RequestHandler->isPost()) {
|
||||||
|
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->Transaction->addInvoice($this->data, null,
|
||||||
|
$this->data['Lease']['id'])) {
|
||||||
|
$this->Session->setFlash("INVOICE FAILED", true);
|
||||||
|
// REVISIT <AP> 20090706:
|
||||||
|
// Until we can work out the session problems,
|
||||||
|
// just die.
|
||||||
|
die("<H1>INVOICE FAILED</H1>");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: postReceipt
|
||||||
|
* - handles the creation of a receipt
|
||||||
|
*/
|
||||||
|
|
||||||
|
function postReceipt($redirect = true) {
|
||||||
|
if (!$this->RequestHandler->isPost()) {
|
||||||
|
echo('<H2>THIS IS NOT A POST FOR SOME REASON</H2>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->data['Entry'] AS &$entry) {
|
||||||
|
$entry['Tender'] = $entry['type'][$entry['tender_type_id']];
|
||||||
|
unset($entry['type']);
|
||||||
|
unset($entry['tender_type_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->Transaction->addReceipt($this->data,
|
||||||
|
$this->data['Customer']['id'],
|
||||||
|
(isset($this->data['Lease']['id'])
|
||||||
|
? $this->data['Lease']['id']
|
||||||
|
: null ))) {
|
||||||
|
$this->Session->setFlash("RECEIPT FAILED", true);
|
||||||
|
// REVISIT <AP> 20090706:
|
||||||
|
// Until we can work out the session problems,
|
||||||
|
// just die.
|
||||||
|
die("<H1>RECEIPT FAILED</H1>");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
255
site/controllers/unit_sizes_controller.php
Normal file
255
site/controllers/unit_sizes_controller.php
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
<?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 gridDataOrder(&$params, &$model, $index, $direction) {
|
||||||
|
if ($index == 'UnitType.name')
|
||||||
|
$index = 'UnitType.code';
|
||||||
|
|
||||||
|
$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[] = 'UnitType.code ' . $direction;
|
||||||
|
$order[] = 'sqft ' . $direction;
|
||||||
|
$order[] = 'UnitSize.rent ' . $direction;
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
461
site/controllers/units_controller.php
Normal file
461
site/controllers/units_controller.php
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class UnitsController extends AppController {
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* override: addGridViewSideMenuLinks
|
||||||
|
* - Adds grid view navigation side menu 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: index / unavailable / vacant / occupied / all
|
||||||
|
* - Generate a listing of units
|
||||||
|
*/
|
||||||
|
|
||||||
|
function index() { $this->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: gridData
|
||||||
|
* - With the application controller handling the gridData action,
|
||||||
|
* these virtual functions ensure that the correct data is passed
|
||||||
|
* to jqGrid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function gridDataSetup(&$params) {
|
||||||
|
parent::gridDataSetup($params);
|
||||||
|
if (!isset($params['action']))
|
||||||
|
$params['action'] = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
|
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', */
|
||||||
|
/* ), */
|
||||||
|
/* ); */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataTables(&$params, &$model) {
|
||||||
|
$link = $this->gridDataCountTables($params, $model);
|
||||||
|
$link['link']['CurrentLease']['StatementEntry'] = array('fields' => array());
|
||||||
|
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 gridDataConditions(&$params, &$model) {
|
||||||
|
$conditions = parent::gridDataConditions($params, $model);
|
||||||
|
|
||||||
|
if ($params['action'] === 'unavailable') {
|
||||||
|
$conditions[] = $this->Unit->conditionUnavailable();
|
||||||
|
}
|
||||||
|
elseif ($params['action'] === 'vacant') {
|
||||||
|
$conditions[] = $this->Unit->conditionVacant();
|
||||||
|
}
|
||||||
|
elseif ($params['action'] === 'occupied') {
|
||||||
|
$conditions[] = $this->Unit->conditionOccupied();
|
||||||
|
}
|
||||||
|
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 gridDataOrder(&$params, &$model, $index, $direction) {
|
||||||
|
// Instead of sorting by name, sort by defined order
|
||||||
|
if ($index === 'Unit.name')
|
||||||
|
$index = 'Unit.sort_order';
|
||||||
|
|
||||||
|
$order = array();
|
||||||
|
$order[] = parent::gridDataOrder($params, $model, $index, $direction);
|
||||||
|
|
||||||
|
// If sorting by anything other than name (defined order)
|
||||||
|
// add the sort-order as a secondary condition
|
||||||
|
if ($index !== 'Unit.name')
|
||||||
|
$order[] = parent::gridDataOrder($params, $model,
|
||||||
|
'Unit.sort_order', $direction);
|
||||||
|
|
||||||
|
return $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
|
||||||
|
$links['Unit'] = array('name');
|
||||||
|
$links['UnitSize'] = array('name');
|
||||||
|
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: move_in
|
||||||
|
* - Sets up the move-in page for the given unit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function move_in($id = null) {
|
||||||
|
$customer = array();
|
||||||
|
$unit = array();
|
||||||
|
|
||||||
|
if (!empty($id)) {
|
||||||
|
$this->Unit->recursive = -1;
|
||||||
|
$unit = current($this->Unit->read(null, $id));
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = 'Unit Move-In';
|
||||||
|
$this->set(compact('customer', 'unit', 'title'));
|
||||||
|
$this->render('/leases/move');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: move_out
|
||||||
|
* - prepare or execute a move out on a specific lease
|
||||||
|
*/
|
||||||
|
|
||||||
|
function move_out($id) {
|
||||||
|
|
||||||
|
$unit = $this->Unit->find
|
||||||
|
('first', array
|
||||||
|
('contain' => array
|
||||||
|
(// Models
|
||||||
|
'CurrentLease' =>
|
||||||
|
array(//'conditions' => array('Lease.moveout_date' => null),
|
||||||
|
// Models
|
||||||
|
'Customer' =>
|
||||||
|
array('fields' => array('id', 'name'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'conditions' => array('Unit.id' => $id),
|
||||||
|
));
|
||||||
|
$this->set('customer', $unit['CurrentLease']['Customer']);
|
||||||
|
$this->set('unit', $unit['Unit']);
|
||||||
|
$this->set('lease', $unit['CurrentLease']);
|
||||||
|
|
||||||
|
$redirect = array('controller' => 'units',
|
||||||
|
'action' => 'view',
|
||||||
|
$id);
|
||||||
|
|
||||||
|
$title = ('Lease #' . $unit['CurrentLease']['number'] . ': ' .
|
||||||
|
$unit['Unit']['name'] . ': ' .
|
||||||
|
$unit['CurrentLease']['Customer']['name'] . ': Prepare Move-Out');
|
||||||
|
$this->set(compact('title', 'redirect'));
|
||||||
|
$this->render('/leases/move');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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'); }
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: overview
|
||||||
|
* - Displays overview information for all units
|
||||||
|
*/
|
||||||
|
function overview() {
|
||||||
|
$result = $this->Unit->find
|
||||||
|
('all',
|
||||||
|
array('link' => array('UnitSize' => array('fields' => array(), 'UnitType' => array('fields' => array('name')))),
|
||||||
|
'fields' => array('status', 'COUNT(Unit.id) AS cnt', 'SUM(Unit.rent) AS rents'),
|
||||||
|
//'conditions' => array('
|
||||||
|
'group' => array('UnitType.id', 'Unit.status'),
|
||||||
|
'order' => array('UnitType.name', 'Unit.status')
|
||||||
|
));
|
||||||
|
|
||||||
|
$overview = array('types' => array(), 'count' => 0, 'rents' => 0);
|
||||||
|
foreach ($result AS $row) {
|
||||||
|
$utname = $row['UnitType']['name'];
|
||||||
|
if (empty($overview['types'][$utname]))
|
||||||
|
$overview['types'][$utname] = array('name' => $utname,
|
||||||
|
'subs' => array(),
|
||||||
|
'count' => 0,
|
||||||
|
'rents' => 0,
|
||||||
|
'phys_pct' => 0,
|
||||||
|
'econ_pct' => 0);
|
||||||
|
$type = &$overview['types'][$utname];
|
||||||
|
$type['subs'][] = array('name' => $row['Unit']['status'],
|
||||||
|
'count' => $row[0]['cnt'],
|
||||||
|
'rents' => $row[0]['rents'],
|
||||||
|
'phys_subpct' => 0,
|
||||||
|
'phys_totpct' => 0,
|
||||||
|
'econ_subpct' => 0,
|
||||||
|
'econ_totpct' => 0);
|
||||||
|
|
||||||
|
|
||||||
|
$type['count'] += $row[0]['cnt'];
|
||||||
|
$type['rents'] += $row[0]['rents'];
|
||||||
|
|
||||||
|
$overview['count'] += $row[0]['cnt'];
|
||||||
|
$overview['rents'] += $row[0]['rents'];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($overview['types'] AS &$type) {
|
||||||
|
foreach ($type['subs'] AS &$sub) {
|
||||||
|
$sub['phys_subpct'] = $sub['count'] / $type['count'];
|
||||||
|
$sub['econ_subpct'] = $sub['rents'] / $type['rents'];
|
||||||
|
$sub['phys_totpct'] = $sub['count'] / $overview['count'];
|
||||||
|
$sub['econ_totpct'] = $sub['rents'] / $overview['rents'];
|
||||||
|
}
|
||||||
|
$type['phys_pct'] = $type['count'] / $overview['count'];
|
||||||
|
$type['econ_pct'] = $type['rents'] / $overview['rents'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the Reports menu section
|
||||||
|
$this->sideMenuAreaActivate('REPORT');
|
||||||
|
|
||||||
|
// Prepare to render.
|
||||||
|
$this->set('title', 'Unit Overview');
|
||||||
|
$this->set(compact('overview'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* action: view
|
||||||
|
* - Displays information about a specific unit
|
||||||
|
*/
|
||||||
|
|
||||||
|
function view($id = null) {
|
||||||
|
if (!$id) {
|
||||||
|
$this->Session->setFlash(__('Invalid Item.', true));
|
||||||
|
$this->redirect(array('action'=>''));
|
||||||
|
}
|
||||||
|
|
||||||
|
$unit = $this->Unit->find
|
||||||
|
('first',
|
||||||
|
array('contain' =>
|
||||||
|
array(// Models
|
||||||
|
'UnitSize',
|
||||||
|
'Lease' => array('Customer'),
|
||||||
|
'CurrentLease' => array('Customer')
|
||||||
|
),
|
||||||
|
'conditions' => array('Unit.id' => $id),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Get the balance on each lease.
|
||||||
|
foreach ($unit['Lease'] AS &$lease) {
|
||||||
|
$stats = $this->Unit->Lease->stats($lease['id']);
|
||||||
|
$lease['balance'] = $stats['balance'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$outstanding_balance = 0;
|
||||||
|
$outstanding_deposit = 0;
|
||||||
|
if (isset($unit['CurrentLease']['id'])) {
|
||||||
|
// Figure out the outstanding balance of the current lease.
|
||||||
|
$stats = $this->Unit->stats($id);
|
||||||
|
$outstanding_balance =
|
||||||
|
$stats['CurrentLease']['balance'];
|
||||||
|
|
||||||
|
// Figure out the total security deposit for the current lease.
|
||||||
|
$deposits = $this->Unit->Lease->securityDeposits($unit['CurrentLease']['id']);
|
||||||
|
$outstanding_deposit = $this->Unit->Lease->securityDepositBalance($unit['CurrentLease']['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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 {
|
||||||
|
// Unit is unavailable (dirty, damaged, reserved, business-use, etc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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']), 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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
377
site/models/account.php
Normal file
377
site/models/account.php
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
<?php
|
||||||
|
class Account extends AppModel {
|
||||||
|
|
||||||
|
var $hasOne = array(
|
||||||
|
'CurrentLedger' => array(
|
||||||
|
'className' => 'Ledger',
|
||||||
|
// REVISIT <AP> 20090702:
|
||||||
|
// I would prefer this statement, which has no
|
||||||
|
// engine specific code. However, it doesn't
|
||||||
|
// work with the Linkable behavior. I need to
|
||||||
|
// look into that, just not right now.
|
||||||
|
//'conditions' => array(array('CurrentLedger.close_transaction_id' => null)),
|
||||||
|
'conditions' => array('CurrentLedger.close_transaction_id IS NULL'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasMany = array(
|
||||||
|
'Ledger',
|
||||||
|
'LedgerEntry',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: type
|
||||||
|
* - Returns the type of this account
|
||||||
|
*/
|
||||||
|
function type($id) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$account = $this->find('first', array
|
||||||
|
('recursive' => -1,
|
||||||
|
'fields' => array('type'),
|
||||||
|
'conditions' => array(array('Account.id' => $id)),
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
|
||||||
|
return $account['Account']['type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: fundamentalType
|
||||||
|
* - Returns the fundmental type of the account, credit or debit
|
||||||
|
*/
|
||||||
|
function fundamentalType($id_or_type) {
|
||||||
|
if (is_numeric($id_or_type))
|
||||||
|
$type = $this->type($id_or_type);
|
||||||
|
else
|
||||||
|
$type = $id_or_type;
|
||||||
|
|
||||||
|
// Asset and Expense accounts are debit accounts
|
||||||
|
if (in_array(strtoupper($type), array('ASSET', 'EXPENSE')))
|
||||||
|
return 'debit';
|
||||||
|
|
||||||
|
// Otherwise, it's a credit account
|
||||||
|
return 'credit';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: fundamentalOpposite
|
||||||
|
* - Returns the opposite fundmental type of the account, credit or debit
|
||||||
|
*/
|
||||||
|
function fundamentalOpposite($id_or_type) {
|
||||||
|
if (in_array(strtolower($id_or_type), array('credit', 'debit')))
|
||||||
|
$fund = $id_or_type;
|
||||||
|
else
|
||||||
|
$fund = $this->fundamentalType($id_or_type);
|
||||||
|
|
||||||
|
if (strtolower($fund) == 'debit')
|
||||||
|
return 'credit';
|
||||||
|
|
||||||
|
return 'debit';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: name
|
||||||
|
* - Returns the name of this account
|
||||||
|
*/
|
||||||
|
function name($id) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$account = $this->find('first', array
|
||||||
|
('recursive' => -1,
|
||||||
|
'fields' => array('name'),
|
||||||
|
'conditions' => array(array('Account.id' => $id)),
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
|
||||||
|
return $account['Account']['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: debitCreditFields
|
||||||
|
* - Returns the fields necessary to determine whether the queried
|
||||||
|
* entries are a debit, or a credit, and also the effect each have
|
||||||
|
* on the overall balance of the account.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function debitCreditFields($sum = false, $balance = true,
|
||||||
|
$entry_name = 'LedgerEntry', $account_name = 'Account') {
|
||||||
|
return $this->LedgerEntry->debitCreditFields
|
||||||
|
($sum, $balance, $entry_name, $account_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: Account IDs
|
||||||
|
* - Returns the ID of the desired account
|
||||||
|
*/
|
||||||
|
|
||||||
|
function 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'
|
||||||
|
); }
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: fundamentalAccounts
|
||||||
|
* - Returns an array of accounts by their fundamental type
|
||||||
|
*/
|
||||||
|
|
||||||
|
function fundamentalAccounts($ftype) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$account = $this->find('all', array
|
||||||
|
('contain' => array('CurrentLedger'),
|
||||||
|
'fields' => array('Account.id', 'Account.type', 'Account.name', 'CurrentLedger.id'),
|
||||||
|
'conditions' => array('Account.type' => strtoupper($ftype))
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
|
||||||
|
return $account;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: relatedAccounts
|
||||||
|
* - Returns an array of accounts related by similar attributes
|
||||||
|
*/
|
||||||
|
|
||||||
|
function relatedAccounts($attribute, $extra = null) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$accounts = $this->find('all', array
|
||||||
|
('fields' => array('Account.id', 'Account.name'),
|
||||||
|
'conditions' => array('Account.'.$attribute => true),
|
||||||
|
'order' => array('Account.name'),
|
||||||
|
) + (isset($extra) ? $extra : array())
|
||||||
|
);
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
|
||||||
|
// Rearrange to be of the form (id => name)
|
||||||
|
$rel_accounts = array();
|
||||||
|
foreach ($accounts AS $acct) {
|
||||||
|
$rel_accounts[$acct['Account']['id']] = $acct['Account']['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rel_accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: xxxAccounts
|
||||||
|
* - Returns an array of accounts suitable for activity xxx
|
||||||
|
*/
|
||||||
|
|
||||||
|
function invoiceAccounts() { return $this->relatedAccounts('invoices'); }
|
||||||
|
function receiptAccounts() { return $this->relatedAccounts('receipts'); }
|
||||||
|
function depositAccounts() { return $this->relatedAccounts('deposits'); }
|
||||||
|
function refundAccounts() { return $this->relatedAccounts('refunds'); }
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: collectableAccounts
|
||||||
|
* - Returns an array of accounts suitable to show income collection
|
||||||
|
*/
|
||||||
|
|
||||||
|
function collectableAccounts() {
|
||||||
|
$accounts = $this->receiptAccounts();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: currentLedgerID
|
||||||
|
* - Returns the current ledger ID of the account
|
||||||
|
*/
|
||||||
|
function currentLedgerID($id) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$item = $this->find('first', array
|
||||||
|
('contain' => 'CurrentLedger',
|
||||||
|
'conditions' => array('Account.id' => $id),
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
return $item['CurrentLedger']['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: ledgers
|
||||||
|
* - Returns an array of ledger ids from the given account
|
||||||
|
*/
|
||||||
|
function ledgers($id, $all = false) {
|
||||||
|
if ($all) {
|
||||||
|
$contain = array('Ledger' => array('fields' => array('Ledger.id')));
|
||||||
|
} else {
|
||||||
|
$contain = array('CurrentLedger' => array('fields' => array('CurrentLedger.id')));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$account = $this->find('first', array
|
||||||
|
('contain' => $contain,
|
||||||
|
'fields' => array(),
|
||||||
|
'conditions' => array(array('Account.id' => $id)),
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
|
||||||
|
if ($all) {
|
||||||
|
$ledger_ids = array();
|
||||||
|
foreach ($account['Ledger'] AS $ledger)
|
||||||
|
array_push($ledger_ids, $ledger['id']);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$ledger_ids = array($account['CurrentLedger']['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pr(array('function' => 'Account::ledgers', */
|
||||||
|
/* 'args' => compact('id', 'all'), */
|
||||||
|
/* 'return' => $ledger_ids)); */
|
||||||
|
|
||||||
|
return $ledger_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: closeCurrentLedger
|
||||||
|
* - Closes the current account ledger, and opens a new one
|
||||||
|
* with the old balance carried forward.
|
||||||
|
*/
|
||||||
|
function closeCurrentLedgers($ids = null) {
|
||||||
|
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$account = $this->find('all', array
|
||||||
|
('contain' => array('CurrentLedger.id'),
|
||||||
|
'fields' => array(),
|
||||||
|
'conditions' => (empty($ids)
|
||||||
|
? array()
|
||||||
|
: array(array('Account.id' => $ids)))
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
//pr(compact('id', 'account'));
|
||||||
|
|
||||||
|
$ledger_ids = array();
|
||||||
|
foreach ($account AS $acct)
|
||||||
|
$ledger_ids[] = $acct['CurrentLedger']['id'];
|
||||||
|
|
||||||
|
return $this->Ledger->closeLedgers($ledger_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: ledgerEntries
|
||||||
|
* - Returns an array of ledger entries that belong to the given
|
||||||
|
* account, either just from the current ledger, or from all ledgers.
|
||||||
|
*/
|
||||||
|
function ledgerEntries($id, $all = false, $cond = null, $link = null) {
|
||||||
|
$ledgers = $this->ledgers($id, $all);
|
||||||
|
return $this->Ledger->ledgerEntries($ledgers, $cond, $link);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: stats
|
||||||
|
* - Returns summary data from the requested account.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function stats($id = null, $all = false, $query = null) {
|
||||||
|
if (!$id)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$this->queryInit($query);
|
||||||
|
$query['link'] = array('Account' => $query['link']);
|
||||||
|
|
||||||
|
$stats = array();
|
||||||
|
foreach ($this->ledgers($id, $all) AS $ledger)
|
||||||
|
$this->statsMerge($stats['Ledger'],
|
||||||
|
$this->Ledger->stats($ledger, $query));
|
||||||
|
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
486
site/models/behaviors/linkable.php
Normal file
486
site/models/behaviors/linkable.php
Normal file
@@ -0,0 +1,486 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* LinkableBehavior
|
||||||
|
* Light-weight approach for data mining on deep relations between models.
|
||||||
|
* Join tables based on model relations to easily enable right to left find operations.
|
||||||
|
*
|
||||||
|
* Can be used as a alternative to the ContainableBehavior:
|
||||||
|
* - On data fetching only in right to left operations,
|
||||||
|
* wich means that in "one to many" relations (hasMany, hasAndBelongsToMany)
|
||||||
|
* should only be used from the "many to one" tables. i.e:
|
||||||
|
* To fetch all Users assigneds to a Project with ProjectAssignment,
|
||||||
|
* $Project->find('all', array('link' => 'User', 'conditions' => 'project_id = 1'))
|
||||||
|
* - Won't produce the desired result as data came from users table will be lost.
|
||||||
|
* $User->find('all', array('link' => 'Project', 'conditions' => 'project_id = 1'))
|
||||||
|
* - Will fetch all users related to the specified project in one query
|
||||||
|
*
|
||||||
|
* - On data mining as a much lighter approach - can reduce 300+ query find operations
|
||||||
|
* in one single query with joins; "or your money back!" ;-)
|
||||||
|
*
|
||||||
|
* - Has the 'fields' param enabled to make it easy to replace Containable usage,
|
||||||
|
* only change the 'contain' param to 'link'.
|
||||||
|
*
|
||||||
|
* Linkable Behavior. Taking it easy in your DB.
|
||||||
|
* RafaelBandeira <rafaelbandeira3(at)gmail(dot)com>
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @version 1.0;
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* NOTE TO <AP> 20090615:
|
||||||
|
* This structure should be linkable (it was my test case).
|
||||||
|
|
||||||
|
$entry = $this->LedgerEntry->find
|
||||||
|
('first',
|
||||||
|
array('link' => array('DebitLedger' =>
|
||||||
|
array(
|
||||||
|
'fields' => array('id'),
|
||||||
|
'alias' => 'MyDebitLedger',
|
||||||
|
'Account' =>
|
||||||
|
array(
|
||||||
|
'fields' => array('id'),
|
||||||
|
'alias' => 'MyDebitAccount'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'MyCreditLedger' =>
|
||||||
|
array(
|
||||||
|
'fields' => array('id'),
|
||||||
|
'class' => 'CreditLedger',
|
||||||
|
'MyCreditAccount' =>
|
||||||
|
array(
|
||||||
|
'fields' => array('id'),
|
||||||
|
'class' => 'Account'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'MyOtherLedger' =>
|
||||||
|
array(
|
||||||
|
'fields' => array('id'),
|
||||||
|
'class' => 'Ledger',
|
||||||
|
'alias' => 'AliasToMyOtherLedger',
|
||||||
|
'Account' =>
|
||||||
|
array(
|
||||||
|
'fields' => array('id'),
|
||||||
|
'alias' => 'AliasToMyOtherAccount'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
'conditions' => array('LedgerEntry.id' => $id),
|
||||||
|
));
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
class LinkableBehavior extends ModelBehavior {
|
||||||
|
|
||||||
|
protected $_key = 'link';
|
||||||
|
|
||||||
|
protected $_options = array(
|
||||||
|
'type' => true, 'table' => true, 'alias' => true, 'joins' => true,
|
||||||
|
'conditions' => true, 'fields' => true, 'reference' => true,
|
||||||
|
'class' => true, 'defaults' => true, 'linkalias' => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
protected $_defaults = array('type' => 'LEFT');
|
||||||
|
|
||||||
|
function pr($lev, $mixed) {
|
||||||
|
if ($lev >= 3)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pr($mixed);
|
||||||
|
return;
|
||||||
|
|
||||||
|
$trace = debug_backtrace(false);
|
||||||
|
//array_shift($trace);
|
||||||
|
$calls = array();
|
||||||
|
foreach ($trace AS $call) {
|
||||||
|
$call = array_intersect_key($call,
|
||||||
|
array('file'=>1,
|
||||||
|
'line'=>1,
|
||||||
|
//'class'=>1,
|
||||||
|
'function'=>1,
|
||||||
|
));
|
||||||
|
$calls[] = implode("; ", $call);
|
||||||
|
}
|
||||||
|
pr(array('debug' => $mixed, 'stack' => $calls));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a function for made recursive str_replaces in an array
|
||||||
|
* NOTE: The palacement of this function is terrible, but I don't
|
||||||
|
* know if I really want to go down this path or not.
|
||||||
|
*/
|
||||||
|
function recursive_array_replace($find, $replace, &$data) {
|
||||||
|
if (!isset($data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (is_array($data)) {
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
if (is_array($value)) {
|
||||||
|
$this->recursive_array_replace($find, $replace, $data[$key]);
|
||||||
|
} else {
|
||||||
|
$data[$key] = str_replace($find, $replace, $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$data = str_replace($find, $replace, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beforeFind(&$Model, $query) {
|
||||||
|
if (!isset($query[$this->_key]))
|
||||||
|
return $query;
|
||||||
|
|
||||||
|
if (!isset($query['fields']) || $query['fields'] === true) {
|
||||||
|
$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));
|
||||||
|
|
||||||
|
$reference = array('class' => $Model->alias,
|
||||||
|
'alias' => $Model->alias,
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = array_diff_key($query, array($this->_key => 1));
|
||||||
|
$this->buildQuery($Model, $Model->alias, $Model->alias, $Model->findQueryType,
|
||||||
|
$query[$this->_key], $result);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildQuery(&$Reference, $referenceClass, $referenceAlias, $query_type, $links, &$result) {
|
||||||
|
if (empty($links))
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->pr(10,
|
||||||
|
array('begin' => 'Linkable::buildQuery',
|
||||||
|
'args' => compact('referenceClass', 'referenceAlias', 'query_type', 'links'),
|
||||||
|
));
|
||||||
|
|
||||||
|
//$defaults = $this->_defaults;// + array($this->_key => array());
|
||||||
|
//$optionsKeys = $this->_options + array($this->_key => true);
|
||||||
|
|
||||||
|
$links = Set::normalize($links);
|
||||||
|
$this->pr(24,
|
||||||
|
array('checkpoint' => 'Normalized links',
|
||||||
|
compact('links'),
|
||||||
|
));
|
||||||
|
foreach ($links as $alias => $options) {
|
||||||
|
if (is_null($options)) {
|
||||||
|
$options = array();
|
||||||
|
}
|
||||||
|
//$options = array_intersect_key($options, $optionsKeys);
|
||||||
|
//$options = am($this->_defaults, compact('alias'), $options);
|
||||||
|
|
||||||
|
$options += compact('alias');
|
||||||
|
$options += $this->_defaults;
|
||||||
|
|
||||||
|
if (empty($options['alias'])) {
|
||||||
|
throw new InvalidArgumentException(sprintf('%s::%s must receive aliased links', get_class($this), __FUNCTION__));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($options['class']))
|
||||||
|
$options['class'] = $alias;
|
||||||
|
|
||||||
|
if (!isset($options['conditions']))
|
||||||
|
$options['conditions'] = null;
|
||||||
|
elseif (!is_array($options['conditions']))
|
||||||
|
$options['conditions'] = array($options['conditions']);
|
||||||
|
|
||||||
|
$this->pr(20,
|
||||||
|
array('checkpoint' => 'Begin Model Work',
|
||||||
|
compact('referenceAlias', 'alias', 'options'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$modelClass = $options['class'];
|
||||||
|
$modelAlias = $options['alias'];
|
||||||
|
$Model =& ClassRegistry::init($modelClass); // the incoming model to be linked in query
|
||||||
|
|
||||||
|
$this->pr(12,
|
||||||
|
array('checkpoint' => 'Model Established',
|
||||||
|
'Reference' => ($referenceAlias .' : '. $referenceClass .
|
||||||
|
' ('. $Reference->alias .' : '. $Reference->name .')'),
|
||||||
|
'Model' => ($modelAlias .' : '. $modelClass .
|
||||||
|
' ('. $Model->alias .' : '. $Model->name .')'),
|
||||||
|
));
|
||||||
|
|
||||||
|
$db =& $Model->getDataSource();
|
||||||
|
|
||||||
|
$associatedThroughReference = 0;
|
||||||
|
$association = null;
|
||||||
|
|
||||||
|
// Figure out how these two models are related, creating
|
||||||
|
// a relationship if one doesn't otherwise already exists.
|
||||||
|
if (($associations = $Reference->getAssociated()) &&
|
||||||
|
isset($associations[$Model->alias])) {
|
||||||
|
$this->pr(12, array('checkpoint' => "Reference ($referenceClass) defines association to Model ($modelClass)"));
|
||||||
|
$associatedThroughReference = 1;
|
||||||
|
$type = $associations[$Model->alias];
|
||||||
|
$association = $Reference->{$type}[$Model->alias];
|
||||||
|
}
|
||||||
|
elseif (($associations = $Model->getAssociated()) &&
|
||||||
|
isset($associations[$Reference->alias])) {
|
||||||
|
$this->pr(12, array('checkpoint' => "Model ($modelClass) defines association to Reference ($referenceClass)"));
|
||||||
|
$type = $associations[$Reference->alias];
|
||||||
|
$association = $Model->{$type}[$Reference->alias];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No relationship... make our best effort to create one.
|
||||||
|
$this->pr(12, array('checkpoint' => "No assocation between Reference ($referenceClass) and Model ($modelClass)"));
|
||||||
|
$type = 'belongsTo';
|
||||||
|
$Model->bind($Reference->alias);
|
||||||
|
// Grab the association now, since we'll unbind in a moment.
|
||||||
|
$association = $Model->{$type}[$Reference->alias];
|
||||||
|
$Model->unbindModel(array('belongsTo' => array($Reference->alias)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine which model holds the foreign key
|
||||||
|
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference) {
|
||||||
|
$primaryAlias = $referenceAlias;
|
||||||
|
$foreignAlias = $modelAlias;
|
||||||
|
$primaryModel = $Reference;
|
||||||
|
$foreignModel = $Model;
|
||||||
|
} else {
|
||||||
|
$primaryAlias = $modelAlias;
|
||||||
|
$foreignAlias = $referenceAlias;
|
||||||
|
$primaryModel = $Model;
|
||||||
|
$foreignModel = $Reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($associatedThroughReference)
|
||||||
|
$associationAlias = $referenceAlias;
|
||||||
|
else
|
||||||
|
$associationAlias = $modelAlias;
|
||||||
|
|
||||||
|
$this->pr(30,
|
||||||
|
array('checkpoint' => 'Options/Association pre-merge',
|
||||||
|
compact('association', 'options'),
|
||||||
|
));
|
||||||
|
|
||||||
|
// A couple exceptions before performing a union of
|
||||||
|
// options and association. Namely, most fields result
|
||||||
|
// in either/or, but a couple should include BOTH the
|
||||||
|
// options AND the association settings.
|
||||||
|
foreach (array('fields', 'conditions') AS $fld) {
|
||||||
|
$this->pr(31,
|
||||||
|
array('checkpoint' => 'Options/Associations field original',
|
||||||
|
compact('fld') +
|
||||||
|
array("options[$fld]" =>
|
||||||
|
array('value' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? $options[$fld] : '-null-') : '-unset-',
|
||||||
|
'type' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? gettype($options[$fld]) : '-null-') : '-unset-'),
|
||||||
|
"association[$fld]" =>
|
||||||
|
array('value' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? $association[$fld] : '-null-') : '-unset-',
|
||||||
|
'type' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? gettype($association[$fld]) : '-null-') : '-unset-'),
|
||||||
|
)));
|
||||||
|
|
||||||
|
if (!isset($options[$fld]) ||
|
||||||
|
(empty($options[$fld]) && !is_array($options[$fld])))
|
||||||
|
unset($options[$fld]);
|
||||||
|
elseif (!empty($options[$fld]) && !is_array($options[$fld]))
|
||||||
|
$options[$fld] = array($options[$fld]);
|
||||||
|
|
||||||
|
if (!isset($association[$fld]) ||
|
||||||
|
(empty($association[$fld]) && !is_array($association[$fld])))
|
||||||
|
unset($association[$fld]);
|
||||||
|
elseif (!empty($association[$fld]) && !is_array($association[$fld]))
|
||||||
|
$association[$fld] = array($association[$fld]);
|
||||||
|
|
||||||
|
|
||||||
|
$this->pr(31,
|
||||||
|
array('checkpoint' => 'Options/Associations field normalize',
|
||||||
|
compact('fld') +
|
||||||
|
array("options[$fld]" =>
|
||||||
|
array('value' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? $options[$fld] : '-null-') : '-unset-',
|
||||||
|
'type' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? gettype($options[$fld]) : '-null-') : '-unset-'),
|
||||||
|
"association[$fld]" =>
|
||||||
|
array('value' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? $association[$fld] : '-null-') : '-unset-',
|
||||||
|
'type' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? gettype($association[$fld]) : '-null-') : '-unset-'),
|
||||||
|
)));
|
||||||
|
|
||||||
|
if (isset($options[$fld]) && isset($association[$fld]))
|
||||||
|
$options[$fld] = array_merge($options[$fld],
|
||||||
|
$association[$fld]);
|
||||||
|
|
||||||
|
$this->pr(31,
|
||||||
|
array('checkpoint' => 'Options/Associations field merge complete',
|
||||||
|
compact('fld') +
|
||||||
|
array("options[$fld]" =>
|
||||||
|
array('value' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? $options[$fld] : '-null-') : '-unset-',
|
||||||
|
'type' => array_key_exists($fld, $options) ? (isset($options[$fld]) ? gettype($options[$fld]) : '-null-') : '-unset-'),
|
||||||
|
"association[$fld]" =>
|
||||||
|
array('value' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? $association[$fld] : '-null-') : '-unset-',
|
||||||
|
'type' => array_key_exists($fld, $association) ? (isset($association[$fld]) ? gettype($association[$fld]) : '-null-') : '-unset-'),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pr(30,
|
||||||
|
array('checkpoint' => 'Options/Association post-merge',
|
||||||
|
compact('association', 'options'),
|
||||||
|
));
|
||||||
|
|
||||||
|
// For any option that's not already set, use
|
||||||
|
// whatever is specified by the assocation.
|
||||||
|
$options += array_intersect_key($association, $this->_options);
|
||||||
|
|
||||||
|
// Replace all instances of the MODEL_ALIAS variable
|
||||||
|
// tag with the correct model alias.
|
||||||
|
$this->recursive_array_replace("%{MODEL_ALIAS}",
|
||||||
|
$associationAlias,
|
||||||
|
$options['conditions']);
|
||||||
|
|
||||||
|
$this->pr(15,
|
||||||
|
array('checkpoint' => 'Models Established - Check Associations',
|
||||||
|
'primaryModel' => $primaryAlias .' : '. $primaryModel->name,
|
||||||
|
'foreignModel' => $foreignAlias .' : '. $foreignModel->name,
|
||||||
|
compact('type', 'association', 'options'),
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($type === 'hasAndBelongsToMany') {
|
||||||
|
if (isset($association['with']))
|
||||||
|
$linkClass = $association['with'];
|
||||||
|
else
|
||||||
|
$linkClass = Inflector::classify($association['joinTable']);
|
||||||
|
|
||||||
|
$Link =& $Model->{$linkClass};
|
||||||
|
|
||||||
|
if (isset($options['linkalias']))
|
||||||
|
$linkAlias = $options['linkalias'];
|
||||||
|
else
|
||||||
|
$linkAlias = $Link->alias;
|
||||||
|
|
||||||
|
// foreignKey and associationForeignKey can refer to either
|
||||||
|
// the model or the reference, depending on which class
|
||||||
|
// actually defines the association. Make sure to we're
|
||||||
|
// using the foreign keys to point to the right class.
|
||||||
|
if ($associatedThroughReference) {
|
||||||
|
$modelAFK = 'associationForeignKey';
|
||||||
|
$referenceAFK = 'foreignKey';
|
||||||
|
} else {
|
||||||
|
$modelAFK = 'foreignKey';
|
||||||
|
$referenceAFK = 'associationForeignKey';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pr(17,
|
||||||
|
array('checkpoint' => 'Linking HABTM',
|
||||||
|
compact('linkClass', 'linkAlias',
|
||||||
|
'modelAFK', 'referenceAFK'),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Get the foreign key fields (for the link table) directly from
|
||||||
|
// the defined model associations, if they exists. This is the
|
||||||
|
// users direct specification, and therefore definitive if present.
|
||||||
|
$modelLink = $Link->escapeField($association[$modelAFK], $linkAlias);
|
||||||
|
$referenceLink = $Link->escapeField($association[$referenceAFK], $linkAlias);
|
||||||
|
|
||||||
|
// If we haven't figured out the foreign keys, see if there is a
|
||||||
|
// model for the link table, and if it has the appropriate
|
||||||
|
// associations with the two tables we're trying to join.
|
||||||
|
if (empty($modelLink) && isset($Link->belongsTo[$Model->alias]))
|
||||||
|
$modelLink = $Link->escapeField($Link->belongsTo[$Model->alias]['foreignKey'], $linkAlias);
|
||||||
|
if (empty($referenceLink) && isset($Link->belongsTo[$Reference->alias]))
|
||||||
|
$referenceLink = $Link->escapeField($Link->belongsTo[$Reference->alias]['foreignKey'], $linkAlias);
|
||||||
|
|
||||||
|
// We're running quite thin here. None of the models spell
|
||||||
|
// out the appropriate linkages. We'll have to SWAG it.
|
||||||
|
if (empty($modelLink))
|
||||||
|
$modelLink = $Link->escapeField(Inflector::underscore($Model->alias) . '_id', $linkAlias);
|
||||||
|
if (empty($referenceLink))
|
||||||
|
$referenceLink = $Link->escapeField(Inflector::underscore($Reference->alias) . '_id', $linkAlias);
|
||||||
|
|
||||||
|
// Get the primary key from the tables we're joining.
|
||||||
|
$referenceKey = $Reference->escapeField(null, $referenceAlias);
|
||||||
|
$modelKey = $Model->escapeField(null, $modelAlias);
|
||||||
|
|
||||||
|
$this->pr(21,
|
||||||
|
array('checkpoint' => 'HABTM links/keys',
|
||||||
|
array(compact('modelLink', 'modelKey'),
|
||||||
|
compact('referenceLink', 'referenceKey')),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Join the linkage table to our model. We'll use an inner join,
|
||||||
|
// as the whole purpose of the linkage table is to make this
|
||||||
|
// connection. As we are embedding this join, the INNER will not
|
||||||
|
// cause any problem with the overall query, should the user not
|
||||||
|
// be concerned with whether or not the join has any results.
|
||||||
|
// They control that with the 'type' parameter which will be at
|
||||||
|
// the top level join.
|
||||||
|
$options['joins'][] = array('type' => 'INNER',
|
||||||
|
'alias' => $modelAlias,
|
||||||
|
'conditions' => "{$modelKey} = {$modelLink}",
|
||||||
|
'table' => $db->fullTableName($Model, true));
|
||||||
|
|
||||||
|
// Now for the top level join. This will be added into the list
|
||||||
|
// of joins down below, outside of the HABTM specific code.
|
||||||
|
$options['class'] = $linkClass;
|
||||||
|
$options['alias'] = $linkAlias;
|
||||||
|
$options['table'] = $Link->getDataSource()->fullTableName($Link);
|
||||||
|
$options['conditions'][] = "{$referenceLink} = {$referenceKey}";
|
||||||
|
}
|
||||||
|
elseif (isset($association['foreignKey']) && $association['foreignKey']) {
|
||||||
|
$foreignKey = $primaryModel->escapeField($association['foreignKey'], $primaryAlias);
|
||||||
|
$primaryKey = $foreignModel->escapeField($foreignModel->primaryKey, $foreignAlias);
|
||||||
|
|
||||||
|
$this->pr(17,
|
||||||
|
array('checkpoint' => 'Linking due to foreignKey',
|
||||||
|
compact('foreignKey', 'primaryKey'),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Only differentiating to help show the logical flow.
|
||||||
|
// Either way works and this test can be tossed out
|
||||||
|
if (($type === 'hasMany' || $type === 'hasOne') ^ $associatedThroughReference)
|
||||||
|
$options['conditions'][] = "{$primaryKey} = {$foreignKey}";
|
||||||
|
else
|
||||||
|
$options['conditions'][] = "{$foreignKey} = {$primaryKey}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->pr(17,
|
||||||
|
array('checkpoint' => 'Linking with no logic (expecting user defined)',
|
||||||
|
));
|
||||||
|
|
||||||
|
// No Foreign Key... nothing we can do.
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pr(19,
|
||||||
|
array('checkpoint' => 'Conditions',
|
||||||
|
array('options[conditions]' => $options['conditions'],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
if (empty($options['table'])) {
|
||||||
|
$options['table'] = $db->fullTableName($Model, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($options['fields']) || !is_array($options['fields']))
|
||||||
|
$options['fields'] = $db->fields($Model, $modelAlias);
|
||||||
|
elseif (!empty($options['fields']))
|
||||||
|
$options['fields'] = $db->fields($Model, $modelAlias, $options['fields']);
|
||||||
|
|
||||||
|
// When performing a count query, fields are useless.
|
||||||
|
// For everything else, we need to add them into the set.
|
||||||
|
if ($query_type !== 'count')
|
||||||
|
$result['fields'] = array_merge($result['fields'], $options['fields']);
|
||||||
|
|
||||||
|
$result['joins'][] = array_intersect_key($options,
|
||||||
|
array('type' => true,
|
||||||
|
'alias' => true,
|
||||||
|
'table' => true,
|
||||||
|
'joins' => true,
|
||||||
|
'conditions' => true));
|
||||||
|
|
||||||
|
$sublinks = array_diff_key($options, $this->_options);
|
||||||
|
$this->buildQuery($Model, $modelClass, $modelAlias, $query_type, $sublinks, $result);
|
||||||
|
|
||||||
|
$this->pr(19,
|
||||||
|
array('checkpoint' => 'Model Join Complete',
|
||||||
|
compact('referenceAlias', 'modelAlias', 'options', 'result'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->pr(20,
|
||||||
|
array('return' => 'Linkable::buildQuery',
|
||||||
|
compact('referenceAlias'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,13 +3,6 @@ class Contact extends AppModel {
|
|||||||
|
|
||||||
var $displayField = 'display_name';
|
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(
|
var $hasMany = array(
|
||||||
'ContactsMethod',
|
'ContactsMethod',
|
||||||
'ContactsCustomer',
|
'ContactsCustomer',
|
||||||
@@ -48,14 +41,15 @@ class Contact extends AppModel {
|
|||||||
function saveContact($id, $data) {
|
function saveContact($id, $data) {
|
||||||
|
|
||||||
// Establish a display name if not already given
|
// 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']['display_name'] =
|
||||||
(($data['Contact']['first_name'] &&
|
$data['Contact']['last_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) {
|
||||||
: ($data['Contact']['first_name']
|
if (!$data['Contact']['display_name'] && $data['Contact'][$fld])
|
||||||
? $data['Contact']['first_name']
|
$data['Contact']['display_name'] = $data['Contact'][$fld];
|
||||||
: $data['Contact']['last_name']));
|
}
|
||||||
|
|
||||||
// Save the contact data
|
// Save the contact data
|
||||||
$this->create();
|
$this->create();
|
||||||
506
site/models/customer.php
Normal file
506
site/models/customer.php
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
<?php
|
||||||
|
class Customer extends AppModel {
|
||||||
|
|
||||||
|
var $name = 'Customer';
|
||||||
|
var $validate = array(
|
||||||
|
'id' => array('numeric'),
|
||||||
|
'name' => array('notempty'),
|
||||||
|
);
|
||||||
|
|
||||||
|
var $belongsTo = array(
|
||||||
|
'PrimaryContact' => array(
|
||||||
|
'className' => 'Contact',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasMany = array(
|
||||||
|
'CurrentLease' => array(
|
||||||
|
'className' => 'Lease',
|
||||||
|
'conditions' => 'CurrentLease.close_date IS NULL',
|
||||||
|
),
|
||||||
|
'Lease',
|
||||||
|
'StatementEntry',
|
||||||
|
'ContactsCustomer' => array(
|
||||||
|
// It would be nice to claim a dependency here, which would
|
||||||
|
// simplify deletion of a customer. However, for this to work
|
||||||
|
// Cake must have a primaryKey as a single field. This table
|
||||||
|
// makes use of a complex key, so we're out of luck.
|
||||||
|
/* 'dependent' => true, */
|
||||||
|
),
|
||||||
|
|
||||||
|
'Transaction',
|
||||||
|
'Tender',
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasAndBelongsToMany = array(
|
||||||
|
'Contact' => array(
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
//var $default_log_level = 20;
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: accountId
|
||||||
|
* - Returns the account ID for the given customer
|
||||||
|
*/
|
||||||
|
function accountId($id) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$customer = $this->find('first',
|
||||||
|
array('contain' => false,
|
||||||
|
'fields' => array('account_id'),
|
||||||
|
'conditions' => array(array('Customer.id' => $id))));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
|
||||||
|
return $customer['Customer']['account_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: leaseIds
|
||||||
|
* - Returns the lease IDs for the given customer
|
||||||
|
*/
|
||||||
|
function leaseIds($id, $current = false) {
|
||||||
|
$Lease = $current ? 'CurrentLease' : 'Lease';
|
||||||
|
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$customer = $this->find('first',
|
||||||
|
array('contain' =>
|
||||||
|
array($Lease => array('fields' => array('id'))),
|
||||||
|
'fields' => array(),
|
||||||
|
'conditions' => array(array('Customer.id' => $id))));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
|
||||||
|
$ids = array();
|
||||||
|
foreach ($customer[$Lease] AS $lease)
|
||||||
|
$ids[] = $lease['id'];
|
||||||
|
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: securityDeposits
|
||||||
|
* - Returns an array of security deposit entries
|
||||||
|
*/
|
||||||
|
function securityDeposits($id, $query = null) {
|
||||||
|
$this->prEnter(compact('id', 'query'));
|
||||||
|
$this->queryInit($query);
|
||||||
|
|
||||||
|
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||||
|
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||||
|
$this->StatementEntry->Account->securityDepositAccountID());
|
||||||
|
|
||||||
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
|
||||||
|
return $this->prReturn($set);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: securityDepositBalance
|
||||||
|
* - Returns the balance of the customer security deposit(s)
|
||||||
|
*/
|
||||||
|
|
||||||
|
function securityDepositBalance($id, $query = null) {
|
||||||
|
$this->prEnter(compact('id', 'query'));
|
||||||
|
$this->queryInit($query);
|
||||||
|
|
||||||
|
$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: unreconciledCharges
|
||||||
|
* - Returns charges have not yet been fully paid
|
||||||
|
*/
|
||||||
|
|
||||||
|
function unreconciledCharges($id, $query = null) {
|
||||||
|
$this->prEnter(compact('id', 'query'));
|
||||||
|
$this->queryInit($query);
|
||||||
|
|
||||||
|
$query['conditions'][] = array('StatementEntry.customer_id' => $id);
|
||||||
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
|
||||||
|
return $this->prReturn($set);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: saveCustomer
|
||||||
|
* - Saves the customer and related data
|
||||||
|
*/
|
||||||
|
|
||||||
|
function saveCustomer($id, $data, $primary_contact_entry) {
|
||||||
|
|
||||||
|
// Go through each contact, and create new ones as needed
|
||||||
|
foreach ($data['Contact'] AS &$contact) {
|
||||||
|
if (isset($contact['id']))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$I = new Contact();
|
||||||
|
if (!$I->saveContact(null, array('Contact' => $contact)))
|
||||||
|
return false;
|
||||||
|
$contact['id'] = $I->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the primary contact ID based on caller selection
|
||||||
|
$data['Customer']['primary_contact_id']
|
||||||
|
= $data['Contact'][$primary_contact_entry]['id'];
|
||||||
|
|
||||||
|
// Provide a default customer name if not specified
|
||||||
|
if (!$data['Customer']['name']) {
|
||||||
|
$this->Contact->recursive = -1;
|
||||||
|
$pcontact = $this->Contact->read(null, $data['Customer']['primary_contact_id']);
|
||||||
|
$data['Customer']['name'] = $pcontact['Contact']['display_name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the customer data
|
||||||
|
$this->create();
|
||||||
|
if ($id)
|
||||||
|
$this->id = $id;
|
||||||
|
if (!$this->save($data, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$id = $this->id;
|
||||||
|
|
||||||
|
// Appears that $this->save() "helpfully" choses to add in
|
||||||
|
// any missing data fields, populated with default values.
|
||||||
|
// So, after saving is complete, the fields 'lease_count',
|
||||||
|
// 'past_lease_count', and 'current_lease_count' have all
|
||||||
|
// been reset to zero. Gee, thanks Cake...
|
||||||
|
$this->update($id);
|
||||||
|
|
||||||
|
// Remove all associated Customer Contacts, as it ensures
|
||||||
|
// any entries deleted by the user actually get deleted
|
||||||
|
// in the system. We'll recreate the needed ones anyway.
|
||||||
|
// REVISIT <AP>: 20090706
|
||||||
|
// Appears that $this->save() is already doing the
|
||||||
|
// delete. I would have thought this would only happen
|
||||||
|
// on a saveAll??
|
||||||
|
/* $this->ContactsCustomer->deleteAll */
|
||||||
|
/* (array('customer_id' => $id), false); */
|
||||||
|
|
||||||
|
// At this point, since we've saved data to customer,
|
||||||
|
// we'll proceed forward as much as possible, even
|
||||||
|
// if we encounter an error. For now, we'll assume
|
||||||
|
// the operation will succeed.
|
||||||
|
$ret = true;
|
||||||
|
|
||||||
|
// Go through each entry of this customer method
|
||||||
|
foreach ($data['Contact'] AS &$contact) {
|
||||||
|
// Update the ContactsCustomer to reflect the appropriate IDs
|
||||||
|
$contact['ContactsCustomer']['customer_id'] = $id;
|
||||||
|
$contact['ContactsCustomer']['contact_id'] = $contact['id'];
|
||||||
|
|
||||||
|
// Save the relationship between customer and contact
|
||||||
|
$CM = new ContactsCustomer();
|
||||||
|
if (!$CM->save($contact['ContactsCustomer'], false)) {
|
||||||
|
$ret = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the result
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: 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: merge
|
||||||
|
* - Merges two customers into one
|
||||||
|
*/
|
||||||
|
|
||||||
|
function merge($dst_id, $src_id, $contacts) {
|
||||||
|
$this->prEnter(compact('dst_id', 'src_id', 'contacts'));
|
||||||
|
|
||||||
|
// Get the entire list of destination customer contacts
|
||||||
|
$dst_contacts = array();
|
||||||
|
$result = $this->find('all',
|
||||||
|
array('link' => array('ContactsCustomer'),
|
||||||
|
'fields' => array('ContactsCustomer.contact_id', 'ContactsCustomer.type'),
|
||||||
|
'conditions' => array(array('id' => $dst_id,
|
||||||
|
'ContactsCustomer.active' => true))));
|
||||||
|
foreach ($result AS $contact) {
|
||||||
|
$dst_contacts[$contact['ContactsCustomer']['contact_id']] = $contact['ContactsCustomer'];
|
||||||
|
}
|
||||||
|
$this->pr(17, compact('dst_contacts'));
|
||||||
|
|
||||||
|
// Get the entire list of source customer contacts
|
||||||
|
$src_contacts = array();
|
||||||
|
$result = $this->find('all',
|
||||||
|
array('link' => array('ContactsCustomer'),
|
||||||
|
'fields' => array('ContactsCustomer.contact_id', 'ContactsCustomer.type'),
|
||||||
|
'conditions' => array(array('id' => $src_id,
|
||||||
|
'ContactsCustomer.active' => true))));
|
||||||
|
foreach ($result AS $contact) {
|
||||||
|
$src_contacts[$contact['ContactsCustomer']['contact_id']] = $contact['ContactsCustomer'];
|
||||||
|
}
|
||||||
|
$this->pr(17, compact('src_contacts'));
|
||||||
|
|
||||||
|
// Verify the contacts list are all valid source customer contacts
|
||||||
|
foreach ($contacts AS $contact_id) {
|
||||||
|
if (!array_key_exists($contact_id, $src_contacts))
|
||||||
|
return $this->prReturn(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any contacts which are already destination customer contacts
|
||||||
|
$new_contacts = array_diff($contacts, array_keys($dst_contacts));
|
||||||
|
$all_contacts = array_merge($new_contacts, array_keys($dst_contacts));
|
||||||
|
$this->pr(17, compact('new_contacts', 'all_contacts'));
|
||||||
|
|
||||||
|
// For now, we'll assume the operation will succeed.
|
||||||
|
$ret = true;
|
||||||
|
|
||||||
|
// Add each desired source customer contact to the destination customer
|
||||||
|
foreach ($new_contacts AS $contact_id) {
|
||||||
|
$CM = new ContactsCustomer();
|
||||||
|
if (!$CM->save(array('customer_id' => $dst_id)
|
||||||
|
+ $src_contacts[$contact_id], false)) {
|
||||||
|
$ret = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->Lease->updateAll
|
||||||
|
(array('Lease.customer_id' => $dst_id),
|
||||||
|
array('Lease.customer_id' => $src_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->Tender->updateAll
|
||||||
|
(array('Tender.customer_id' => $dst_id),
|
||||||
|
array('Tender.customer_id' => $src_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->StatementEntry->updateAll
|
||||||
|
(array('StatementEntry.customer_id' => $dst_id),
|
||||||
|
array('StatementEntry.customer_id' => $src_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->Transaction->updateAll
|
||||||
|
(array('Transaction.customer_id' => $dst_id),
|
||||||
|
array('Transaction.customer_id' => $src_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make sure our lease counts, etc are correct
|
||||||
|
$this->update($dst_id);
|
||||||
|
|
||||||
|
// Delete the old customer
|
||||||
|
$this->pr(12, compact('src_id'), "Delete Customer");
|
||||||
|
$this->delete($src_id);
|
||||||
|
|
||||||
|
// Delete all the orphaned customers
|
||||||
|
foreach (array_diff(array_keys($src_contacts), $all_contacts) AS $contact_id) {
|
||||||
|
// Delete un-used or duplicate contacts
|
||||||
|
// REVISIT <AP> 20100702:
|
||||||
|
// Not sure if we really want to do this.
|
||||||
|
// On the one hand, they're probably really redundant,
|
||||||
|
// and only clutter up the list of all contacts. On the
|
||||||
|
// other hand, it destroys data, not only losing the
|
||||||
|
// history, but making it difficult to recover if the
|
||||||
|
// merge is a mistake. Additionally, we need to do
|
||||||
|
// extra checking to ensure that the contact is not
|
||||||
|
// in use by some other customer.
|
||||||
|
// We need some sort of Contact.deleted field...
|
||||||
|
$this->pr(12, compact('contact_id'), "Delete Contact");
|
||||||
|
$this->Contact->delete($contact_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, delete all customer contact relationships
|
||||||
|
$this->ContactsCustomer->deleteAll
|
||||||
|
(array('customer_id' => $src_id), false);
|
||||||
|
|
||||||
|
// Return the result
|
||||||
|
return $this->prReturn($ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: updateLeaseCount
|
||||||
|
* - Updates the internal lease count
|
||||||
|
*/
|
||||||
|
|
||||||
|
function updateLeaseCount($id) {
|
||||||
|
$this->id = $id;
|
||||||
|
|
||||||
|
$lease_count =
|
||||||
|
$this->find('count',
|
||||||
|
array('link' => array('Lease' => array('type' => 'INNER')),
|
||||||
|
'conditions' => array('Customer.id' => $id)));
|
||||||
|
$current_count =
|
||||||
|
$this->find('count',
|
||||||
|
array('link' => array('CurrentLease' => array('type' => 'INNER')),
|
||||||
|
'conditions' => array('Customer.id' => $id)));
|
||||||
|
|
||||||
|
$this->saveField('lease_count', $lease_count);
|
||||||
|
$this->saveField('current_lease_count', $current_count);
|
||||||
|
$this->saveField('past_lease_count', $lease_count - $current_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: balance
|
||||||
|
* - Returns the balance of money owed on the lease
|
||||||
|
*/
|
||||||
|
|
||||||
|
function balance($id) {
|
||||||
|
$stats = $this->stats($id);
|
||||||
|
return $stats['balance'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: stats
|
||||||
|
* - Returns summary data from the requested customer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function stats($id = null, $query = null) {
|
||||||
|
//$this->prFunctionLevel(20);
|
||||||
|
$this->prEnter(compact('id', 'query'));
|
||||||
|
if (!$id)
|
||||||
|
return $this->prExit(null);
|
||||||
|
|
||||||
|
$this->queryInit($query);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
851
site/models/lease.php
Normal file
851
site/models/lease.php
Normal file
@@ -0,0 +1,851 @@
|
|||||||
|
<?php
|
||||||
|
class Lease extends AppModel {
|
||||||
|
|
||||||
|
var $belongsTo = array(
|
||||||
|
'LeaseType',
|
||||||
|
'Unit',
|
||||||
|
'Customer',
|
||||||
|
'LateSchedule',
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasMany = array(
|
||||||
|
'StatementEntry',
|
||||||
|
);
|
||||||
|
|
||||||
|
//var $default_log_level = array('log' => 30, 'show' => 30);
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: securityDeposits
|
||||||
|
* - Returns an array of security deposit entries
|
||||||
|
*/
|
||||||
|
function securityDeposits($id, $query = null) {
|
||||||
|
$this->prEnter(compact('id', 'query'));
|
||||||
|
$this->queryInit($query);
|
||||||
|
|
||||||
|
$query['conditions'][] = array('StatementEntry.lease_id' => $id);
|
||||||
|
$query['conditions'][] = array('StatementEntry.account_id' =>
|
||||||
|
$this->StatementEntry->Account->securityDepositAccountID());
|
||||||
|
|
||||||
|
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, false, true);
|
||||||
|
return $this->prReturn($set);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: securityDepositBalance
|
||||||
|
* - Returns the balance of the lease security deposit(s)
|
||||||
|
*/
|
||||||
|
|
||||||
|
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: releaseSecurityDeposits
|
||||||
|
* - Releases all security deposits associated with this lease.
|
||||||
|
* That simply makes a disbursement out of them, which can be used
|
||||||
|
* to pay outstanding customer charges, or simply to become
|
||||||
|
* a customer surplus (customer credit).
|
||||||
|
*/
|
||||||
|
function releaseSecurityDeposits($id, $stamp = null, $query = null) {
|
||||||
|
//$this->prFunctionLevel(30);
|
||||||
|
$this->prEnter(compact('id', 'stamp', 'query'));
|
||||||
|
|
||||||
|
$secdeps = $this->securityDeposits($id, $query);
|
||||||
|
$secdeps = $secdeps['entries'];
|
||||||
|
$this->pr(20, compact('secdeps'));
|
||||||
|
|
||||||
|
// If there are no paid security deposits, then
|
||||||
|
// we can consider all security deposits released.
|
||||||
|
if (count($secdeps) == 0)
|
||||||
|
return $this->prReturn(true);
|
||||||
|
|
||||||
|
// Build a transaction
|
||||||
|
$release = array('Transaction' => array(), 'Entry' => array());
|
||||||
|
$release['Transaction']['stamp'] = $stamp;
|
||||||
|
$release['Transaction']['comment'] = "Security Deposit Release";
|
||||||
|
foreach ($secdeps AS $charge) {
|
||||||
|
if ($charge['StatementEntry']['type'] !== 'CHARGE')
|
||||||
|
die("INTERNAL ERROR: SECURITY DEPOSIT IS NOT CHARGE");
|
||||||
|
|
||||||
|
// Since security deposits are being released, this also means
|
||||||
|
// any unpaid (or only partially paid) security deposit should
|
||||||
|
// have the remaining balance reversed.
|
||||||
|
if ($charge['StatementEntry']['balance'] > 0)
|
||||||
|
$this->StatementEntry->reverse($charge['StatementEntry']['id'], true, $stamp);
|
||||||
|
|
||||||
|
$release['Entry'][] =
|
||||||
|
array('amount' => $charge['StatementEntry']['reconciled'],
|
||||||
|
'account_id' => $this->StatementEntry->Account->securityDepositAccountID(),
|
||||||
|
'comment' => "Released Security Deposit",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$customer_id = $secdeps[0]['StatementEntry']['customer_id'];
|
||||||
|
$lease_id = $secdeps[0]['StatementEntry']['lease_id'];
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: rentLastCharges
|
||||||
|
* - Returns a list of rent charges from this lease that
|
||||||
|
* do not have sequential followup charges. Under normal
|
||||||
|
* circumstances, there would only be one entry, which is
|
||||||
|
* the most recent rent charge. However, it's possible
|
||||||
|
* that there are several, indicating a problem with lease.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function rentLastCharges($id) {
|
||||||
|
$this->prEnter(compact('id'));
|
||||||
|
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
|
||||||
|
$entries = $this->find
|
||||||
|
('all',
|
||||||
|
array('link' =>
|
||||||
|
array(// Models
|
||||||
|
'StatementEntry',
|
||||||
|
|
||||||
|
'SEx' =>
|
||||||
|
array('class' => 'StatementEntry',
|
||||||
|
'fields' => array(),
|
||||||
|
'conditions' => array
|
||||||
|
('SEx.lease_id = StatementEntry.lease_id',
|
||||||
|
'SEx.type' => 'CHARGE',
|
||||||
|
'SEx.account_id' => $rent_account_id,
|
||||||
|
'SEx.reverse_transaction_id IS NULL',
|
||||||
|
'SEx.effective_date = DATE_ADD(StatementEntry.through_date, INTERVAL 1 day)',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
//'fields' => array('id', 'amount', 'effective_date', 'through_date'),
|
||||||
|
'fields' => array(),
|
||||||
|
'conditions' => array(array('Lease.id' => $id),
|
||||||
|
array('StatementEntry.type' => 'CHARGE'),
|
||||||
|
array('StatementEntry.account_id' => $rent_account_id),
|
||||||
|
array('StatementEntry.reverse_transaction_id IS NULL'),
|
||||||
|
array('SEx.id' => null),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: rentChargeGaps
|
||||||
|
* - Checks for gaps in rent charges
|
||||||
|
*/
|
||||||
|
|
||||||
|
function rentChargeGaps($id) {
|
||||||
|
$this->prEnter(compact('id'));
|
||||||
|
$entries = $this->rentLastCharges($id);
|
||||||
|
if ($entries && count($entries) > 1)
|
||||||
|
return $this->prReturn(true);
|
||||||
|
return $this->prReturn(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: rentChargeThrough
|
||||||
|
* - Determines the date that rent has been charged through
|
||||||
|
* Returns one of:
|
||||||
|
* null: There are gaps in the charges
|
||||||
|
* false: There are not yet any charges
|
||||||
|
* date: The date rent has been charged through
|
||||||
|
*/
|
||||||
|
|
||||||
|
function rentChargeThrough($id) {
|
||||||
|
$this->prEnter(compact('id'));
|
||||||
|
$entries = $this->rentLastCharges($id);
|
||||||
|
if (!$entries)
|
||||||
|
return $this->prReturn(false);
|
||||||
|
if (count($entries) != 1)
|
||||||
|
return $this->prReturn(null);
|
||||||
|
return $this->prReturn($entries[0]['StatementEntry']['through_date']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: rentPaidThrough
|
||||||
|
* - Determines the date of the first unpaid rent
|
||||||
|
*/
|
||||||
|
|
||||||
|
function rentPaidThrough($id) {
|
||||||
|
$this->prEnter(compact('id'));
|
||||||
|
$rent_account_id = $this->StatementEntry->Account->rentAccountID();
|
||||||
|
|
||||||
|
// First, see if we can find any unpaid entries. Of course,
|
||||||
|
// the first unpaid entry gives us a very direct indication
|
||||||
|
// of when the customer is paid up through, which is 1 day
|
||||||
|
// prior to the effective date of that first unpaid charge.
|
||||||
|
$rent = $this->StatementEntry->reconciledSet
|
||||||
|
('CHARGE',
|
||||||
|
array('fields' =>
|
||||||
|
array('StatementEntry.*',
|
||||||
|
'DATE_SUB(StatementEntry.effective_date, INTERVAL 1 DAY) AS paid_through',
|
||||||
|
),
|
||||||
|
|
||||||
|
'conditions' =>
|
||||||
|
array(array('StatementEntry.lease_id' => $id),
|
||||||
|
array('StatementEntry.account_id' => $rent_account_id),
|
||||||
|
array('StatementEntry.reverse_transaction_id IS NULL'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'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'),
|
||||||
|
),
|
||||||
|
|
||||||
|
'order' => array('StatementEntry.through_date DESC'),
|
||||||
|
),
|
||||||
|
false);
|
||||||
|
$this->pr(20, $rent, "Paid rent");
|
||||||
|
|
||||||
|
if ($rent['entries'])
|
||||||
|
return $this->prReturn($rent['entries'][0]['StatementEntry']['through_date']);
|
||||||
|
|
||||||
|
|
||||||
|
// After all that, having found that there are no unpaid
|
||||||
|
// charges, and in fact, no paid charges either, we cannot
|
||||||
|
// possibly say when the customer is paid through.
|
||||||
|
return $this->prReturn(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: 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(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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: 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 1 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'");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: moveIn
|
||||||
|
* - Moves the specified customer into the specified lease
|
||||||
|
*/
|
||||||
|
|
||||||
|
function moveIn($customer_id, $unit_id,
|
||||||
|
$deposit = null, $rent = null,
|
||||||
|
$stamp = null, $comment = null)
|
||||||
|
{
|
||||||
|
$this->prEnter(compact('customer_id', 'unit_id',
|
||||||
|
'deposit', 'rent', 'stamp', 'comment'));
|
||||||
|
|
||||||
|
$lt = $this->LeaseType->find('first',
|
||||||
|
array('conditions' =>
|
||||||
|
array('code' => 'SL')));
|
||||||
|
|
||||||
|
// Use NOW if not given a movein date
|
||||||
|
if (!isset($stamp))
|
||||||
|
$stamp = date('Y-m-d G:i:s');
|
||||||
|
|
||||||
|
if (!$comment)
|
||||||
|
$comment = null;
|
||||||
|
|
||||||
|
if (!isset($deposit) || !isset($rent)) {
|
||||||
|
$rates = $this->Unit->find
|
||||||
|
('first',
|
||||||
|
array('contain' =>
|
||||||
|
array('UnitSize' =>
|
||||||
|
array('deposit', 'rent'),
|
||||||
|
),
|
||||||
|
'fields' => array('deposit', 'rent'),
|
||||||
|
'conditions' => array('Unit.id' => $unit_id),
|
||||||
|
));
|
||||||
|
|
||||||
|
$deposit =
|
||||||
|
(isset($deposit)
|
||||||
|
? $deposit
|
||||||
|
: (isset($rates['Unit']['deposit'])
|
||||||
|
? $rates['Unit']['deposit']
|
||||||
|
: (isset($rates['UnitSize']['deposit'])
|
||||||
|
? $rates['UnitSize']['deposit']
|
||||||
|
: 0)));
|
||||||
|
|
||||||
|
$rent =
|
||||||
|
(isset($rent)
|
||||||
|
? $rent
|
||||||
|
: (isset($rates['Unit']['rent'])
|
||||||
|
? $rates['Unit']['rent']
|
||||||
|
: (isset($rates['UnitSize']['rent'])
|
||||||
|
? $rates['UnitSize']['rent']
|
||||||
|
: 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Save this new lease.
|
||||||
|
$this->create();
|
||||||
|
if (!$this->save(array('lease_type_id' => $lt['LeaseType']['id'],
|
||||||
|
'unit_id' => $unit_id,
|
||||||
|
'customer_id' => $customer_id,
|
||||||
|
'lease_date' => $stamp,
|
||||||
|
'movein_date' => $stamp,
|
||||||
|
'deposit' => $deposit,
|
||||||
|
'rent' => $rent,
|
||||||
|
'comment' => $comment), false)) {
|
||||||
|
return $this->prReturn(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the lease number to be the same as the lease ID
|
||||||
|
$this->id;
|
||||||
|
$this->saveField('number', $this->id);
|
||||||
|
|
||||||
|
// Update the current lease count for the customer
|
||||||
|
$this->Customer->updateLeaseCount($customer_id);
|
||||||
|
|
||||||
|
// Update the unit status
|
||||||
|
$this->Unit->updateStatus($unit_id, 'OCCUPIED');
|
||||||
|
|
||||||
|
// REVISIT <AP>: 20090702
|
||||||
|
// We need to assess the security deposit charge,
|
||||||
|
// and probably rent as well. Rent, however, will
|
||||||
|
// require user parameters to indicate whether it
|
||||||
|
// was waived, pro-rated, etc.
|
||||||
|
|
||||||
|
// Return the new lease ID
|
||||||
|
return $this->prReturn($this->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: moveOut
|
||||||
|
* - Moves the customer out of the specified lease
|
||||||
|
*/
|
||||||
|
|
||||||
|
function moveOut($id, $status = 'VACANT',
|
||||||
|
$stamp = null, $close = 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');
|
||||||
|
|
||||||
|
// Reset the data
|
||||||
|
$this->create();
|
||||||
|
$this->id = $id;
|
||||||
|
|
||||||
|
// Set the customer move-out date
|
||||||
|
$this->data['Lease']['moveout_date'] = $stamp;
|
||||||
|
|
||||||
|
// Save it!
|
||||||
|
$this->save($this->data, false);
|
||||||
|
|
||||||
|
// Release the security deposit(s)
|
||||||
|
$this->releaseSecurityDeposits($id, $stamp);
|
||||||
|
|
||||||
|
// Close the lease, if so requested
|
||||||
|
if ($close)
|
||||||
|
$this->close($id, $stamp);
|
||||||
|
|
||||||
|
// Update the current lease count for the customer
|
||||||
|
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||||
|
|
||||||
|
// Finally, update the unit status
|
||||||
|
$this->recursive = -1;
|
||||||
|
$this->read();
|
||||||
|
$this->Unit->updateStatus($this->data['Lease']['unit_id'], $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: close
|
||||||
|
* - Closes the lease to further action
|
||||||
|
*/
|
||||||
|
|
||||||
|
function close($id, $stamp = null) {
|
||||||
|
$this->prEnter(compact('id', 'stamp'));
|
||||||
|
|
||||||
|
if (!$this->closeable($id))
|
||||||
|
return $this->prReturn(false);
|
||||||
|
|
||||||
|
// Reset the data
|
||||||
|
$this->create();
|
||||||
|
$this->id = $id;
|
||||||
|
|
||||||
|
// Use NOW if not given a moveout date
|
||||||
|
if (!isset($stamp))
|
||||||
|
$stamp = date('Y-m-d G:i:s');
|
||||||
|
|
||||||
|
// Set the close date
|
||||||
|
$this->data['Lease']['close_date'] = $stamp;
|
||||||
|
|
||||||
|
// Save it!
|
||||||
|
$this->save($this->data, false);
|
||||||
|
|
||||||
|
// Update the current lease count for the customer
|
||||||
|
$this->Customer->updateLeaseCount($this->field('customer_id'));
|
||||||
|
|
||||||
|
return $this->prReturn(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: closeable
|
||||||
|
* - Indicates whether or not the lease can be closed
|
||||||
|
*/
|
||||||
|
|
||||||
|
function closeable($id) {
|
||||||
|
$this->prEnter(compact('id'));
|
||||||
|
|
||||||
|
$this->recursive = -1;
|
||||||
|
$this->read(null, $id);
|
||||||
|
|
||||||
|
// We can't close a lease that's still in use
|
||||||
|
if (!isset($this->data['Lease']['moveout_date']))
|
||||||
|
return $this->prReturn(false);
|
||||||
|
|
||||||
|
// We can't close a lease that's already closed
|
||||||
|
if (isset($this->data['Lease']['close_date']))
|
||||||
|
return $this->prReturn(false);
|
||||||
|
|
||||||
|
// A lease can only be closed if there are no outstanding
|
||||||
|
// 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 $this->prReturn(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: refund
|
||||||
|
* - Marks any lease balance as payable to the customer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function refund($id, $stamp = null) {
|
||||||
|
$this->prEnter(compact('id'));
|
||||||
|
$balance = $this->balance($id);
|
||||||
|
|
||||||
|
if ($balance >= 0)
|
||||||
|
return $this->prReturn(array('error' => true));
|
||||||
|
|
||||||
|
$balance *= -1;
|
||||||
|
|
||||||
|
// Build a transaction
|
||||||
|
$refund = array('Transaction' => array(), 'Entry' => array());
|
||||||
|
$refund['Transaction']['stamp'] = $stamp;
|
||||||
|
$refund['Transaction']['comment'] = "Lease Refund";
|
||||||
|
|
||||||
|
$refund['Entry'][] =
|
||||||
|
array('amount' => $balance);
|
||||||
|
|
||||||
|
$result = $this->StatementEntry->Transaction->addRefund
|
||||||
|
($refund, null, $id);
|
||||||
|
|
||||||
|
return $this->prReturn($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: 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']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: stats
|
||||||
|
* - Returns summary data from the requested lease.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function stats($id = null, $query = null) {
|
||||||
|
$this->prEnter(compact('id', 'query'));
|
||||||
|
if (!$id)
|
||||||
|
return $this->prReturn(null);
|
||||||
|
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
179
site/models/ledger.php
Normal file
179
site/models/ledger.php
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<?php
|
||||||
|
class Ledger extends AppModel {
|
||||||
|
|
||||||
|
var $belongsTo = array(
|
||||||
|
'Account',
|
||||||
|
'PriorLedger' => array('className' => 'Ledger'),
|
||||||
|
'CloseTransaction' => array('className' => 'Transaction'),
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasMany = array(
|
||||||
|
'Transaction',
|
||||||
|
'LedgerEntry',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: accountID
|
||||||
|
* - Returns the account ID for the given ledger
|
||||||
|
*/
|
||||||
|
function accountID($id) {
|
||||||
|
$this->cacheQueries = true;
|
||||||
|
$item = $this->find('first', array
|
||||||
|
('link' => array('Account'),
|
||||||
|
'conditions' => array('Ledger.id' => $id),
|
||||||
|
));
|
||||||
|
$this->cacheQueries = false;
|
||||||
|
//pr(compact('id', 'item'));
|
||||||
|
return $item['Account']['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: currentLedgerID
|
||||||
|
* - Returns the current ledger ID of the account for the given ledger.
|
||||||
|
*/
|
||||||
|
function currentLedgerID($id) {
|
||||||
|
return $this->Account->currentLedgerID($this->accountID($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: closeLedger
|
||||||
|
* - Closes the current ledger, and returns a fresh one
|
||||||
|
*/
|
||||||
|
function closeLedgers($ids) {
|
||||||
|
$ret = array('new_ledger_ids' => array());
|
||||||
|
|
||||||
|
$entries = array();
|
||||||
|
foreach ($ids AS $id) {
|
||||||
|
// Query stats to get the balance forward
|
||||||
|
$stats = $this->stats($id);
|
||||||
|
|
||||||
|
// Populate fields from the current ledger
|
||||||
|
$this->recursive = -1;
|
||||||
|
$this->id = $id;
|
||||||
|
$this->read();
|
||||||
|
|
||||||
|
// Build a new ledger to replace the current one
|
||||||
|
$this->data['Ledger']['id'] = null;
|
||||||
|
$this->data['Ledger']['close_transaction_id'] = null;
|
||||||
|
$this->data['Ledger']['prior_ledger_id'] = $id;
|
||||||
|
$this->data['Ledger']['comment'] = null;
|
||||||
|
++$this->data['Ledger']['sequence'];
|
||||||
|
$this->data['Ledger']['name'] =
|
||||||
|
($this->data['Ledger']['account_id'] .
|
||||||
|
'-' .
|
||||||
|
$this->data['Ledger']['sequence']);
|
||||||
|
|
||||||
|
// Save the new ledger
|
||||||
|
$this->id = null;
|
||||||
|
if (!$this->save($this->data, false))
|
||||||
|
return array('error' => true, 'new_ledger_data' => $this->data) + $ret;
|
||||||
|
$ret['new_ledger_ids'][] = $this->id;
|
||||||
|
|
||||||
|
$entries[] = array('old_ledger_id' => $id,
|
||||||
|
'new_ledger_id' => $this->id,
|
||||||
|
'amount' => $stats['balance']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the close
|
||||||
|
$result = $this->Transaction->addClose(array('Transaction' => array(),
|
||||||
|
'Ledger' => $entries));
|
||||||
|
$ret['Transaction'] = $result;
|
||||||
|
if ($result['error'])
|
||||||
|
return array('error' => true) + $ret;
|
||||||
|
|
||||||
|
return $ret + array('error' => false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: debitCreditFields
|
||||||
|
* - Returns the fields necessary to determine whether the queried
|
||||||
|
* entries are a debit, or a credit, and also the effect each have
|
||||||
|
* on the overall balance of the ledger.
|
||||||
|
*/
|
||||||
|
function debitCreditFields($sum = false, $balance = true,
|
||||||
|
$entry_name = 'LedgerEntry', $account_name = 'Account') {
|
||||||
|
return $this->LedgerEntry->debitCreditFields
|
||||||
|
($sum, $balance, $entry_name, $account_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: ledgerEntries
|
||||||
|
* - Returns an array of ledger entries that belong to a given
|
||||||
|
* ledger. There is extra work done to establish debit/credit
|
||||||
|
*/
|
||||||
|
function ledgerEntries($ids, $query = null) {
|
||||||
|
if (empty($ids))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$entries = $this->LedgerEntry->find
|
||||||
|
('all', array
|
||||||
|
('link' => array('Ledger' => array('Account')),
|
||||||
|
'fields' => array_merge(array("LedgerEntry.*"),
|
||||||
|
$this->LedgerEntry->debitCreditFields()),
|
||||||
|
'conditions' => array('LedgerEntry.ledger_id' => $ids),
|
||||||
|
));
|
||||||
|
|
||||||
|
//pr(compact('entries'));
|
||||||
|
return $entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: stats
|
||||||
|
* - Returns summary data from the requested ledger.
|
||||||
|
*/
|
||||||
|
function stats($id, $query = null) {
|
||||||
|
if (!$id)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
$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;
|
||||||
|
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
177
site/models/ledger_entry.php
Normal file
177
site/models/ledger_entry.php
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<?php
|
||||||
|
class LedgerEntry extends AppModel {
|
||||||
|
|
||||||
|
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(
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasAndBelongsToMany = array(
|
||||||
|
// The Debit half of the double entry matching THIS Credit (if it is one)
|
||||||
|
'DebitEntry' => array(
|
||||||
|
'className' => 'LedgerEntry',
|
||||||
|
'joinTable' => 'double_entries',
|
||||||
|
'linkalias' => 'DDE',
|
||||||
|
'foreignKey' => 'credit_entry_id',
|
||||||
|
'associationForeignKey' => 'debit_entry_id',
|
||||||
|
),
|
||||||
|
|
||||||
|
// The Credit half of the double entry matching THIS Debit (if it is one)
|
||||||
|
'CreditEntry' => array(
|
||||||
|
'className' => 'LedgerEntry',
|
||||||
|
'joinTable' => 'double_entries',
|
||||||
|
'linkalias' => 'CDE',
|
||||||
|
'foreignKey' => 'debit_entry_id',
|
||||||
|
'associationForeignKey' => 'credit_entry_id',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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 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' : ''),
|
||||||
|
|
||||||
|
($sum ? 'SUM(' : '') .
|
||||||
|
"IF({$entry_name}.crdr = 'CREDIT'," .
|
||||||
|
" {$entry_name}.amount, NULL)" .
|
||||||
|
($sum ? ')' : '') . ' AS credit' . ($sum ? 's' : ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
if ($sum)
|
||||||
|
$fields[] = "COUNT({$entry_name}.id) AS entries";
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: verifyLedgerEntry
|
||||||
|
* - Verifies consistenty of new ledger entry data
|
||||||
|
* (not in a pre-existing ledger entry)
|
||||||
|
*/
|
||||||
|
function verifyLedgerEntry($entry, $tender = null) {
|
||||||
|
/* pr(array("LedgerEntry::verifyLedgerEntry()" */
|
||||||
|
/* => compact('entry', 'tender'))); */
|
||||||
|
|
||||||
|
if (empty($entry['account_id']) ||
|
||||||
|
empty($entry['crdr']) ||
|
||||||
|
empty($entry['amount'])
|
||||||
|
) {
|
||||||
|
/* pr(array("LedgerEntry::verifyLedgerEntry()" */
|
||||||
|
/* => "Entry verification failed")); */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = null, $query = null, $set = null) {
|
||||||
|
$this->queryInit($query);
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
site/models/site.php
Normal file
37
site/models/site.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
class Site extends AppModel {
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
784
site/models/statement_entry.php
Normal file
784
site/models/statement_entry.php
Normal file
@@ -0,0 +1,784 @@
|
|||||||
|
<?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: outstandingDebits
|
||||||
|
* - Determines all debit types that have not yet been resolved.
|
||||||
|
* The name is a bit dumb, but it means any statement entry type
|
||||||
|
* that a positive customer balance could be used to offset. In
|
||||||
|
* other words, entries that are still in need of matching
|
||||||
|
* disbursements. Most notably, this means charges but could
|
||||||
|
* also mean things like refunds as well.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function outstandingDebits($query = null, $customer_id = null,
|
||||||
|
$lease_id = null, $debit_types = null)
|
||||||
|
{
|
||||||
|
$this->prEnter(compact('query', 'lease_id',
|
||||||
|
'customer_id', 'charge_ids',
|
||||||
|
'debit_types'));
|
||||||
|
$this->queryInit($query);
|
||||||
|
|
||||||
|
if (empty($debit_types))
|
||||||
|
$debit_types = $this->debitTypes();
|
||||||
|
|
||||||
|
if (!empty($customer_id))
|
||||||
|
$query['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
|
||||||
|
|
||||||
|
if (!empty($lease_id))
|
||||||
|
$query['conditions'][] = array('StatementEntry.lease_id' => $lease_id);
|
||||||
|
|
||||||
|
/* if (isset($charge_ids)) { */
|
||||||
|
/* // REVISIT <AP> 20100330: */
|
||||||
|
/* // Not using $query here, as this code was extracted from its */
|
||||||
|
/* // original location in assignCredits, and so I'm keeping the */
|
||||||
|
/* // logic consistent. It does seem, however, that we shouldn't */
|
||||||
|
/* // be ignoring $query if passed in. I'm sure this won't be */
|
||||||
|
/* // looked at until someone _does_ pass $query in (and it break), */
|
||||||
|
/* // so hopefully at that time, we can understand what needs to */
|
||||||
|
/* // happen in that case (requirements are not clear at present). */
|
||||||
|
/* $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); */
|
||||||
|
/* } */
|
||||||
|
|
||||||
|
if (empty($query['order']))
|
||||||
|
$query['order'] = 'StatementEntry.effective_date ASC';
|
||||||
|
|
||||||
|
$debits = array();
|
||||||
|
foreach ($debit_types AS $dtype) {
|
||||||
|
$rset = $this->reconciledSet($dtype, $query, true);
|
||||||
|
$entries = $rset['entries'];
|
||||||
|
$debits = array_merge($debits, $entries);
|
||||||
|
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->prReturn($debits);
|
||||||
|
}
|
||||||
|
|
||||||
|
function outstandingCharges($query = null, $customer_id = null, $lease_id = null) {
|
||||||
|
return $this->outstandingDebits($query, $customer_id, $lease_id, array('CHARGE'));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* 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($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;
|
||||||
|
if (!empty($customer_id))
|
||||||
|
$lquery['conditions'][] = array('StatementEntry.customer_id' => $customer_id);
|
||||||
|
|
||||||
|
$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, using either the specific set
|
||||||
|
// of charges given, or all outstanding charges based on the
|
||||||
|
// query, customer and/or lease
|
||||||
|
if (!empty($charge_ids)) {
|
||||||
|
$this->INTERNAL_ERROR("PERHAPS IMPLEMENTED - THOUGH NEVER TESTED");
|
||||||
|
$lquery = $query;
|
||||||
|
$lquery['conditions'][] = array('StatementEntry.id' => $charge_ids);
|
||||||
|
$charges = $this->reconciledSet('CHARGE', $query, true);
|
||||||
|
} else {
|
||||||
|
$charges = $this->outstandingDebits($query, $customer_id, $lease_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = round(min($charge['balance'], $credit['balance']), 2);
|
||||||
|
if (!isset($credit['applied']))
|
||||||
|
$credit['applied'] = 0;
|
||||||
|
|
||||||
|
$credit['applied'] = round($credit['applied'] + $disbursement_amount, 2);
|
||||||
|
$credit['balance'] = round($credit['balance'] - $disbursement_amount, 2);
|
||||||
|
|
||||||
|
$this->pr(20, compact('credit', 'disbursement_amount'),
|
||||||
|
($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'] = round($charge['balance'] - $disbursement_amount, 2);
|
||||||
|
$this->pr(20, compact('charge', 'disbursement_amount'),
|
||||||
|
($charge['balance'] > 0 ? 'Unfinished' : 'Fully Paid') . ' Charge');
|
||||||
|
|
||||||
|
if ($charge['balance'] < 0)
|
||||||
|
die("HOW DID WE GET A NEGATIVE CHARGE AMOUNT?");
|
||||||
|
}
|
||||||
|
// 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
1373
site/models/transaction.php
Normal file
1373
site/models/transaction.php
Normal file
File diff suppressed because it is too large
Load Diff
248
site/models/unit.php
Normal file
248
site/models/unit.php
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
<?php
|
||||||
|
class Unit extends AppModel {
|
||||||
|
|
||||||
|
var $name = 'Unit';
|
||||||
|
var $validate = array(
|
||||||
|
'id' => array('numeric'),
|
||||||
|
'unit_size_id' => array('numeric'),
|
||||||
|
'name' => array('notempty'),
|
||||||
|
'sort_order' => array('numeric'),
|
||||||
|
'walk_order' => array('numeric'),
|
||||||
|
'deposit' => array('money'),
|
||||||
|
'amount' => array('money')
|
||||||
|
);
|
||||||
|
|
||||||
|
var $belongsTo = array(
|
||||||
|
'UnitSize',
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasOne = array(
|
||||||
|
'CurrentLease' => array(
|
||||||
|
'className' => 'Lease',
|
||||||
|
'conditions' => 'CurrentLease.moveout_date IS NULL',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var $hasMany = array(
|
||||||
|
'Lease',
|
||||||
|
);
|
||||||
|
|
||||||
|
//var $default_log_level = array('log' => 30, 'show' => 15);
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* helpers: status enumerations
|
||||||
|
*/
|
||||||
|
|
||||||
|
function statusEnums() {
|
||||||
|
static $status_enums;
|
||||||
|
if (!isset($status_enums))
|
||||||
|
$status_enums = $this->getEnumValues('status');
|
||||||
|
return $status_enums;
|
||||||
|
}
|
||||||
|
|
||||||
|
function activeStatusEnums() {
|
||||||
|
return array_diff_key($this->statusEnums(), array(''=>1, 'DELETED'=>1));
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusValue($enum) {
|
||||||
|
$enums = $this->statusEnums();
|
||||||
|
return $enums[$enum];
|
||||||
|
}
|
||||||
|
|
||||||
|
function occupiedEnumValue() {
|
||||||
|
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) .
|
||||||
|
' AND ' .
|
||||||
|
($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, $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) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
**************************************************************************
|
||||||
|
* function: stats
|
||||||
|
* - Returns summary data from the requested customer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function stats($id = null) {
|
||||||
|
if (!$id)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Get the basic information necessary
|
||||||
|
$unit = $this->find('first',
|
||||||
|
array('contain' => array
|
||||||
|
('Lease' => array
|
||||||
|
('fields' => array('Lease.id')),
|
||||||
|
|
||||||
|
'CurrentLease' => array
|
||||||
|
('fields' => array('CurrentLease.id'))),
|
||||||
|
|
||||||
|
'conditions' => array
|
||||||
|
(array('Unit.id' => $id)),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Get the stats for the current lease
|
||||||
|
$stats['CurrentLease'] = $this->Lease->stats($unit['CurrentLease']['id']);
|
||||||
|
|
||||||
|
// Sum the stats for all leases together
|
||||||
|
foreach ($unit['Lease'] AS $lease) {
|
||||||
|
$this->statsMerge($stats['Lease'], $this->Lease->stats($lease['id']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the collection
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
64
site/models/unit_size.php
Normal file
64
site/models/unit_size.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
class UnitSize extends AppModel {
|
||||||
|
|
||||||
|
var $belongsTo =
|
||||||
|
array(
|
||||||
|
'UnitType',
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
50
site/models/unit_type.php
Normal file
50
site/models/unit_type.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
class UnitType extends AppModel {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user