Compare commits

..

64 Commits

Author SHA1 Message Date
abijah
90ecbda541 Several minor changes including a couple minor bugfixes.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@592 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-17 02:15:02 +00:00
abijah
4afe0bd77b Added logic to prevent a double entry where the credit and debit record in the exact same ledger. There is no known reason why we would need to record such an entry, even though it would not be incorrect to do so.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@591 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 23:41:44 +00:00
abijah
8440a7c833 Checkin a support function that is needed by the Transaction model since several checkins ago.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@590 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 22:51:08 +00:00
abijah
00d509f23d Minor formatting issue for the Transaction.type enums
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@589 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 22:48:00 +00:00
abijah
080c82fc10 More bug fixes, especially around reversals of charges that have been paid from something other than customer money (such as waivers). I suspect it still will not work correctly for concession, depending on your point of view. Either concession should not count as customer credit, since they never paid that money, or it should since they were told they would get $X off, and upon charge reversal, they no longer get the credit. It seems they would only get the credit when reversing a mistaken charge, in which case, the user can manually provide a new concession.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@588 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 22:35:02 +00:00
abijah
1b02be19f0 Adjusted to match the naming in the database
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@587 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 22:31:06 +00:00
abijah
366d59a5e6 Added a customer credit account.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@586 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 19:16:42 +00:00
abijah
2634cf824a Another checkin screwup where we accidentally specified ledger.php instead of ledger_entry.php
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@585 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 19:14:37 +00:00
abijah
1429fe720b Added mechanism to lookup the NSF tender from the bounce transaction.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@584 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 19:00:45 +00:00
abijah
754cdb8522 Fixed bug with NSF, which was failing to locate the nsf_entry_id. To resolve this, I added the original data into the return structure for each item, instead of just the created IDs. Also, added NsfTender to the Transaction model to locate which Tender an NSF transaction relates to.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@583 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 18:59:16 +00:00
abijah
a1a68f3209 Updated SQL scratchpad
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@582 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 16:48:44 +00:00
abijah
8843d24baa Added SQL scratchpad
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@581 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 16:37:54 +00:00
abijah
892618db36 I need to check again in the morning, but it seem like this is finally what we need for reversals. Of course, we're still using A/P as a temporary solution. If we do indeed move forward with this sort of solution (dedicated customer credit account), then we'll have to create a new account fairly soon.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@580 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 09:31:19 +00:00
abijah
3decfff33b Fixed is_bool / is_boolean bug
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@579 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 06:12:46 +00:00
abijah
945221d565 Changed reversals to create an explicit credit BEFORE assigning credits to outstanding charges. This ensures that charges are paid from the customer surplus account and we don't end up with bizarre disbursements, like having Rent paid from Damage or whatever the reversed charge was.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@578 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 05:17:09 +00:00
abijah
a968d7abe6 Changed the explicit receipt amount to be driven from stats instead of the assignCredits return value. This is in anticipation of creating an explicit credit directly, without even calling assignCredits. I'll do that for reversal on the next checkin.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@577 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 04:43:34 +00:00
abijah
8b6b8884f7 More good progress on reversals. There is still an issue where after reversing, a disbursement can be applied to a new charge from the old charge account, instead of the surplus account. I'll work on that next.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@576 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 03:56:36 +00:00
abijah
14805190fc Added back in the logic to simply update an existing explicit credit instead of creating an additional one. At the moment, I can't think of a scenario for this other than when reversing charges, which is still broken. So, until I make progress on that, it's not even clear this changes is needed, let alone whether it works.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@575 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 02:22:45 +00:00
abijah
a1bdecfcaa More tweaks to the addTransaction algorithm, working on a solid plan for customer surplus
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@574 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 02:04:13 +00:00
abijah
4896834a96 Added the account back into the grid, since an invoice could have charges across several accounts.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@573 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 01:06:14 +00:00
abijah
a9c3c40053 Bug fix for matching ledger entry to double entry
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@572 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 00:49:21 +00:00
abijah
4125d7ba16 Quick and dirty hack to get double entries to show again.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@571 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-16 00:45:11 +00:00
abijah
26d9b1bc38 Modified the charges list on the receipt page to have the query performed directly as part of returning the grid data, instead of an intermediary call to get the unreconciled entries first. This not only ensures consistent data, but is quicker, cleaner, and fixes the customer balance bug that seems to have been introduced as part of the change to how customer surplus is handled (although it could have been resolved using the old technique just as well).
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@570 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-15 23:04:57 +00:00
abijah
f30e536e47 Modified to automatically calculate the crdr field, if not specified
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@569 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-15 20:43:00 +00:00
abijah
96a030e340 First pass changes for a dedicated customer credit account. This checkin is just refactoring.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@568 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-15 19:50:31 +00:00
abijah
460e9a2e64 Branch to experiment with keeping all customer surplus items in a dedicated liability account.
git-svn-id: file:///svn-source/pmgr/branches/surplus_account_20090815@567 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-15 19:48:30 +00:00
abijah
6e63365604 A couple bugfixes and some more tweaks to how reversals are handled.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@566 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-15 16:47:55 +00:00
abijah
4f85dc243e Getting closer on the reversal issue. There is definitely more testing to do, and some tweaks as well, but this may be approximately what we will finally settle on.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@565 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-15 03:11:16 +00:00
abijah
30f755cf42 Fixed negative numbers
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@564 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-15 01:22:16 +00:00
abijah
170ba466de Cleaned up several places regarding redirect and rendering during development for debug purposes. Also, added a link to the intended redirect target when we've rendered instead. These changes haven't been tested, hopefully they are benign.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@563 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:49:42 +00:00
abijah
d2d1bb3fc4 Change to how reversals are handled. In the process, I've tried to solidify _exactly_ what addTransaction will do, since it was becoming a house of cards of sorts. It was using special logic to decide things like whether to add ledger entries, statement entries, or both, whether to assignCredits afterwards, whether the generated receipt was to be considered a credit, and so on. Consequently, modifications to any calling function (addInvoice, addWaiver, etc) would often require addTransaction modifications, which would turn around and break all of the other calling functions. So, that embedded logic has been removed from addTransaction, and the rules of what addTransaction should do are now defined by the callers. This change is DEFINTELY not complete, as it probably has several bugs, and it DOES NOT YET WORK for reversals. I need a clean baseline to move forward from though, and this checkin approximates where we need to go.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@562 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:31:56 +00:00
abijah
d024d333d2 Moved the INTERNAL_ERROR calls to use the class function instead of the global one.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@561 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:21:21 +00:00
abijah
778bb43895 Modified INTERNAL_ERROR to support inclusion of the blank layout, since all the javascript is lost otherwise. This should only matter for development. Also, fixed a bug with rendering when redirect is called but headers have already been output.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@560 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:10:16 +00:00
abijah
41321481c7 Minor tweak to grid caption and display of the entry id instead of the transaction id.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@559 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:07:50 +00:00
abijah
fe9f6ce949 Allow the dollar sign in the input box, since it will get stripped off later anyway.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@558 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:07:02 +00:00
abijah
f81bfdecc2 Fixed the currency function to always return in dollar amounts, and never in just cents, as the core currency function does.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@557 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:06:17 +00:00
abijah
8dd6fc957d Fixed url generation since it was not propogating the admin/dev routing items. Also modified the invoice at move in to exclude the customer list, and to transition directly to the receipt page afterwards.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@556 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:05:36 +00:00
abijah
d92acf12de Dropped the security level for viewing statement and ledger entries
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@555 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:01:25 +00:00
abijah
43d1d2ccf5 Fixed bug with statement entry counts when statement_entry_id is set
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@554 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 21:00:21 +00:00
abijah
c06399cf86 Added support for deleting (destroying) a transaction. This is strictly development/super-admin type functionality.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@553 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 20:58:50 +00:00
abijah
d1187f9bdd Added the http request to the internal error box, since it will be necessary to help track down any reported errors.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@552 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 18:13:12 +00:00
abijah
b7a77757f9 Fixed a couple URL problems, including redirect, which was previously entirely unable to utilize our AppHelper for the url generation. Added a couple menu items into the Admin section. Changed debug to be OFF for any route other than the 'dev' one.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@551 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-14 18:11:33 +00:00
abijah
5f199d97fe Fixed the datetime bug
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@550 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 22:23:45 +00:00
abijah
fc292e3366 Moved paid-through from the detail box, which is semi-static information, to the info box, which holds data that changes with time (i.e. the so called pertinent information).
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@549 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 22:09:27 +00:00
abijah
94e300a129 Modified the formatted date/age results to be span encapsulated for later css formatting.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@548 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 22:08:43 +00:00
abijah
791b2d8ab1 Got rid of LATE as a unit status, as it did not represent an physical condition. The logic was moved to Lease, where it is a much better fit. The sitemap still presents LATE units, as it is a useful view, but the underlying logic is driven from Lease, not Unit. This checkin also includes a small feature change to how late charges are assessed, as well as a menu item to kick off the charge assessments (both accidentally wedged in to this changeset).
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@547 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 20:55:19 +00:00
abijah
091920d80a Changed the default of the debug flag when generating grid data. The original intention was to have it debug by default, so that debugging would be on if the url were manually typed in. That has never been needed though, and this change not only results in cleaner logic, it makes sure we have no issues with routing.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@546 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 20:07:49 +00:00
abijah
1511986ed0 Changed the custom route variables, so that the dev route could set the admin flag and not screw up the routing. The AppController now checks the routing params to set the dev/admin flags.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@545 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 16:43:07 +00:00
abijah
1e88e1fce2 Added admin and development routes, which should help while in transition to user security.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@544 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 16:09:45 +00:00
abijah
a2014a916e Added cached fields for charged_through and paid_through dates of a lease. Also, added the ability to dynamically determine whether a unit is late or not. In reality, we really need this to be part of the lease, not one of the status types for unit. The sitemap, however, is driven from unit information, so it's not clear whether we should move that information or not.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@543 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-13 02:37:37 +00:00
abijah
bd52030984 Changed naming to match that on the lease page
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@542 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-12 23:16:36 +00:00
abijah
f23726783e Fixed bug causing a $0.00 security deposit charge on the move-in invoice
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@541 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-12 23:16:17 +00:00
abijah
1d27b4dcb5 Many bug fixes, found while entering real facility data. Most are quite minor, although there was a functionality change to assignCredits, to support concessions without assigning them to anything other than rent. I've found that there is a glaring problem with charge reversals, which don't work correctly with the collected report. Of course, we're deleting prior disbursements, which obviously needs to be rectified.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@540 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-12 09:37:49 +00:00
abijah
e74f8987d9 Made viewing the deposit slip the default action when viewing the list of deposit transactions.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@539 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 18:25:40 +00:00
abijah
9c55a047a8 Implemented very crude attempt at security privleges. This is not really intended to be security, just a quick and dirty mechanism to avoid prying eyes. More robust security is left to future implementation.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@538 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 18:22:21 +00:00
abijah
6f2038f7b0 Added ability to customize the action on grid links
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@537 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 17:30:12 +00:00
abijah
ec22f4b003 Removed the Signed column from the leases grid unless requested
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@536 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 17:04:21 +00:00
abijah
70629e360b Added the Batch Number to ACH entries. This is the unique number of each initiated batch, and is what would show up on a bank statement or used by the bank to track down an ACH transaction.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@535 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 16:22:09 +00:00
abijah
fc4e812d67 More tweaking with grids that need to be reloaded immediately after page load. Fixed a bug with the statement_entry balance field, as there were two of them.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@534 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 15:56:40 +00:00
abijah
b6ee958c35 Marked off several to-do items
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@533 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 15:21:50 +00:00
abijah
f82df229f2 Modified the statement entries grid, as well as the underlying controller, to support a couple new fields, 'applied' and 'balance'.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@532 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 07:27:56 +00:00
abijah
96c499786c Finally have a (slightly kludgy) fix for keeping the 'collected' grid from being rendered before ready.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@531 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 07:24:52 +00:00
abijah
52e0181bfb Fixed bug with display on the bank deposit page, caused by reusing a class name in the edit tender page.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@530 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 07:21:34 +00:00
abijah
b65c5c1dbf Added ability to rename columns on a case by case basis
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@529 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-08-11 07:15:04 +00:00
417 changed files with 27093 additions and 1070 deletions

View File

@@ -471,7 +471,6 @@ CREATE TABLE `pmgr_units` (
'DIRTY',
'VACANT',
'OCCUPIED',
'LATE', -- NOT SURE
'LOCKED',
'LIENED')
NOT NULL DEFAULT 'VACANT',
@@ -725,6 +724,9 @@ CREATE TABLE `pmgr_leases` (
`notice_received_date` DATE DEFAULT NULL,
`close_date` DATE DEFAULT NULL,
`charge_through_date` DATE DEFAULT NULL,
`paid_through_date` DATE DEFAULT NULL,
`deposit` FLOAT(12,2) DEFAULT NULL,
`rent` FLOAT(12,2) DEFAULT NULL,
@@ -872,47 +874,46 @@ CREATE TABLE `pmgr_accounts` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
LOCK TABLES `pmgr_accounts` WRITE;
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`)
VALUES
('EQUITY', 'Equity', 1),
('LIABILITY', 'Loan', 1);
INSERT INTO `pmgr_accounts` (`type`, `name`)
VALUES
('ASSET', 'A/R' ),
-- REVISIT <AP>: 20090710 : We don't really need NSF, as it
-- will always run a zero balance. However, it will help
-- us identify how serious the NSF situation is.
('LIABILITY', 'A/P' ),
('LIABILITY', 'Customer Credit' );
INSERT INTO `pmgr_accounts` (`type`, `name`, `receipts`)
VALUES
('ASSET', 'Cash', 1),
('ASSET', 'Check', 1),
('ASSET', 'Money Order', 1),
('ASSET', 'ACH', 1),
('EXPENSE', 'Concession', 1);
INSERT INTO `pmgr_accounts` (`type`, `name`)
VALUES
('ASSET', 'NSF' ),
('LIABILITY', 'A/P' );
INSERT INTO `pmgr_accounts` (`type`, `name`, `receipts`, `refunds`)
VALUES
('ASSET', 'Cash', 1, 0),
('ASSET', 'Check', 1, 0),
('ASSET', 'Money Order', 1, 0),
('ASSET', 'ACH', 1, 0),
('ASSET', 'Closing', 0, 0), -- REVISIT <AP>: Temporary
('EXPENSE', 'Concession', 1, 0),
('EXPENSE', 'Waiver', 0, 0);
INSERT INTO `pmgr_accounts` (`type`, `name`, `refunds`, `deposits`)
VALUES
-- REVISIT <AP>: 20090710 : We probably don't really want petty cash depositable.
-- This is just for testing our deposit code
('ASSET', 'Petty Cash', 1, 1);
('EXPENSE', 'Waiver' ),
('EXPENSE', 'Bad Debt' );
INSERT INTO `pmgr_accounts` (`type`, `name`, `invoices`)
VALUES
('LIABILITY', 'Tax', 1),
('LIABILITY', 'Tax', 0),
('LIABILITY', 'Security Deposit', 1),
('INCOME', 'Rent', 1),
('INCOME', 'Late Charge', 1),
('INCOME', 'NSF Charge', 1),
('INCOME', 'Cleaning', 1),
('INCOME', 'Damage', 1);
INSERT INTO `pmgr_accounts` (`type`, `name`, `deposits`, `refunds`)
VALUES
('ASSET', 'Bank', 1, 1);
INSERT INTO `pmgr_accounts` (`type`, `name`)
VALUES
('EXPENSE', 'Bad Debt' ),
('EXPENSE', 'Maintenance' );
INSERT INTO `pmgr_accounts` (`type`, `name`, `refunds`)
VALUES
('ASSET', 'Petty Cash', 1);
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`, `deposits`, `refunds`)
VALUES
('ASSET', 'Bank', 6, 1, 1);
INSERT INTO `pmgr_accounts` (`type`, `name`, `level`)
VALUES
('ASSET', 'Closing', 6),
('LIABILITY', 'Loan', 1),
('EQUITY', 'Equity', 1);
UNLOCK TABLES;
@@ -970,6 +971,7 @@ CREATE TABLE `pmgr_transactions` (
'CREDIT_NOTE', -- Inverse of Sales Invoice
'PAYMENT', -- Actual payment
'DEPOSIT',
'WITHDRAWAL',
'CLOSE', -- Essentially an internal (not accounting) transaction
-- 'CREDIT',
-- 'REFUND',
@@ -1119,6 +1121,9 @@ CREATE TABLE `pmgr_statement_entries` (
-- Allow the disbursement to reconcile against the charge
`charge_entry_id` INT(10) UNSIGNED DEFAULT NULL,
-- The transaction that reversed this charge, if any
`reverse_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
`comment` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)

85
db/scratch.sql Normal file
View File

@@ -0,0 +1,85 @@
-- Delete bad transaction(s)
DELETE M
FROM
pmgr_ledger_entries LE,
pmgr_tenders M
WHERE
M.ledger_entry_id = LE.id AND
LE.transaction_id
IN (467);
DELETE LE
FROM
pmgr_ledger_entries LE
WHERE
LE.transaction_id
IN (467);
DELETE SE
FROM
pmgr_statement_entries SE
WHERE
SE.transaction_id
IN (467);
DELETE T
FROM
pmgr_transactions T
WHERE
T.id
IN (467);
-- Delete bad transaction, one variable setting
SET @tid = 467;
DELETE M FROM pmgr_ledger_entries LE, pmgr_tenders M
WHERE M.ledger_entry_id = LE.id AND LE.transaction_id = @tid;
DELETE LE FROM pmgr_ledger_entries LE
WHERE LE.transaction_id = @tid;
DELETE SE FROM pmgr_statement_entries SE
WHERE SE.transaction_id = @tid;
DELETE T FROM pmgr_transactions T
WHERE T.id = @tid;
-- Determine economic conditions
SELECT `status`, COUNT(id), SUM(rent) FROM pmgr_units
GROUP BY `status` WITH ROLLUP;
-- Check that transaction totals add up correctly
SELECT T.id, T.type, T.amount,
-- T.type, A.type, E.crdr,
SUM(IF(E.account_id = T.account_id,
IF(A.type IN ('ASSET','EXPENSE') XOR E.crdr='DEBIT',-1,1),0)
*E.amount) AS Tamt,
SUM(IF(E.account_id = T.account_id,
0,IF(A.type IN ('ASSET','EXPENSE') XOR E.crdr='DEBIT',-1,1))
*E.amount) AS Oamt,
COUNT(E.id) AS Ecnt
FROM pmgr_transactions T
-- LEFT JOIN pmgr_statement_entries E ON E.transaction_id = T.id
LEFT JOIN pmgr_ledger_entries E ON E.transaction_id = T.id
LEFT JOIN pmgr_accounts A ON A.id = T.account_id -- E.account_id
-- WHERE
-- E.account_id != T.account_id
GROUP BY T.id
HAVING
(T.type = 'INVOICE' AND Tamt <> T.amount)
OR
(T.type <> 'INVOICE' AND Oamt <> T.amount)
OR
(Tamt * -1 <> Oamt)
-- Verify that statement entries all have the correct type
SELECT SE.id, SE.type, T.id, T.type
FROM pmgr_statement_entries SE
LEFT JOIN pmgr_transactions T ON T.id = SE.transaction_id
WHERE
((T.type = 'RECEIPT' OR T.type = 'CREDIT_NOTE') AND
SE.type NOT IN ('DISBURSEMENT', 'WAIVER', 'REVERSAL', 'WRITEOFF', 'SURPLUS')
)
OR
((T.type = 'INVOICE' OR T.type = 'PAYMENT') AND
SE.type NOT IN ('CHARGE', 'PAYMENT', 'REFUND')
)
-- catch other types not considered in this query
OR T.type NOT IN ('RECEIPT', 'CREDIT_NOTE', 'INVOICE', 'PAYMENT')

View File

@@ -555,9 +555,7 @@ foreach my $tender_name ('Cash', 'Check', 'Money Order', 'ACH',
($name1, $name_field) = ('Money Order Number', 'data1')
if ($tender_name eq 'Money Order');
# REVISIT <AP>: 20090810
# Make data3 be the confirmation number?
($name1, $name2) = ('Routing Number', 'Account Number')
($name1, $name2, $name3, $name_field) = ('Routing Number', 'Account Number', 'Batch Number', 'data3')
if ($tender_name eq 'ACH');
($name1, $name2) = ('Debit Card Number', 'Expiration Date')

View File

@@ -38,29 +38,95 @@ class AppController extends Controller {
var $helpers = array('Html', 'Form', 'Javascript', 'Format', 'Time', 'Grid');
var $components = array('DebugKit.Toolbar');
function __construct() {
$this->params['dev'] = false;
$this->params['admin'] = false;
parent::__construct();
}
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' => 'Deposits', 'url' => array('controller' => 'transactions', 'action' => 'deposit')),
array('name' => 'Debug', 'header' => true),
array('name' => 'Un-Nuke', 'url' => '#', 'htmlAttributes' =>
array('onclick' => '$(".pr-section").show(); return false;')),
array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index')),
array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index')),
//array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger')),
//array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data')),
);
// Stupid Cake... our constructor sets admin/dev,
// but cake stomps it somewhere along the way
// after constructing the CakeError controller.
if ($this->name === 'CakeError') {
$this->params['dev'] = false;
$this->params['admin'] = false;
}
$menu = array();
$menu[] = array('name' => 'Common', 'header' => true);
$menu[] = array('name' => 'Site Map', 'url' => array('controller' => 'maps', 'action' => 'view', 1));
$menu[] = array('name' => 'Units', 'url' => array('controller' => 'units', 'action' => 'index'));
$menu[] = array('name' => 'Leases', 'url' => array('controller' => 'leases', 'action' => 'index'));
$menu[] = array('name' => 'Customers', 'url' => array('controller' => 'customers', 'action' => 'index'));
$menu[] = array('name' => 'Deposits', 'url' => array('controller' => 'transactions', 'action' => 'deposit'));
if ($this->params['admin']) {
$menu[] = array('name' => 'Admin', 'header' => true);
$menu[] = array('name' => 'Accounts', 'url' => array('controller' => 'accounts', 'action' => 'index'));
$menu[] = array('name' => 'Contacts', 'url' => array('controller' => 'contacts', 'action' => 'index'));
$menu[] = array('name' => 'Ledgers', 'url' => array('controller' => 'ledgers', 'action' => 'index'));
$menu[] = array('name' => 'Tenders', 'url' => array('controller' => 'tenders', 'action' => 'index'));
$menu[] = array('name' => 'Transactions', 'url' => array('controller' => 'transactions', 'action' => 'index'));
$menu[] = array('name' => 'Ldgr Entries', 'url' => array('controller' => 'ledger_entries', 'action' => 'index'));
$menu[] = array('name' => 'Stmt Entries', 'url' => array('controller' => 'statement_entries', 'action' => 'index'));
$menu[] = array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger'));
$menu[] = array('name' => 'Assess Charges', 'url' => array('controller' => 'leases', 'action' => 'assess_all'));
}
if ($this->params['dev']) {
$menu[] = array('name' => 'Development', 'header' => true);
$menu[] = array('name' => 'Un-Nuke', 'url' => '#', 'htmlAttributes' =>
array('onclick' => '$(".pr-section").show(); return false;'));
$menu[] = array('name' => 'New Ledgers', 'url' => array('controller' => 'accounts', 'action' => 'newledger'));
//array('name' => 'RESET DATA', 'url' => array('controller' => 'accounts', 'action' => 'reset_data'));
}
return $menu;
}
function beforeFilter() {
$this->params['dev'] =
(!empty($this->params['dev_route']));
$this->params['admin'] =
(!empty($this->params['admin_route']) || !empty($this->params['dev_route']));
if (!$this->params['dev'])
Configure::write('debug', '0');
}
function beforeRender() {
$this->set('sidemenu', $this->sideMenuLinks());
}
function redirect($url, $status = null, $exit = true) {
// OK, since the controller will not be able to
// utilize our overriden url function in AppHelper,
// we'll have to do it manually here.
App::import('Helper', 'Html');
$url = HtmlHelper::url($url, true);
if (headers_sent()) {
// If we've already sent the headers, it's because
// we're debugging, and our debug output has gotten
// out before the redirect. That's probably a good
// thing, as we don't typically want pages to be
// jerked out from under us while trying to read
// the debug output. So, since we can't redirect
// anyway, we may as well go with the flow and just
// render this page instead, using an empty template
$this->set('message',
("Intended redirect:<P><BR>" .
'<A HREF="'.$url.'">'.$url.'</A>'));
echo $this->render('/empty');
if ($exit)
$this->_stop();
}
return parent::redirect($url, $status, $exit);
}
function reset_data() {
$this->layout = null;
$this->autoLayout = false;
@@ -162,16 +228,10 @@ class AppController extends Controller {
}
function gridDataSetup(&$params) {
// Assume we're debugging.
// The actual grid request will set this to false
$debug = true;
// Debug only if requested
$params['debug'] = !empty($this->passedArgs['debug']);
if (isset($this->passedArgs['debug']))
$debug = $this->passedArgs['debug'];
$params['debug'] = $debug;
if ($debug) {
if ($params['debug']) {
ob_start();
}
else {
@@ -653,7 +713,7 @@ class AppController extends Controller {
foreach ($records AS &$record) {
// Add the calculated fields (if any), to the model fields
if (isset($record[0])) {
$record[$model_alias] += $record[0];
$record[$model_alias] = $record[0] + $record[$model_alias];
unset($record[0]);
}
}
@@ -695,9 +755,12 @@ class AppController extends Controller {
if (isset($params['post']['nolinks']))
return;
App::import('Helper', 'Html');
foreach ($links AS $table => $fields) {
$special = array('controller', 'id');
$special = array('controller', 'action', 'id');
$controller = Inflector::pluralize(Inflector::underscore($table));
$action = 'view';
$id = 'id';
extract(array_intersect_key($fields, array_flip($special)));
foreach ($records AS &$record) {
@@ -712,9 +775,9 @@ class AppController extends Controller {
//$params['linkrecord'][] = compact('table', 'field', 'id', 'controller', 'record');
$record[$table][$field] =
'<A HREF="' .
Router::url(array('controller' => $controller,
'action' => 'view',
$record[$table][$id])) .
HtmlHelper::url(array('controller' => $controller,
'action' => $action,
$record[$table][$id])) .
'">' .
$record[$table][$field] .
'</A>';
@@ -804,5 +867,15 @@ class AppController extends Controller {
echo " <cell><![CDATA[$data]]></cell>\n";
}
function INTERNAL_ERROR($msg, $depth = 0) {
INTERNAL_ERROR($msg, false, $depth+1);
$this->render_empty();
$this->_stop();
}
function render_empty() {
$this->render('/empty');
}
}
?>

View File

@@ -37,5 +37,14 @@ App::import('Core', 'Helper');
* @subpackage cake.cake
*/
class AppHelper extends Helper {
function url($url = null, $full = false) {
foreach(array('admin_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);
}
}
?>

View File

@@ -402,6 +402,10 @@ class AppModel extends Model {
}
function filter_null($array) {
return array_diff_key($array, array_filter($array, 'is_null'));
}
function recursive_array_replace($find, $replace, &$data) {
if (!isset($data))
return;
@@ -476,4 +480,12 @@ class AppModel extends Model {
return date('Y-m-d', strtotime($dateString));
}
function INTERNAL_ERROR($msg, $depth = 0) {
INTERNAL_ERROR($msg, false, $depth+1);
echo $this->requestAction(array('controller' => 'accounts',
'action' => 'render_empty'),
array('return', 'bare' => false)
);
$this->_stop();
}
}

View File

@@ -32,16 +32,17 @@
*
*/
function INTERNAL_ERROR($message) {
echo '<DIV class="internal-error" style="color:#000; background:#c22; padding:0.5em 1.5em 0.5em 1.5em;">';
echo '<H1 style="margin-bottom:0.2em">INTERNAL ERROR:</H1>';
echo '<H2 style="margin-top:0; margin-left:1.5em">' . $message . '</H2>';
echo '<H4>This error was not caused by anything that you did wrong.';
echo '<BR>It is a problem within the application itself and should be reported to the administrator.</H4>';
function INTERNAL_ERROR($message, $exit = true, $drop = 0) {
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
echo "<HR>Stack Trace:<OL>";
$trace = debug_backtrace(false);
echo '<HR style="margin-top:1.0em; margin-bottom:0.5em;">' . "\nStack Trace:\n";
echo '<OL style="margin-left:1.5em";>' . "\n";
$trace = array_slice(debug_backtrace(false), $drop);
for ($i = 0; $i < count($trace); ++$i) {
$bline = $trace[$i]['line'];
$bfile = $trace[$i]['file'];
@@ -58,10 +59,16 @@ function INTERNAL_ERROR($message) {
echo("<LI>$bfile:$bline (" . ($bclas ? "$bclas::$bfunc" : "entry point") . ")</LI>\n");
}
echo '</OL>';
echo "</OL>\n";
echo '<HR style="margin-top:1.0em; margin-bottom:0.5em;">' . "\nHTTP Request:\n";
echo '<P><PRE style="color:#000; background:#c22;">' . "\n";
print_r($_REQUEST);
echo "\n</PRE>\n";
echo '</DIV>';
die();
if ($exit)
die();
}
/**

View File

@@ -32,6 +32,20 @@
* It's hardcoded to map #1, but at some point we'll implement
* a login mechanism and the default path will be to log on instead.
*/
Router::connect('/', array('controller' => 'maps', 'action' => 'view', '1'));
Router::connect('/', array('controller' => 'maps', 'action' => 'view', '1'));
/*
* Route for admin functionality
*/
Router::connect('/admin/:controller/:action/*',
array('action' => null, 'admin_route' => true)
);
/*
* Route for development functionality
*/
Router::connect('/dev/:controller/:action/*',
array('action' => null, 'dev_route' => true)
);
?>

View File

@@ -90,6 +90,10 @@ class AccountsController extends AppController {
$conditions[] = array('Account.type' => strtoupper($params['action']));
}
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
$conditions[] = array('Account.level >=' => 10);
return $conditions;
}
@@ -139,12 +143,9 @@ class AccountsController extends AppController {
$account = $this->Account->read(null, $id);
$account = $account['Account'];
$payment_accounts = $this->Account->collectableAccounts();
//$payment_accounts[$this->Account->nameToID('Closing')] = 'Closing';
//$payment_accounts[$this->Account->nameToID('Equity')] = 'Equity';
//$payment_accounts[$id] = 'Reversals';
$default_accounts = array_diff_key($this->Account->receiptAccounts(),
array($this->Account->concessionAccountID() => 1));
$accounts = $this->Account->collectableAccounts();
$payment_accounts = $accounts['all'];
$default_accounts = $accounts['default'];
$this->set(compact('payment_accounts', 'default_accounts'));
$title = ($account['name'] . ': Collected Report');
@@ -160,12 +161,6 @@ class AccountsController extends AppController {
*/
function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid Item.', true));
$this->redirect(array('action'=>'index'));
}
// Get details about the account and its ledgers (no ledger entries yet)
$account = $this->Account->find
('first',
array('contain' =>
@@ -177,14 +172,18 @@ class AccountsController extends AppController {
array('CloseTransaction' => array
('order' => array('CloseTransaction.stamp' => 'DESC'))),
),
'conditions' => array(array('Account.id' => $id)),
'conditions' => array(array('Account.id' => $id),
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
array('Account.level >=' => 10),
),
)
);
// Get all ledger entries of the CURRENT ledger
$entries = $this->Account->ledgerEntries($id);
//pr(compact('entries'));
$account['CurrentLedger']['LedgerEntry'] = $entries;
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);
@@ -202,8 +201,4 @@ class AccountsController extends AppController {
$this->set(compact('account', 'title', 'stats'));
}
function tst($id) {
//$entries = $this->Account->($id);
pr($entries);
}
}

View File

@@ -126,8 +126,6 @@ class ContactsController extends AppController {
// Now that the work is done, let the user view the updated contact
$this->redirect(array('action'=>'view', $this->data['Contact']['id']));
//$this->render('/empty');
return;
}
if ($id) {

View File

@@ -287,23 +287,20 @@ class CustomersController extends AppController {
if (isset($this->params['form']['cancel'])) {
if (isset($this->data['Customer']['id']))
$this->redirect(array('action'=>'view', $this->data['Customer']['id']));
else
$this->redirect(array('action'=>'index'));
return;
$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']));
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
@@ -320,17 +317,12 @@ class CustomersController extends AppController {
pr("CUSTOMER SAVE FAILED");
}
// If existing customer, then view it. Otherwise, since
// this is a new customer, go to the move in screen.
// If existing customer, then view it.
if ($this->data['Customer']['id'])
$this->redirect(array('action'=>'view', $this->Customer->id));
else
$this->redirect(array('action'=>'move_in', $this->Customer->id));
// For debugging, only if the redirects above have been
// commented out, otherwise this section isn't reached.
$this->render('/empty');
return;
// Since this is a new customer, go to the move in screen.
$this->redirect(array('action'=>'move_in', $this->Customer->id));
}
if ($id) {
@@ -379,20 +371,9 @@ class CustomersController extends AppController {
$this->Customer->recursive = -1;
$customer = $this->Customer->read(null, $id);
$customer = $customer['Customer'];
$unreconciled = $this->Customer->unreconciledCharges($id);
//pr($unreconciled);
$charges = $unreconciled['entries'];
$stats = $unreconciled['summary']['Charge'];
// Kludge until we update receipt to have the unpaid
// charges grid generated from a dynamic query instead
// of simply pre-providing the list of charge IDs
foreach ($charges AS &$charge)
$charge['id'] = $charge['StatementEntry']['id'];
}
else {
$customer = null;
$charges = array();
$stats = array('balance' => 0);
}
$TT = new TenderType();
@@ -401,7 +382,7 @@ class CustomersController extends AppController {
$this->set(compact('payment_types', 'default_type'));
$title = ($customer['name'] . ': Receipt Entry');
$this->set(compact('customer', 'charges', 'stats', 'title'));
$this->set(compact('customer', 'title'));
}
@@ -472,50 +453,4 @@ class CustomersController extends AppController {
$this->render('/transactions/bad_debt');
}
/**************************************************************************
**************************************************************************
**************************************************************************
* action: unreconciled
* - 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->unreconciledCharges($id);
foreach ($unreconciled['entries'] AS &$entry)
$entry = array_intersect_key($entry['StatementEntry'],
array('id'=>1));
$unreconciled = array('entries' =>
array('entry' => $unreconciled['entries'],
'balance' => $unreconciled['summary']['balance']));
// XML Helper will dump an empty tag if the array is empty
if (empty($unreconciled['entries']['entry']))
unset($unreconciled['entries']['entry']);
/* pr(compact('unreconciled')); */
/* echo htmlspecialchars($xml->serialize($unreconciled)); */
/* $this->render('/fake'); */
$opts = array();
//$opts['format'] = 'tags';
echo $xml->header();
echo $xml->serialize($unreconciled, $opts);
}
}

View File

@@ -36,6 +36,28 @@ class DoubleEntriesController extends AppController {
'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['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['CreditLedger'] = $entry['Ledger'];
unset($entry['Ledger']);
// Prepare to render.
$title = "Double Ledger Entry #{$entry['DoubleEntry']['id']}";
$this->set(compact('entry', 'title'));

View File

@@ -3,10 +3,11 @@
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')),
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' => 'Delinquent', 'url' => array('controller' => 'leases', 'action' => 'delinquent')),
array('name' => 'All', 'url' => array('controller' => 'leases', 'action' => 'all')),
);
@@ -28,10 +29,11 @@ class LeasesController extends AppController {
* - Generate a listing of leases
*/
function index() { $this->all(); }
function active() { $this->gridView('Active Leases'); }
function closed() { $this->gridView('Closed Leases'); }
function all() { $this->gridView('All Leases', 'all'); }
function index() { $this->all(); }
function active() { $this->gridView('Active Leases'); }
function delinquent() { $this->gridView('Delinquent Leases'); }
function closed() { $this->gridView('Closed Leases'); }
function all() { $this->gridView('All Leases', 'all'); }
/**************************************************************************
@@ -63,6 +65,8 @@ class LeasesController extends AppController {
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));
}
@@ -73,6 +77,9 @@ class LeasesController extends AppController {
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';
}
@@ -151,10 +158,6 @@ class LeasesController extends AppController {
// 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'));
// For debugging, only if the redirect above have been
// commented out, otherwise this section isn't reached.
$this->render('/empty');
}
/**************************************************************************
@@ -171,9 +174,7 @@ class LeasesController extends AppController {
$this->Lease->moveOut($this->data['Lease']['id'],
'VACANT',
$this->data['Lease']['moveout_date'],
//true // Close this lease, if able
false
$this->data['Lease']['moveout_date']
);
$this->redirect($this->data['redirect']);
@@ -226,8 +227,6 @@ class LeasesController extends AppController {
/* function promote_surplus($id) { */
/* $this->Lease->promoteSurplus($id); */
/* pr("PREVENTING REDIRECT"); */
/* $this->render('fake'); */
/* $this->redirect(array('controller' => 'leases', */
/* 'action' => 'view', */
/* $id)); */
@@ -337,7 +336,7 @@ class LeasesController extends AppController {
// REVISIT <AP>: 20090708
// We should probably seek confirmation first...
if (!$this->Lease->closeable($id)) {
INTERNAL_ERROR("This lease is not ready to close");
$this->INTERNAL_ERROR("This lease is not ready to close");
$this->redirect(array('action'=>'view', $id));
}
@@ -418,11 +417,17 @@ class LeasesController extends AppController {
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'));
}
/**************************************************************************
**************************************************************************
@@ -442,21 +447,18 @@ class LeasesController extends AppController {
('first',
array('contain' =>
array(// Models
'LeaseType',
'Unit',
'Customer',
'LeaseType(id,name)',
'Unit(id,name)',
'Customer(id,name)',
),
'fields' => array('Lease.*', $this->Lease->delinquentField()),
'conditions' => array(array('Lease.id' => $id)),
)
);
$lease['Lease'] += $lease[0];
unset($lease[0]);
$lease['Lease']['paid_through'] = $this->Lease->rentPaidThrough($id);
$this->set('charge_gaps', $this->Lease->rentChargeGaps($id));
$this->set('charge_through', $this->Lease->rentChargeThrough($id));
// Figure out the outstanding balances for this lease
// Figure out the outstanding balances for this lease
$outstanding_balance = $this->Lease->balance($id);
$outstanding_deposit = $this->Lease->securityDepositBalance($id);
@@ -495,7 +497,7 @@ class LeasesController extends AppController {
// 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)
INTERNAL_ERROR("Should not have a customer lease credit.");
$this->INTERNAL_ERROR("Should not have a customer lease credit.");
/* if ($outstanding_balance < 0) */
/* $this->sidemenu_links[] = */

View File

@@ -145,12 +145,6 @@ class LedgerEntriesController extends AppController {
*/
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->LedgerEntry->find
('first',
array('contain' => array
@@ -163,6 +157,10 @@ class LedgerEntriesController extends AppController {
array('fields' => array('id', 'sequence', 'name'),
'Account' =>
array('fields' => array('id', 'name', 'type'),
'conditions' =>
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
array('Account.level >=' => 5),
),
),
@@ -170,6 +168,9 @@ class LedgerEntriesController extends AppController {
array('fields' => array('id', 'name'),
),
'DebitDoubleEntry' => array('id'),
'CreditDoubleEntry' => array('id'),
'DebitEntry' => array('fields' => array('id', 'crdr')),
'CreditEntry' => array('fields' => array('id', 'crdr')),
),
@@ -177,6 +178,11 @@ class LedgerEntriesController extends AppController {
'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']))
@@ -191,6 +197,11 @@ class LedgerEntriesController extends AppController {
else
$entry['MatchingEntry'] = $entry['DebitEntry'][0];
if (empty($entry['DebitDoubleEntry']['id']))
$entry['DoubleEntry'] = $entry['CreditDoubleEntry'];
else
$entry['DoubleEntry'] = $entry['DebitDoubleEntry'];
// Prepare to render.
$title = "Ledger Entry #{$entry['LedgerEntry']['id']}";
$this->set(compact('entry', 'title'));

View File

@@ -50,24 +50,21 @@ class LedgersController extends AppController {
}
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
'Account',
'LedgerEntry',
'CloseTransaction',
),
);
}
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';
@@ -85,6 +82,10 @@ class LedgersController extends AppController {
$conditions[] = array('Ledger.close_transaction_id !=' => null);
}
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
$conditions[] = array('Account.level >=' => 10);
return $conditions;
}
@@ -119,22 +120,25 @@ class LedgersController extends AppController {
*/
function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid Item.', true));
$this->redirect(array('action'=>'index'));
}
// Get details about the ledger itself (no entries yet)
$ledger = $this->Ledger->find
('first',
array('contain' =>
array(// Models
'Account',
),
'conditions' => array(array('Ledger.id' => $id)),
'conditions' => array(array('Ledger.id' => $id),
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
array('Account.level >=' => 10),
),
)
);
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);

View File

@@ -85,11 +85,33 @@ class MapsController extends AppController {
'units' => array());
// Find all of the map/unit information from this SiteArea
$this->Map->recursive = 2;
$this->Map->SiteArea->unbindModel(array('hasOne' => array('Map')));
$map = $this->Map->read(null, $id);
//pr($map);
$map = $this->Map->find('first', array('contain' => false,
'conditions' => array('id' => $id)));
$units = $this->Map->Unit->find
('all',
array('link' =>
array('Map' =>
array('fields' => array()),
'CurrentLease' =>
array('fields' => array($this->Map->Unit->CurrentLease->
delinquentField('CurrentLease'))),
'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
* to reside in the view. However, two separate views need this
@@ -113,7 +135,7 @@ class MapsController extends AppController {
$info['depth'] = $bottom * $screen_adjustment_factor;
// Go through each unit in the map, calculating the map location
foreach ($map['Unit'] AS $unit) {
foreach ($units AS $unit) {
$lft = $unit['MapsUnit']['pt_left'] + $boundary_adjustment;
$top = $unit['MapsUnit']['pt_top'] + $boundary_adjustment;
@@ -132,10 +154,9 @@ class MapsController extends AppController {
$width *= $screen_adjustment_factor;
$depth *= $screen_adjustment_factor;
//$info['units'][$unit['id']] =
$info['units'][] =
array( 'id' => $unit['id'],
'name' => $unit['name'],
array( 'id' => $unit['Unit']['id'],
'name' => $unit['Unit']['name'],
'left' => $lft,
'right' => $lft + $width,
'top' => $top,
@@ -143,11 +164,14 @@ class MapsController extends AppController {
'width' => $width,
'depth' => $depth,
'n-s' => $unit['MapsUnit']['transpose'] ? 0 : 1,
'status' => $unit['status']
'status' => (($unit['Unit']['status'] === 'OCCUPIED' &&
!empty($unit[0]['delinquent']))
? 'LATE' : $unit['Unit']['status'])
);
}
//pr($info);
/* pr($info); */
/* $this->render('/empty'); */
return $info;
}
@@ -160,8 +184,10 @@ class MapsController extends AppController {
*/
function legend($id = null, $requested_width = 400) {
$status = $this->Map->Unit->activeStatusEnums();
//pr($status);
$status = array_keys($this->Map->Unit->activeStatusEnums());
$occupied_key = array_search('OCCUPIED', $status);
array_splice($status, $occupied_key+1, 0, array('LATE'));
$rows = 2;
$cols = (int)((count($status) + $rows - 1) / $rows);
@@ -191,7 +217,7 @@ class MapsController extends AppController {
$item_width *= $screen_adjustment_factor;
$item_depth *= $screen_adjustment_factor;
foreach ($status AS $code => $value) {
foreach ($status AS $code) {
$info['units'][] = array('name' => $code,
'status' => $code,
'width' => $item_width,
@@ -241,9 +267,9 @@ class MapsController extends AppController {
$info['palate']['unit']['DIRTY']['bg'] = array('red' => 128, 'green' => 192, 'blue' => 192);
$info['palate']['unit']['VACANT']['bg'] = array('red' => 0, 'green' => 255, 'blue' => 128);
$info['palate']['unit']['OCCUPIED']['bg'] = array('red' => 0, 'green' => 128, 'blue' => 255);
$info['palate']['unit']['LATE']['bg'] = array('red' => 255, 'green' => 64, 'blue' => 64);
$info['palate']['unit']['LOCKED']['bg'] = array('red' => 255, 'green' => 128, 'blue' => 128);
$info['palate']['unit']['LIENED']['bg'] = array('red' => 255, 'green' => 192, 'blue' => 192);
$info['palate']['unit']['LATE']['bg'] = array('red' => 255, 'green' => 192, 'blue' => 192);
$info['palate']['unit']['LOCKED']['bg'] = array('red' => 255, 'green' => 64, 'blue' => 64);
$info['palate']['unit']['LIENED']['bg'] = array('red' => 255, 'green' => 0, 'blue' => 128);
// Determine text color to go with each background
foreach ($info['palate']['unit'] AS &$code) {

View File

@@ -35,7 +35,7 @@ class StatementEntriesController extends AppController {
* to jqGrid.
*/
function gridDataTables(&$params, &$model) {
function gridDataCountTables(&$params, &$model) {
$link =
array(// Models
'Transaction' =>
@@ -58,27 +58,26 @@ class StatementEntriesController extends AppController {
),
);
if (isset($params['post']['custom']['statement_entry_id'])) {
$link['DisbursementEntry'] = array();
$link['ChargeEntry'] = array();
if (!empty($params['post']['custom']['statement_entry_id'])) {
$link['ChargeEntry'] = array();
$link['DisbursementEntry'] = array();
}
/* if ($params['action'] === 'collected') { */
/* $link['DisbursementEntry'] = array('Receipt' => array('class' => 'Transaction')); */
/* $link['ChargeEntry'] = array('Invoice' => array('class' => 'Transaction')); */
/* } */
/* if (count(array_intersect($params['fields'], array('applied'))) == 1) { */
/* $link['DisbursementEntry'] = array(); */
/* $link['ChargeEntry'] = array(); */
/* } */
/* elseif (isset($params['post']['custom']['customer_id']) || isset($params['post']['custom']['lease_id'])) { */
/* $link['DisbursementEntry'] = array(); */
/* } */
return array('link' => $link);
}
function 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);
@@ -87,11 +86,13 @@ class StatementEntriesController extends AppController {
" 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 'balance'");
") AS 'unapplied'");
}
$fields = array_merge($fields,
@@ -117,12 +118,27 @@ class StatementEntriesController extends AppController {
if (isset($account_id))
$conditions[] = array('StatementEntry.account_id' => $account_id);
if (isset($customer_id))
$conditions[] = array('StatementEntry.customer_id' => $customer_id);
if (isset($statement_entry_id)) {
$conditions[] = array('OR' =>
array(array('ChargeEntry.id' => $statement_entry_id),
array('DisbursementEntry.id' => $statement_entry_id)));
}
if ($params['action'] === 'unreconciled') {
$query = array('conditions' => $conditions);
$set = $this->StatementEntry->reconciledSet('CHARGE', $query, true);
$entries = array();
foreach ($set['entries'] AS $entry)
$entries[] = $entry['StatementEntry']['id'];
$conditions[] = array('StatementEntry.id' => $entries);
$params['userdata']['balance'] = $set['summary']['balance'];
}
return $conditions;
}
@@ -151,26 +167,26 @@ class StatementEntriesController extends AppController {
}
function gridDataRecordsExecute(&$params, &$model, $query) {
if (in_array('applied', $params['post']['fields'])) {
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
$tquery['fields'] = array("IF(StatementEntry.type = 'CHARGE'," .
" SUM(COALESCE(DisbursementEntry.amount,0))," .
" SUM(COALESCE(ChargeEntry.amount,0)))" .
" AS 'applied'",
/* if ($params['action'] === '???') { */
/* $tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1)); */
/* $tquery['fields'] = array("IF(StatementEntry.type = 'CHARGE'," . */
/* " SUM(COALESCE(DisbursementEntry.amount,0))," . */
/* " SUM(COALESCE(ChargeEntry.amount,0)))" . */
/* " AS 'applied'", */
"StatementEntry.amount - (" .
"IF(StatementEntry.type = 'CHARGE'," .
" SUM(COALESCE(DisbursementEntry.amount,0))," .
" SUM(COALESCE(ChargeEntry.amount,0)))" .
") AS 'balance'",
);
/* "StatementEntry.amount - (" . */
/* "IF(StatementEntry.type = 'CHARGE'," . */
/* " SUM(COALESCE(DisbursementEntry.amount,0))," . */
/* " SUM(COALESCE(ChargeEntry.amount,0)))" . */
/* ") AS 'balance'", */
/* ); */
//pr(compact('tquery'));
$total = $model->find('first', $tquery);
$params['userdata']['total'] = $total[0]['applied'];
$params['userdata']['balance'] = $total[0]['balance'];
}
else {
/* //pr(compact('tquery')); */
/* $total = $model->find('first', $tquery); */
/* $params['userdata']['total'] = $total[0]['applied']; */
/* $params['userdata']['balance'] = $total[0]['balance']; */
/* } */
if ($params['action'] === 'collected') {
$tquery = array_diff_key($query, array('fields'=>1,'group'=>1,'limit'=>1,'order'=>1));
$tquery['fields'] = array("SUM(COALESCE(StatementEntry.amount,0)) AS 'total'");
$total = $model->find('first', $tquery);
@@ -213,12 +229,6 @@ class StatementEntriesController extends AppController {
*/
function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid Item.', true));
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
}
// Get the StatementEntry and related fields
$entry = $this->StatementEntry->find
('first',
array('contain' => array
@@ -228,10 +238,17 @@ class StatementEntriesController extends AppController {
'Lease' => array('fields' => array('id')),
),
'conditions' => array('StatementEntry.id' => $id),
'conditions' => array(array('StatementEntry.id' => $id),
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
array('Account.level >=' => 5)
),
));
$reconciled = $this->StatementEntry->reconciledEntries($id);
if (empty($entry)) {
$this->Session->setFlash(__('Invalid Item.', true));
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
}
$stats = $this->StatementEntry->stats($id);
@@ -243,19 +260,14 @@ class StatementEntriesController extends AppController {
if (strtoupper($entry['StatementEntry']['type']) === 'CHARGE') {
$reversal = $this->StatementEntry->find
('first',
array('link' => array('DisbursementEntry'),
'conditions' => array(array('StatementEntry.id' => $id),
array('DisbursementEntry.type' => 'REVERSAL')),
));
$reversable = $this->StatementEntry->reversable($id);
// Set up dynamic menu items
if (empty($reversal) || $stats['balance'] > 0)
if ($reversable || $stats['balance'] > 0)
$this->sidemenu_links[] =
array('name' => 'Operations', 'header' => true);
if (empty($reversal))
if ($reversable)
$this->sidemenu_links[] =
array('name' => 'Reverse',
'url' => array('action' => 'reverse',
@@ -270,7 +282,7 @@ class StatementEntriesController extends AppController {
// Prepare to render.
$title = "Statement Entry #{$entry['StatementEntry']['id']}";
$this->set(compact('entry', 'title', 'reconciled', 'stats'));
$this->set(compact('entry', 'title', 'stats'));
}
}

View File

@@ -178,7 +178,8 @@ class TendersController extends AppController {
&& empty($tender['Tender']['nsf_transaction_id'])
// Hard to tell what types of items can come back as NSF.
// For now, assume iff it is a named item, it can be NSF.
&& !empty($tender['TenderType']['data1_name'])
// (or if we're in development mode)
&& (!empty($tender['TenderType']['data1_name']) || !empty($this->params['dev']))
) {
$this->sidemenu_links[] =
array('name' => 'NSF',
@@ -233,11 +234,6 @@ class TendersController extends AppController {
}
$this->redirect(array('action'=>'view', $this->Tender->id));
// For debugging, only if the redirects above have been
// commented out, otherwise this section isn't reached.
$this->render('/fake');
return;
}
if ($id) {

View File

@@ -54,7 +54,12 @@ class TransactionsController extends AppController {
*/
function gridDataCountTables(&$params, &$model) {
return parent::gridDataTables($params, $model);
return array
('link' =>
array(// Models
'Account' => array('fields' => array()),
),
);
}
function gridDataTables(&$params, &$model) {
@@ -79,11 +84,16 @@ class TransactionsController extends AppController {
if (in_array($params['action'], array('invoice', 'receipt', 'deposit')))
$conditions[] = array('Transaction.type' => strtoupper($params['action']));
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
$conditions[] = array('Account.level >=' => 5);
return $conditions;
}
function gridDataPostProcessLinks(&$params, &$model, &$records, $links) {
$links['Transaction'] = array('id');
$links['Transaction'] = array('id', 'action' => ($params['action'] == 'deposit'
? 'deposit_slip' : 'view'));
return parent::gridDataPostProcessLinks($params, $model, $records, $links);
}
@@ -339,8 +349,6 @@ class TransactionsController extends AppController {
die("<H1>REFUND FAILED</H1>");
}
$this->render('/fake');
// Return to viewing the lease/customer
if (empty($data['Lease']['id']))
$this->redirect(array('controller' => 'customers',
@@ -353,6 +361,21 @@ class TransactionsController extends AppController {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* 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 = null) {
$this->Transaction->destroy($id);
//$this->redirect(array('action' => 'index'));
}
/**************************************************************************
**************************************************************************
**************************************************************************
@@ -361,33 +384,45 @@ class TransactionsController extends AppController {
*/
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
'Account' =>
array('fields' => array('Account.id',
'Account.name'),
),
'Ledger' =>
array('fields' => array('Ledger.id',
'Ledger.name'),
),
'Account(id,name)',
'Ledger(id,name)',
'NsfTender(id,name)',
),
'conditions' => array('Transaction.id' => $id),
'conditions' => array(array('Transaction.id' => $id),
// REVISIT <AP>: 20090811
// No security issues have been worked out yet
array('OR' =>
array(array('Account.level >=' => 5),
array('Account.id' => null))),
),
));
if ($transaction['Transaction']['type'] === 'DEPOSIT') {
// REVISIT <AP>: 20090815
// for debug purposes only (pr output)
$this->Transaction->stats($id);
if (empty($transaction)) {
$this->Session->setFlash(__('Invalid Item.', true));
$this->redirect(array('action'=>'index'));
}
if ($transaction['Transaction']['type'] === 'DEPOSIT' || $this->params['dev']) {
$this->sidemenu_links[] =
array('name' => 'Operations', 'header' => true);
$this->sidemenu_links[] =
array('name' => 'View Slip', 'url' => array('action' => 'deposit_slip', $id));
if ($transaction['Transaction']['type'] === 'DEPOSIT')
$this->sidemenu_links[] =
array('name' => 'View Slip', 'url' => array('action' => 'deposit_slip', $id));
if ($this->params['dev'])
$this->sidemenu_links[] =
array('name' => 'Destroy', 'url' => array('action' => 'destroy', $id),
'confirmMessage' => ("This may leave the database in an unstable state." .
" Do NOT do this unless you know what you're doing." .
" Proceed anyway?"));
}
// OK, prepare to render.
@@ -440,15 +475,12 @@ class TransactionsController extends AppController {
$type['TenderType'] + $type[0];
}
// For each form of tender in the deposit, get the deposit items
/* foreach ($deposit['types'] AS $type_id => &$type) { */
/* $type['entries'] = $this->Transaction->DepositTender->find */
/* ('all', */
/* array('contain' => array('Customer', 'LedgerEntry'), */
/* 'conditions' => array(array('DepositTender.deposit_transaction_id' => $id), */
/* array('DepositTender.tender_type_id' => $type_id)), */
/* )); */
/* } */
$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->sidemenu_links[] =
array('name' => 'Operations', 'header' => true);

View File

@@ -256,13 +256,13 @@ class UnitsController extends AppController {
if (isset($unit['CurrentLease']['id']) &&
!isset($unit['CurrentLease']['close_date'])) {
$this->sidemenu_links[] =
array('name' => 'Charge', 'url' => array('controller' => 'leases',
'action' => 'invoice',
$unit['CurrentLease']['id']));
array('name' => 'New Invoice', 'url' => array('controller' => 'leases',
'action' => 'invoice',
$unit['CurrentLease']['id']));
$this->sidemenu_links[] =
array('name' => 'Payment', 'url' => array('controller' => 'customers',
'action' => 'receipt',
$unit['CurrentLease']['customer_id']));
array('name' => 'New Receipt', 'url' => array('controller' => 'customers',
'action' => 'receipt',
$unit['CurrentLease']['customer_id']));
}
// Prepare to render.
@@ -307,11 +307,6 @@ class UnitsController extends AppController {
}
$this->redirect(array('action'=>'view', $this->Unit->id));
// For debugging, only if the redirects above have been
// commented out, otherwise this section isn't reached.
$this->render('/fake');
return;
}
if ($id) {

View File

@@ -139,7 +139,17 @@ class Account extends AppModel {
function invoiceAccountID() { return $this->nameToID('Invoice'); }
function receiptAccountID() { return $this->nameToID('Receipt'); }
function badDebtAccountID() { return $this->nameToID('Bad Debt'); }
function customerCreditAccountID() { return $this->nameToID(
// 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'
); }
/**************************************************************************
**************************************************************************
@@ -223,13 +233,28 @@ class Account extends AppModel {
function collectableAccounts() {
$accounts = $this->receiptAccounts();
foreach(array($this->nsfAccountID(),
foreach(array($this->customerCreditAccountID(),
$this->securityDepositAccountID(),
$this->nsfAccountID(),
$this->waiverAccountID(),
$this->badDebtAccountID(),
$this->securityDepositAccountID())
//$this->nameToID('Closing'),
//$this->nameToID('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;
}
@@ -345,6 +370,7 @@ class Account extends AppModel {
$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));

View File

@@ -142,10 +142,8 @@ class Customer extends AppModel {
continue;
$I = new Contact();
$I->create();
if (!$I->save($contact, false)) {
if (!$I->saveContact(null, array('Contact' => $contact)))
return false;
}
$contact['id'] = $I->id;
}
@@ -203,6 +201,29 @@ class Customer extends AppModel {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: update
* - Update any cached or calculated fields
*/
function update($id) {
// REVISIT <AP>: 20090812
// updateLeaseCount is handled directly when needed.
// Should we simplify by just doing it anyway?
//$this->updateLeaseCount($id);
$current_leases =
$this->find('all',
array('link' => array('CurrentLease' => array('type' => 'INNER')),
'conditions' => array('Customer.id' => $id)));
foreach ($current_leases AS $lease)
$this->Lease->update($lease['CurrentLease']['id']);
}
/**************************************************************************
**************************************************************************
**************************************************************************

View File

@@ -53,12 +53,27 @@ class DoubleEntry extends AppModel {
*/
function addDoubleEntry($entry1, $entry2, $entry1_tender = null) {
/* pr(array('DoubleEntry::addDoubleEntry' => */
/* compact('entry1', 'entry2', 'entry1_tender'))); */
$this->prFunctionLevel(16);
$this->prEnter(compact('entry1', 'entry2', 'entry1_tender'));
$ret = array();
if (!$this->verifyDoubleEntry($entry1, $entry2, $entry1_tender))
return array('error' => true) + $ret;
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();
@@ -67,13 +82,13 @@ class DoubleEntry extends AppModel {
$result = $LE->addLedgerEntry($entry1, $entry1_tender);
$ret['Entry1'] = $result;
if ($result['error'])
return array('error' => true) + $ret;
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 array('error' => true) + $ret;
return $this->prReturn(array('error' => true) + $ret);
// Now link them as a double entry
$double_entry = array();
@@ -82,15 +97,13 @@ class DoubleEntry extends AppModel {
$double_entry['credit_entry_id'] =
($entry1['crdr'] === 'CREDIT') ? $ret['Entry1']['ledger_entry_id'] : $ret['Entry2']['ledger_entry_id'];
/* pr(array('DoubleEntry::addDoubleEntry' => */
/* array('checkpoint' => 'Pre-Save') */
/* + compact('double_entry'))); */
$ret['data'] = $double_entry;
$this->create();
if (!$this->save($double_entry))
return array('error' => true) + $ret;
return $this->prReturn(array('error' => true) + $ret);
$ret['double_entry_id'] = $this->id;
return $ret + array('error' => false);
return $this->prReturn($ret + array('error' => false));
}
}

View File

@@ -12,7 +12,7 @@ class Lease extends AppModel {
'StatementEntry',
);
//var $default_log_level = array('log' => 30, 'show' => 15);
//var $default_log_level = array('log' => 30, 'show' => 30);
/**************************************************************************
**************************************************************************
@@ -426,6 +426,17 @@ class Lease extends AppModel {
$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'));
@@ -449,6 +460,10 @@ class Lease extends AppModel {
// 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),
@@ -512,6 +527,31 @@ class Lease extends AppModel {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* 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 15 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'");
}
/**************************************************************************
**************************************************************************
**************************************************************************
@@ -610,7 +650,7 @@ class Lease extends AppModel {
*/
function moveOut($id, $status = 'VACANT',
$stamp = null, $close = false)
$stamp = null, $close = true)
{
$this->prEnter(compact('id', 'status', 'stamp', 'close'));
@@ -745,6 +785,23 @@ class Lease extends AppModel {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: update
* - Update any cached or calculated fields
*/
function update($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'));
}
/**************************************************************************
**************************************************************************
**************************************************************************

View File

@@ -8,8 +8,22 @@ class LedgerEntry extends AppModel {
);
var $hasOne = array(
'Tender',
'DoubleEntry',
'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(
@@ -113,24 +127,20 @@ class LedgerEntry extends AppModel {
* - Inserts new Ledger Entry into the database
*/
function addLedgerEntry($entry, $tender = null) {
/* pr(array('LedgerEntry::addLedgerEntry' => */
/* compact('entry', 'tender'))); */
$this->prFunctionLevel(16);
$this->prEnter(compact('entry', 'tender'));
$ret = array();
$ret = array('data' => $entry);
if (!$this->verifyLedgerEntry($entry, $tender))
return array('error' => true) + $ret;
return $this->prReturn(array('error' => true) + $ret);
if (empty($entry['ledger_id']))
$entry['ledger_id'] =
$this->Account->currentLedgerID($entry['account_id']);
/* pr(array('LedgerEntry::addLedgerEntry' => */
/* array('checkpoint' => 'Pre-Save') */
/* + compact('entry'))); */
$this->create();
if (!$this->save($entry))
return array('error' => true) + $ret;
return $this->prReturn(array('error' => true) + $ret);
$ret['ledger_entry_id'] = $this->id;
@@ -140,10 +150,10 @@ class LedgerEntry extends AppModel {
$result = $this->Tender->addTender($tender);
$ret['Tender'] = $result;
if ($result['error'])
return array('error' => true) + $ret;
return $this->prReturn(array('error' => true) + $ret);
}
return $ret + array('error' => false);
return $this->prReturn($ret + array('error' => false));
}

View File

@@ -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')
);
}
?>

View File

@@ -18,11 +18,13 @@ class StatementEntry extends AppModel {
'DisbursementEntry' => array(
'className' => 'StatementEntry',
'foreignKey' => 'charge_entry_id',
'dependent' => true,
),
);
//var $default_log_level = array('log' => 30, 'show' => 15);
var $default_log_level = array('log' => 30, 'show' => 15);
var $max_log_level = 19;
/**************************************************************************
**************************************************************************
@@ -123,13 +125,10 @@ class StatementEntry extends AppModel {
function addStatementEntry($entry) {
$this->prEnter(compact('entry'));
$ret = array();
$ret = array('data' => $entry);
if (!$this->verifyStatementEntry($entry))
return $this->prReturn(array('error' => true, 'verify_data' => $entry) + $ret);
$this->pr(20, array('checkpoint' => 'Pre-Save')
+ compact('entry'));
$this->create();
if (!$this->save($entry))
return $this->prReturn(array('error' => true, 'save_data' => $entry) + $ret);
@@ -155,7 +154,7 @@ class StatementEntry extends AppModel {
$charge = $charge['StatementEntry'];
if ($charge['type'] !== 'CHARGE')
INTERNAL_ERROR("Waiver item is not CHARGE.");
$this->INTERNAL_ERROR("Waiver item is not CHARGE.");
// Query the stats to get the remaining balance
$stats = $this->stats($id);
@@ -168,7 +167,6 @@ class StatementEntry extends AppModel {
// Add the charge waiver
$waiver['Entry'][] =
array('amount' => $stats['Charge']['balance'],
'account_id' => $this->Account->waiverAccountID(),
'comment' => null,
);
@@ -178,89 +176,61 @@ class StatementEntry extends AppModel {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: reversable
* - Returns true if the charge can be reversed; false otherwise
*/
function reversable($id) {
$this->prEnter(compact('id'));
// 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, $balance = false, $stamp = null) {
$this->prEnter(compact('id', 'balance', 'stamp'));
function reverse($id, $stamp = null) {
$this->prEnter(compact('id', 'stamp'));
$ret = array();
// Verify the item can be reversed
if (!$this->reversable($id))
$this->INTERNAL_ERROR("Item is not reversable.");
// Get the basic information about the entry to be reversed.
$this->recursive = -1;
$charge = $this->read(null, $id);
$charge = $charge['StatementEntry'];
if ($charge['type'] !== 'CHARGE')
INTERNAL_ERROR("Reversal item is not CHARGE.");
$voided_entry_transactions = array();
$reconciled = $this->reconciledEntries($id);
$this->pr(21, compact('reconciled'));
if ($reconciled && !$balance) {
foreach ($reconciled['entries'] AS $entry) {
if ($entry['DisbursementEntry']['type'] === 'REVERSAL')
INTERNAL_ERROR("Charge has already been reversed");
$voided_entry_transactions[$entry['DisbursementEntry']['transaction_id']]
= array_intersect_key($entry['DisbursementEntry'],
array('customer_id'=>1, 'lease_id'=>1));
$this->del($entry['DisbursementEntry']['id']);
continue;
$DE = new StatementEntry();
$DE->id = $entry['DisbursementEntry']['id'];
$DE->saveField('type', 'VOID');
$DE->saveField('charge_entry_id', null);
}
$this->pr(17, compact('voided_entry_transactions'));
}
// 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);
// Build a transaction
$reversal = array('Transaction' => array(), 'Entry' => array());
$reversal['Transaction']['stamp'] = $stamp;
$reversal['Transaction']['comment'] = "Credit Note: Charge Reversal";
// Add the charge reversal
$reversal['Entry'][] =
array('amount' => $stats['Charge']['balance'],
'account_id' => $charge['account_id'],
'comment' => 'Charge Reversal',
);
$charge['paid'] = $stats['Charge']['disbursement'];
// Record the reversal transaction
$result = $this->Transaction->addReversal
($reversal, $id, $charge['customer_id'], $charge['lease_id']);
$this->pr(21, compact('result'));
$ret['reversal'] = $result;
if ($result['error'])
$ret['error'] = true;
($charge, $stamp, 'Charge Reversal');
foreach ($voided_entry_transactions AS $transaction_id => $tx) {
$result = $this->assignCredits
(null,
$transaction_id,
null,
null,
$tx['customer_id'],
$tx['lease_id']
);
$this->pr(21, compact('result'));
$ret['assigned'][] = $result;
if ($result['error'])
$ret['error'] = true;
if (empty($result['error'])) {
// Mark the charge as reversed
$this->id = $id;
$this->saveField('reverse_transaction_id', $result['transaction_id']);
}
return $this->prReturn($ret + array('error' => false));
return $this->prReturn($result);
}
@@ -288,7 +258,7 @@ class StatementEntry extends AppModel {
}
function reconciledSet($set, $query = null, $unrec = false, $if_rec_include_partial = false) {
//$this->prFunctionLevel(array('log' => 16, 'show' => 10));
$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);
@@ -396,10 +366,11 @@ class StatementEntry extends AppModel {
// First, find all known credits, unless this call is to make
// credit adjustments to a specific charge
// REVISIT <AP>: 20090806
// If the theory below is correct, we should only search for
// explicit credits if we don't have a receipt_id
if (empty($charge_ids)) {
if (empty($receipt_id)) {
if (!empty($charge_ids))
$this->INTERNAL_ERROR("Charge IDs, yet no corresponding receipt");
$lquery = $query;
$lquery['conditions'][] = array('StatementEntry.type' => 'SURPLUS');
// REVISIT <AP>: 20090804
@@ -424,20 +395,13 @@ class StatementEntry extends AppModel {
"Credits Established");
}
else {
if (empty($receipt_id))
INTERNAL_ERROR("Can't make adjustments to a charge without a receipt ID.");
}
// Next, establish credit from the newly added receipt
$receipt_credit = null;
if (!empty($receipt_id)) {
// Establish credit from the (newly added) receipt
$lquery =
array('link' =>
array('StatementEntry',
'LedgerEntry' =>
array('conditions' =>
array('LedgerEntry.account_id !=' =>
$this->Account->accountReceivableAccountID()),
array('LedgerEntry.account_id <> Transaction.account_id')
),
),
'conditions' => array('Transaction.id' => $receipt_id),
@@ -445,15 +409,14 @@ class StatementEntry extends AppModel {
);
$receipt_credit = $this->Transaction->find('first', $lquery);
if (!$receipt_credit)
INTERNAL_ERROR("Unable to locate receipt.");
//$reconciled = $this->reconciledEntries($id);
$this->INTERNAL_ERROR("Unable to locate receipt.");
$stats = $this->Transaction->stats($receipt_id);
$receipt_credit['balance'] =
$receipt_credit['Transaction']['amount'] - $stats['Disbursement']['total'];
$receipt_credit['balance'] = $stats['undisbursed'];
$this->pr(18, compact('receipt_credit'),
$receipt_credit['receipt'] = true;
$credits = array($receipt_credit);
$this->pr(18, compact('credits'),
"Receipt Credit Added");
}
@@ -476,61 +439,44 @@ class StatementEntry extends AppModel {
$this->pr(18, compact('dtype', 'entries'), "Outstanding Debit Entries");
}
// Initialize our list of used credits
$used_credits = array();
// REVISIT <AP>: 20090806
// Testing a theory. We should never have
// explicit credits, as well as a new receipt,
// and yet have outstanding charges.
if (!empty($credits) && !empty($receipt_credit) && !empty($charges))
INTERNAL_ERROR("Explicit credits that haven't already been applied.");
// Work through all unpaid charges, applying disbursements as we go
foreach ($charges AS $charge) {
$this->pr(20, compact('charge'),
'Process Charge');
// Check that we have available credits.
// Technically, this isn't necessary, since the loop
// will handle everything just fine. However, this
// just saves extra processing if/when there is no
// means to resolve a charge anyway.
if (empty($credits) && empty($receipt_credit['balance'])) {
$this->pr(17, 'No more available credits');
break;
}
$charge['balance'] = $charge['StatementEntry']['balance'];
while ($charge['balance'] > 0 &&
(!empty($credits) || !empty($receipt_credit['balance']))) {
// 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');
// Use explicit credits before using implicit credits
// (Not sure it matters though).
if (!empty($credits)) {
// Peel off the first credit available
$credit =& $credits[0];
$disbursement_date = $credit['StatementEntry']['effective_date'];
$disbursement_transaction_id = $credit['StatementEntry']['transaction_id'];
if (empty($credit['receipt']))
$disbursement_account_id = $credit['StatementEntry']['account_id'];
if (!isset($credit['balance']))
$credit['balance'] = $credit['StatementEntry']['amount'];
}
elseif (!empty($receipt_credit['balance'])) {
// Use our only receipt credit
$credit =& $receipt_credit;
$disbursement_date = $credit['Transaction']['stamp'];
$disbursement_transaction_id = $credit['Transaction']['id'];
else
$disbursement_account_id = $credit['LedgerEntry']['account_id'];
}
else {
die("HOW DID WE GET HERE WITH NO SURPLUS?");
}
// 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
@@ -543,35 +489,67 @@ class StatementEntry extends AppModel {
$this->pr(20, compact('credit'),
($credit['balance'] > 0 ? 'Utilized' : 'Exhausted') .
(!empty($credits) ? ' Credit' : ' Receipt'));
(empty($credit['receipt']) ? ' Credit' : ' Receipt'));
if ($credit['balance'] < 0)
die("HOW DID WE END UP WITH NEGATIVE SURPLUS BALANCE?");
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 we've exhausted the credit, get it out of the
// available credit pool (but keep track of it for later).
if ($credit['balance'] <= 0 && !empty($credits))
$used_credits[] = array_shift($credits);
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'],
),
));
// Add a disbursement that uses the available credit to pay the charge
$disbursement = array('type' => $disbursement_type,
'account_id' => $disbursement_account_id,
'amount' => $disbursement_amount,
'effective_date' => $disbursement_date,
'transaction_id' => $disbursement_transaction_id,
'customer_id' => $charge['StatementEntry']['customer_id'],
'lease_id' => $charge['StatementEntry']['lease_id'],
'charge_entry_id' => $charge['StatementEntry']['id'],
'comment' => null,
);
$ret['Disbursement'][] = $result;
if ($result['error'])
$ret['error'] = true;
}
else {
// Receipt Credit
$this->pr(20, compact('disbursement'),
'New Disbursement Entry');
if (strtotime($charge['StatementEntry']['effective_date']) >
strtotime($credit['Transaction']['stamp']))
$disbursement_edate = $charge['StatementEntry']['effective_date'];
else
$disbursement_edate = $credit['Transaction']['stamp'];
$result = $this->addStatementEntry($disbursement);
$ret['Disbursement'][] = $result;
if ($result['error'])
$ret['error'] = true;
// Add a disbursement that uses the available credit to pay the charge
$disbursement =
array('type' => $disbursement_type,
'effective_date' => $disbursement_edate,
'amount' => $disbursement_amount,
'account_id' => $credit['LedgerEntry']['account_id'],
'transaction_id' => $credit['Transaction']['id'],
'customer_id' => $charge['StatementEntry']['customer_id'],
'lease_id' => $charge['StatementEntry']['lease_id'],
'charge_entry_id' => $charge['StatementEntry']['id'],
'comment' => null,
);
$this->pr(20, compact('disbursement'), 'New Disbursement Entry');
$result = $this->addStatementEntry($disbursement);
$ret['Disbursement'][] = $result;
if ($result['error'])
$ret['error'] = true;
}
// Adjust the charge balance to reflect the new disbursement
$charge['balance'] -= $disbursement_amount;
@@ -581,37 +559,47 @@ class StatementEntry extends AppModel {
if ($charge['balance'] <= 0)
$this->pr(20, 'Fully Paid Charge');
}
// Break the $credit reference to avoid future problems
unset($credit);
}
// Partially used credits must be added to the used list
if (isset($credits[0]['applied']))
$used_credits[] = array_shift($credits);
$this->pr(18, compact('credits', 'used_credits', 'receipt_credit'),
'Disbursements added');
$this->pr(18, compact('credits'),
'Disbursements complete');
// Clean up any explicit credits that have been used
foreach ($used_credits AS $credit) {
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->id = $credit['StatementEntry']['id'];
$this->saveField('amount', $credit['balance']);
}
else {
$this->pr(20, compact('credit'),
'Delete Exhausted Credit Entry');
$this->del($credit['StatementEntry']['id'], false);
$this->delete($credit['StatementEntry']['id'], false);
}
}
// Convert non-exhausted receipt credit to an explicit one
if (!empty($receipt_credit['balance'])) {
$credit =& $receipt_credit;
// 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' =>
@@ -619,30 +607,31 @@ class StatementEntry extends AppModel {
array('type' => 'SURPLUS')),
));
if (empty($explicit_credit)) {
$this->pr(18, compact('credit'),
'Create Explicit Credit');
if (!empty($explicit_credit)) {
// REVISIT <AP>: 20090815
// Testing whether or not this case occurs
$this->INTERNAL_ERROR('Existing explicit credit unexpected');
$result = $this->addStatementEntry
(array('type' => 'SURPLUS',
'account_id' => $credit['LedgerEntry']['account_id'],
'amount' => $credit['balance'],
'effective_date' => $credit['Transaction']['stamp'],
'transaction_id' => $credit['Transaction']['id'],
'customer_id' => $customer_id,
'lease_id' => $lease_id,
));
$ret['Credit'] = $result;
if ($result['error'])
$ret['error'] = true;
}
else {
// 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 Explicit 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));
@@ -656,7 +645,7 @@ class StatementEntry extends AppModel {
* - Returns summary data from the requested statement entry
*/
function stats($id = null, $query = null) {
//$this->prFunctionLevel(array('log' => 19, 'show' => 10));
$this->prFunctionLevel(array('log' => 16, 'show' => 10));
$this->prEnter(compact('id', 'query'));
$this->queryInit($query);

View File

@@ -10,6 +10,7 @@ class Tender extends AppModel {
),
'NsfTransaction' => array(
'className' => 'Transaction',
'dependent' => true,
),
);
@@ -17,11 +18,11 @@ class Tender extends AppModel {
/**************************************************************************
**************************************************************************
**************************************************************************
* function: beforeSave
* - Performs any work needed before the save occurs
* function: afterSave
* - Performs any work needed after the save occurs
*/
function afterSave() {
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
@@ -45,10 +46,32 @@ class Tender extends AppModel {
if ($newname !== $this->field('name'))
$this->saveField('name', $newname);
return parent::afterSave();
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);
}
/**************************************************************************
**************************************************************************
**************************************************************************
@@ -78,7 +101,7 @@ class Tender extends AppModel {
function addTender($tender) {
$this->prEnter(compact('tender'));
$ret = array();
$ret = array('data' => $tender);
if (!$this->verifyTender($tender))
return $this->prReturn(array('error' => true) + $ret);

File diff suppressed because it is too large Load Diff

View File

@@ -181,6 +181,16 @@ class Unit extends AppModel {
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: update
* - Update any cached or calculated fields
*/
function update($id) {
}
/**************************************************************************
**************************************************************************
**************************************************************************

View File

@@ -15,8 +15,8 @@ echo '<div class="account collected">' . "\n";
// Reset the form
function resetForm() {
<?php /* REVISIT <AP>: 20090810; Hate to do this, but first data is wrong... */ ?>
setTimeout(updateEntriesGrid, 1000);
// Kick off the grid
updateEntriesGrid();
}
function onGridLoadComplete() {
@@ -40,12 +40,15 @@ function updateEntriesGrid() {
$('#collected-total').html('Calculating...');
$('#collected-entries-jqGrid').clearGridData();
$('#collected-entries-jqGrid').setPostDataItem('dynamic_post', serialize(dynamic_post));
$('#collected-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
$('#collected-entries-jqGrid')
.setGridParam({ page: 1 })
.trigger("reloadGrid");
//$('#collected-entries .HeaderButton').click();
//$('#debug').html("<PRE>\n"+htmlEncode(dump($('#collected-entries-jqGrid').getGridParam()))+"\n</PRE>")
var gridstate = $('#collected-entries-jqGrid').getGridParam('gridstate');
if (gridstate == 'hidden')
$('#collected-entries .HeaderButton').click();
}
@@ -162,12 +165,12 @@ echo $this->element('statement_entries', array
'grid_div_id' => 'collected-entries',
'grid_div_class' => 'text-below',
'grid_events' => array('loadComplete' => 'onGridLoadComplete()'),
//'grid_setup' => array('hiddengrid' => true),
//'caption' => '<SPAN id="receipt-charges-caption"></SPAN>',
'grid_setup' => array('hiddengrid' => true),
'caption' => 'Collected ' . Inflector::pluralize($account['name']),
'filter' => array(//'StatementEntry.type' => 'DISBURSEMENT',
'ChargeEntry.account_id' => $account['id']),
'exclude' => array('Account', 'Charge', 'Type'),
'action' => 'collected',
'filter' => array('ChargeEntry.account_id' => $account['id']),
'include' => array('Amount'),
'exclude' => array(/*'Type',*/ 'Debit', 'Credit'),
),
));
@@ -202,5 +205,3 @@ echo '</div>' . "\n";
echo '</div>' . "\n";
?>
<a href="#" onClick="$('#debug').html(''); return false;">Clear Debug Output</a>

View File

@@ -81,6 +81,33 @@ function resetForm() {
updateCharges($("#customer-id").val());
}
function updateCharges(id) {
$('#charge-entries-jqGrid').clearGridData();
$("#receipt-balance").html("Calculating...");
$("#receipt-charges-caption").html("Please Wait...");
var custom = new Array();
custom['customer_id'] = id;
var dynamic_post = new Array();
dynamic_post['custom'] = custom;
$('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
$('#charge-entries-jqGrid')
.setGridParam({ page: 1 })
.trigger("reloadGrid");
var gridstate = $('#charge-entries-jqGrid').getGridParam('gridstate');
if (gridstate == 'hidden')
$('#charge-entries .HeaderButton').click();
}
function onGridLoadComplete() {
var userdata = $('#charge-entries-jqGrid').getGridParam('userData');
$('#receipt-balance').html(fmtCurrency(userdata['balance']));
$("#receipt-charges-caption").html("Outstanding Charges");
}
function onRowSelect(grid_id, customer_id) {
// Set the customer id that will be returned with the form
$("#customer-id").val(customer_id);
@@ -91,7 +118,10 @@ function onRowSelect(grid_id, customer_id) {
// This is not intended as a long term solution,
// but I need a way to enter data and then view
// the results. This link will help.
$("#receipt-customer-id").html('<A HREF="/pmgr/site/customers/view/' +
$("#receipt-customer-id").html('<A HREF="' +
"<?php echo $html->url(array('controller' => 'customers',
'action' => 'view')); ?>"
+ "/" +
$(grid_id).getCell(customer_id, 'Customer-id').replace(/^#/,'') +
'">' +
$(grid_id).getCell(customer_id, 'Customer-id') +
@@ -214,48 +244,6 @@ function switchPaymentType(paymentid_base, paymentid, radioid) {
$("#"+paymentid_base+"-"+paymentid+"-"+type_id).slideDown();
}
function updateChargesGrid(idlist) {
var dynamic_post = new Array();
dynamic_post['idlist'] = idlist;
$('#charge-entries-jqGrid').setPostDataItem('dynamic_post_replace', serialize(dynamic_post));
$('#charge-entries-jqGrid')
.setGridParam({ page: 1 })
.trigger("reloadGrid");
}
function updateCharges(id) {
var url = '<?php echo ($html->url(array("controller" => $this->params["controller"],
"action" => "unreconciled"))); ?>';
url += '/'+id;
$('#charge-entries-jqGrid').clearGridData();
$("#receipt-balance").html("Calculating...");
$("#receipt-charges-caption").html("Please Wait...");
$.ajax({
type: "GET",
url: url,
dataType: "xml",
success: function(xml) {
var ids = new Array();
$('entry',xml).each(function(i){
ids.push($(this).attr('id'));
});
$('#receipt-balance').html(fmtCurrency($('entries',xml).attr('balance')));
$("#receipt-charges-caption").html("Outstanding Charges");
updateChargesGrid(ids);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
/* alert('ERROR'); */
/* $('#debug').html('<P>request<BR>'+escape(XMLHttpRequest)); */
/* $('#debug').append('<P>status<BR>'+escape(textStatus)); */
/* $('#debug').append('<P>error<BR>'+escape(errorThrown)); */
}
});
}
--></script>
<?php
@@ -303,17 +291,19 @@ echo ('<DIV CLASS="receipt grid-selection-text">' .
echo $this->element('statement_entries', array
(// Element configuration
'account_ftype' => 'credit',
'limit' => 8,
// Grid configuration
(// Grid configuration
'config' => array
(
'grid_div_id' => 'charge-entries',
'grid_div_class' => 'text-below',
'grid_events' => array('loadComplete' => 'onGridLoadComplete()'),
'grid_setup' => array('hiddengrid' => true),
'caption' => '<SPAN id="receipt-charges-caption"></SPAN>',
'rows' => $charges,
'action' => 'unreconciled',
'exclude' => array('Customer', 'Type', 'Debit', 'Credit'),
'include' => array('Applied', 'Balance'),
'remap' => array('Applied' => 'Paid'),
'limit' => 8,
),
));
@@ -393,19 +383,18 @@ Configure::write('debug', '0');
showCurrentAtPos: 0,
dateFormat: 'mm/dd/yy' });
datepickerNow('TransactionStamp');
$("#receipt-customer-id").html("INTERNAL ERROR");
$("#receipt-customer-name").html("INTERNAL ERROR");
$("#receipt-balance").html("INTERNAL ERROR");
$("#receipt-charges-caption").html("Outstanding Charges");
resetForm();
<?php if (isset($customer['id'])): ?>
$("#customer-id").val(<?php echo $customer['id']; ?>);
//$("#receipt-customer-id").html("<?php echo '#'.$customer['id']; ?>");
$("#receipt-customer-id").html('<A HREF="/pmgr/site/customers/view/' +
$("#receipt-customer-id").html('<A HREF="' +
"<?php echo $html->url(array('controller' => 'customers',
'action' => 'view')); ?>"
+ "/" +
"<?php echo $customer['id']; ?>" +
'">#' +
"<?php echo $customer['id']; ?>" +
@@ -417,6 +406,14 @@ Configure::write('debug', '0');
onGridState(null, 'visible');
<?php endif; ?>
resetForm();
datepickerNow('TransactionStamp');
<?php if ($this->params['dev']): ?>
$('#output-debug').html('Post Output');
$('#output-debug').show();
<?php endif; ?>
});
--></script>

View File

@@ -52,8 +52,6 @@ $ledgers = array('debit' => $entry['DebitLedger'],
'credit' => $entry['CreditLedger']);
$entries = array('debit' => $entry['DebitEntry'],
'credit' => $entry['CreditEntry']);
$customer = $entry['Customer'];
$lease = $entry['Lease'];
$entry = $entry['DoubleEntry'];
$rows = array();
@@ -63,21 +61,7 @@ $rows[] = array('Transaction', $html->link('#'.$transaction['id'],
'action' => 'view',
$transaction['id'])));
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
$rows[] = array('Effective', FormatHelper::date($entry['effective_date']));
//$rows[] = array('Through', FormatHelper::date($entry['through_date']));
$rows[] = array('Customer', (isset($customer['name'])
? $html->link($customer['name'],
array('controller' => 'customers',
'action' => 'view',
$customer['id']))
: null));
$rows[] = array('Lease', (isset($lease['id'])
? $html->link('#'.$lease['id'],
array('controller' => 'leases',
'action' => 'view',
$lease['id']))
: null));
$rows[] = array('Comment', $entry['comment']);
$rows[] = array('Comment', $entry['comment']);
echo $this->element('table',
array('class' => 'item ledger-entry detail',
@@ -132,8 +116,8 @@ foreach ($ledgers AS $type => $ledger) {
array('controller' => 'ledgers',
'action' => 'view',
$ledger['id'])));
$rows[] = array('Amount', FormatHelper::currency($entry['amount']));
$rows[] = array('Effect', $ledger['Account']['ftype'] == $type ? 'INCREASE' : 'DECREASE');
$rows[] = array('Amount', FormatHelper::currency($entries[$type]['amount']));
//$rows[] = array('Effect', $ledger['Account']['ftype'] == $type ? 'INCREASE' : 'DECREASE');
echo $this->element('table',
array('class' => array('item', $type, 'detail'),

View File

@@ -46,12 +46,12 @@ if (!isset($grid_setup))
$grid_setup = array();
// Do some prework to bring in the appropriate libraries
$html->css('jqGrid/ui.jqgrid', null, null, false);
//$html->css('jqGrid/jqModal', null, null, false);
$javascript->link('jqGrid/grid.locale-en', false);
$javascript->link('jqGrid/jquery.jqGrid.min', false);
//$javascript->link('jqGrid/js/jqModal', false);
//$javascript->link('jqGrid/js/jqDnR', false);
$imgpath = '/pmgr/site/css/jqGrid/basic/images';
$html->css('jqGrid/basic/grid', null, null, false);
$html->css('jqGrid/jqModal', null, null, false);
$javascript->link('jqGrid/jquery.jqGrid.js', false);
$javascript->link('jqGrid/js/jqModal', false);
$javascript->link('jqGrid/js/jqDnR', false);
$javascript->link('pmgr_jqGrid', false);
@@ -63,7 +63,6 @@ $javascript->link('pmgr_jqGrid', false);
// as part of the data fetch.
$url = $html->url(array('controller' => $controller,
'action' => 'gridData',
'debug' => 0,
));
// Create extra parameters that jqGrid will pass to our
@@ -202,11 +201,11 @@ foreach (array_merge(array('loadComplete' => '', 'loadError' => ''),
if ($event == 'loadComplete') {
$grid_events[$event] =
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url.replace(/\/debug.*$/,'?'); pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Query</A><BR>'); $statement;}");
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url+'/debug:1?'; pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Query</A><BR>'); $statement;}");
}
elseif ($event == 'loadError') {
$grid_events[$event] =
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url.replace(/\/debug.*$/,'?'); pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Error Query</A><BR>'); $statement;}");
array('--special' => "function($params) {url=jQuery('#{$grid_id}').getGridParam('url');url=url+'/debug:1?'; pd=jQuery('#{$grid_id}').getPostData();$.each(pd,function(i){ url+=i+'='+escape(pd[i])+'&'; }); jQuery('#{$grid_id}-query').html('<A HREF=\"'+url+'\">Grid Error Query</A><BR>'); $statement;}");
}
else {
$grid_events[$event] =
@@ -231,8 +230,8 @@ $jqGrid_setup = array_merge
'sortname' => $sortname,
'sortorder' => $sortorder,
'caption' => $caption,
'imgpath' => $imgpath,
'viewrecords' => true,
'gridview' => true,
'pager' => $grid_id.'-pager',
),
$grid_events,
@@ -251,16 +250,16 @@ $jqGrid_setup = array_merge
<script type="text/javascript"><!--
jQuery(document).ready(function(){
currencyFormatter = function(cellval, opts, rowObject) {
currencyFormatter = function(el, cellval, opts) {
if (!cellval)
return "";
return fmtCurrency(cellval);
return;
$(el).html(fmtCurrency(cellval));
}
idFormatter = function(cellval, opts, rowObject) {
idFormatter = function(el, cellval, opts) {
if (!cellval)
return cellval;
return '#'+cellval;
return;
$(el).html('#'+cellval);
}
jQuery('#<?php echo $grid_id; ?>').jqGrid(
@@ -272,6 +271,36 @@ jQuery(document).ready(function(){
del:false,
search:true,
refresh:true});
<?php
/* jQuery('#t_<?php echo $grid_id; ?>').height(25).hide() */
/* .filterGrid('#<?php echo $grid_id; ?>', { */
/* gridModel:true, */
/* gridToolbar:true, */
/* autosearch:true, */
/* }); */
/* jQuery('#<?php echo $grid_id; ?>').navGrid('#<?php echo $grid_id; ?>-pager', */
/* { view:false, */
/* edit:false, */
/* add:false, */
/* del:false, */
/* search:false, */
/* refresh:false}) */
/* .navButtonAdd('#<?php echo $grid_id; ?>-pager',{ */
/* caption:"Search", */
/* title:"Toggle Search", */
/* buttonimg:'<?php echo $imgpath; ?>' + '/find.gif', */
/* onClickButton:function(){ */
/* if(jQuery('#t_<?php echo $grid_id; ?>').css("display")=="none") { */
/* jQuery('#t_<?php echo $grid_id; ?>').css("display",""); */
/* } else { */
/* jQuery('#t_<?php echo $grid_id; ?>').css("display","none"); */
/* } */
/* } */
/* }); */
?>
});
--></script>

View File

@@ -12,9 +12,20 @@ $cols['Signed'] = array('index' => 'Lease.lease_date', 'formatter' => 'dat
$cols['Move-In'] = array('index' => 'Lease.movein_date', 'formatter' => 'date');
$cols['Move-Out'] = array('index' => 'Lease.moveout_date', 'formatter' => 'date');
$cols['Closed'] = array('index' => 'Lease.close_date', 'formatter' => 'date');
$cols['Paid-Thru'] = array('index' => 'Lease.paid_through_date', 'formatter' => 'date');
$cols['Status'] = array('index' => 'status', 'formatter' => 'enum', 'width' => 100);
$cols['Balance'] = array('index' => 'balance', 'formatter' => 'currency');
$cols['Comment'] = array('index' => 'Lease.comment', 'formatter' => 'comment');
if (!empty($this->params['action'])) {
if ($this->params['action'] === 'closed')
$grid->invalidFields(array('Paid-Thru', 'Status'));
elseif ($this->params['action'] === 'active')
$grid->invalidFields(array('Closed'));
elseif ($this->params['action'] === 'delinquent')
$grid->invalidFields(array('Closed'));
}
// Render the grid
$grid
->columns($cols)
@@ -22,4 +33,4 @@ $grid
->defaultFields(array('LeaseID', 'Lease'))
->searchFields(array('Customer', 'Unit'))
->render($this, isset($config) ? $config : null,
array_diff(array_keys($cols), array('Comment')));
array_diff(array_keys($cols), array('Signed', 'Status', 'Comment')));

View File

@@ -21,7 +21,11 @@ $cols['Type'] = array('index' => 'StatementEntry.type', 'formatter' =>
$cols['Debit'] = array('index' => 'charge', 'formatter' => 'currency');
$cols['Credit'] = array('index' => 'disbursement', 'formatter' => 'currency');
$cols['Amount'] = array('index' => "StatementEntry.amount", 'formatter' => 'currency');
$cols['Applied'] = array('index' => "applied", 'formatter' => 'currency');
// 'balance' is already in use as part of charge/disbursement/balance.
// 'unapplied' isn't quite the right term, but it's not customer visible.
$cols['Balance'] = array('index' => "unapplied", 'formatter' => 'currency');
$cols['Sub-Total'] = array('index' => 'subtotal-balance', 'formatter' => 'currency', 'sortable' => false);
@@ -40,6 +44,6 @@ $grid
->searchFields(array('Customer', 'Unit'))
->render($this, isset($config) ? $config : null,
array_diff(array_keys($cols), array('Through', 'Lease',
'Applied', 'Sub-Total',
'Amount', 'Applied', 'Balance', 'Sub-Total',
'Comment')));

View File

@@ -0,0 +1,5 @@
<?php /* -*- mode:PHP -*- */
if (!empty($message))
echo $message;

View File

@@ -24,9 +24,21 @@ class FormatHelper extends AppHelper {
return '-';
//return null;
$currency = self::$number->currency($amount,
isset($dollar_sign) ? $dollar_sign : 'USD',
$spans ? array('before'=>'', 'after'=>'') : array());
// Use of the $number->currency() function results in the clever,
// but problematic, use of cents for amounts less than $1. For
// example, 50 cents is shown as '50c', not '$0.50'. We want to
// keep everything in terms of dollars, especially for the cases
// where this result is placed into a form for input. 50 cents
// will end up as 50 dollars upon submission :-(
$currency = self::$number->format
(abs($amount),
array('places' => 2,
'before' => $spans ? '' : (isset($dollar_sign) ? $dollar_sign : '$'),
'after' => $spans ? '' : null,
));
if ($amount < 0)
$currency = '(' . $currency . ')';
if ($spans)
return ('<SPAN CLASS="dollar-sign">$</SPAN>' .
@@ -35,22 +47,29 @@ class FormatHelper extends AppHelper {
return $currency;
}
function date($date, $age = false) {
function date($date, $age = false, $class = null, $time = false) {
if (!$date) return null;
$date_fmt = 'm/d/Y';
return (self::$time->format($date_fmt, $date) .
($age
? ' (' . self::age($date, 60*60*24) . ')'
: ''));
if (empty($class))
$class = '';
if ($time)
$date_html = self::$time->nice($date);
else
$date_html = self::$time->format('m/d/Y', $date);
$date_html = '<span class="fmt-date '.$class.'">'.$date_html.'</span>';
if ($age) {
$date_html .= ' (' . self::age($date, $class, $time ? 0 : 60*60*24) . ')';
$date_html = '<span class="fmt-dateage '.$class.'">'.$date_html.'</span>';
}
return $date_html;
}
function datetime($datetime, $age = false) {
if (!$datetime) return null;
return (self::$time->nice($datetime) .
($age
? ' (' . self::age($datetime) . ')'
: ''));
function datetime($datetime, $age = false, $class = null) {
return self::date($datetime, $age, $class, true);
}
function phone($phone, $ext = null) {
@@ -81,10 +100,13 @@ class FormatHelper extends AppHelper {
return $comment;
}
function age($datetime, $min_span = 0) {
function age($datetime, $class, $min_span = 0) {
if (!isset($datetime))
return null;
if (empty($class))
$class = '';
$now = time();
$seconds = self::$time->fromString($datetime);
$backwards = ($seconds > $now);
@@ -95,9 +117,11 @@ class FormatHelper extends AppHelper {
//pr(compact('now', 'seconds', 'backwards', 'timefrom', 'timeto', 'span', 'min_span'));
// If now, just return so
if ($span === 0)
return __('now', true);
// If now, just use 'now'
if ($span === 0) {
$approx = 0;
$unit = 'now';
}
// Display seconds if under 45 seconds
if ($span < 45 && $span >= $min_span) {
@@ -164,20 +188,33 @@ class FormatHelper extends AppHelper {
//pr(compact('span', 'min_span', 'approx', 'unit'));
if ($approx == 0) {
if ($unit == 'day')
return __('today', true);
return __('this ' . $unit, true);
if ($unit == 'now')
$age = 'now';
elseif ($unit == 'day')
$age = 'today';
else
$age = 'this ' . $unit;
}
else {
if (isset($relative))
$age = $relative;
elseif ($approx > $span)
$age = 'almost';
elseif ($approx < $span)
$age = 'over';
else
$age = '';
return (__(isset($relative)
? $relative
: ($approx == $span
? ''
: ($approx > $span ? 'almost' : 'over')), true)
. ' '
. self::_n($approx, $unit)
. ($backwards ? '' : __(' ago', true)));
$age .= ' ' . self::_n($approx, $unit);
if ($backwards)
$age .= ' ago';
}
$age = '<span class="fmt-age '.$class.'">'.__($age, true).'</span>';
return $age;
}
/*****************************

View File

@@ -166,9 +166,14 @@ class GridHelper extends AppHelper {
$included = array_diff(array_merge($this->included, $included),
array_merge($this->invalid, $excluded));
// Extract the columns that correspond to the inclusion set
$this->jqGrid_options['jqGridColumns']
= array_intersect_key($this->columns, array_flip($included));
// Defined the columns, based on the inclusion set,
// remapping column names as necessary.
$this->jqGrid_options['jqGridColumns'] = array();
foreach (array_intersect_key($this->columns, array_flip($included)) AS $name => $col) {
if (!empty($config['remap'][$name]))
$name = $config['remap'][$name];
$this->jqGrid_options['jqGridColumns'][$name] = $col;
}
// Make sure search fields are all part of the inclusion set
$this->jqGrid_options['search_fields']
@@ -224,4 +229,4 @@ class GridHelper extends AppHelper {
return $this;
}
}
}

View File

@@ -35,12 +35,12 @@
echo $html->css('layout') . "\n";
echo $html->css('print', null, array('media' => 'print')) . "\n";
echo $html->css('sidemenu') . "\n";
echo $javascript->link('http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js') . "\n";
echo $javascript->link('http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js') . "\n";
//echo $html->css('themes/base/ui.all') . "\n";
//echo $html->css('themes/smoothness/ui.all') . "\n";
//echo $html->css('themes/dotluv/ui.all') . "\n";
echo $html->css('themes/start/ui.all') . "\n";
//echo $html->css('jquery/base/ui.all') . "\n";
//echo $html->css('jquery/smoothness/ui.all') . "\n";
//echo $html->css('jquery/dotluv/ui.all') . "\n";
echo $html->css('jquery/start/ui.all') . "\n";
echo $javascript->link('jquery/jquery') . "\n";
echo $javascript->link('jquery/jquery-ui') . "\n";
echo $javascript->link('jquery.form') . "\n";
echo $javascript->link('pmgr') . "\n";
echo $scripts_for_layout . "\n";

View File

@@ -64,14 +64,20 @@ function showResponse(responseText, statusText) {
if (!$("#repeat").attr("checked")) {
window.location.href =
<?php if (empty($movein)): ?>
"<?php echo $html->url(array('controller' => 'leases',
'action' => 'view')); ?>"
+ "/" + $("#lease-id").val();
<?php else: ?>
"<?php echo $html->url(array('controller' => 'customers',
'action' => 'receipt')); ?>"
+ "/" + $("#lease-id").val();
<?php endif; ?>
return;
}
// get a clean slate
//resetForm();
resetForm();
}
else {
$('#results').html('<H2>Failed to save invoice!</H2>');
@@ -99,7 +105,10 @@ function onRowSelect(grid_id, lease_id) {
// This is not intended as a long term solution,
// but I need a way to enter data and then view
// the results. This link will help.
$("#invoice-lease").html('<A HREF="/pmgr/site/leases/view/' +
$("#invoice-lease").html('<A HREF="' +
"<?php echo $html->url(array('controller' => 'leases',
'action' => 'view')); ?>"
+ "/" +
$(grid_id).getCell(lease_id, 'Lease-id').replace(/^#/,'') +
'">' +
$(grid_id).getCell(lease_id, 'Lease-number') +
@@ -198,23 +207,24 @@ function addChargeSource(flash) {
// Re-Enable warnings
Configure::write('debug', $saved_debug_state);
echo $this->element('leases', array
('config' => array
('grid_div_id' => 'leases-list',
'grid_div_class' => 'text-below',
'caption' => ('<A HREF="#" ONCLICK="$(\'#leases-list .HeaderButton\').click();'.
' return false;">Select Lease</A>'),
'grid_setup' => array('hiddengrid' => isset($lease['id'])),
'grid_events' => array('onSelectRow' =>
array('ids' =>
'if (ids != null){onRowSelect("#"+$(this).attr("id"), ids);}'),
'onHeaderClick' =>
array('gridstate' =>
'onGridState("#"+$(this).attr("id"), gridstate)'),
),
'nolinks' => true,
'limit' => 10,
)));
if (empty($movein))
echo $this->element('leases', array
('config' => array
('grid_div_id' => 'leases-list',
'grid_div_class' => 'text-below',
'caption' => ('<A HREF="#" ONCLICK="$(\'#leases-list .HeaderButton\').click();'.
' return false;">Select Lease</A>'),
'grid_setup' => array('hiddengrid' => isset($lease['id'])),
'grid_events' => array('onSelectRow' =>
array('ids' =>
'if (ids != null){onRowSelect("#"+$(this).attr("id"), ids);}'),
'onHeaderClick' =>
array('gridstate' =>
'onGridState("#"+$(this).attr("id"), gridstate)'),
),
'nolinks' => true,
'limit' => 10,
)));
echo ('<DIV CLASS="invoice grid-selection-text">' .
@@ -266,10 +276,13 @@ echo $this->element('form_table',
/* echo '</fieldset>' . "\n"; */
echo "<BR>\n";
echo $form->input('repeat', array('type' => 'checkbox',
'id' => 'repeat',
'label' => 'Enter Multiple Invoices')) . "\n";
if (empty($movein)) {
echo "<BR>\n";
echo $form->input('repeat', array('type' => 'checkbox',
'id' => 'repeat',
'label' => 'Enter Multiple Invoices')) . "\n";
}
echo $form->submit('Generate Invoice') . "\n";
?>
@@ -331,6 +344,7 @@ Configure::write('debug', '0');
' value="<?php echo date('m/d/Y', $movein['time']); ?>">');
$("#TransactionComment").val('Move-In Charges');
<?php if ($movein['deposit'] != 0): ?>
id = addChargeSource(false);
$('#Entry'+id+'Form').removeCol(2);
$('#Entry'+id+'Form input, #Entry'+id+'Form select').attr('disabled', true);
@@ -351,7 +365,7 @@ Configure::write('debug', '0');
' value="<?php echo FormatHelper::currency($movein['deposit']); ?>">');
//$('#Entry'+id+'Comment').val('Move-In Security Deposit');
$('#Entry'+id+'Comment').removeAttr('disabled');
<?php endif; ?>
id = addChargeSource(false);
$('#Entry'+id+'Form').removeCol(2);
@@ -376,8 +390,7 @@ Configure::write('debug', '0');
('<input type="hidden"' +
' name="data[Entry]['+id+'][amount]"' +
' value="<?php echo FormatHelper::currency($movein['prorated_rent']); ?>">');
$('#Entry'+id+'Comment').val('Move-In Rent'
<?php if ($movein['prorated']) echo "+ ' (Prorated)'" ?>);
$('#Entry'+id+'Comment').val("<?php echo($movein['prorated'] ? 'Move-In Rent (Prorated)' : ''); ?>");
$('#Entry'+id+'Comment').removeAttr('disabled');
<?php endif; ?>
@@ -386,7 +399,10 @@ Configure::write('debug', '0');
<?php if (isset($lease['id'])): ?>
$("#lease-id").val(<?php echo $lease['id']; ?>);
//$("#invoice-lease").html("<?php echo '#'.$lease['number']; ?>");
$("#invoice-lease").html('<A HREF="/pmgr/site/leases/view/' +
$("#invoice-lease").html('<A HREF="' +
"<?php echo $html->url(array('controller' => 'leases',
'action' => 'view')); ?>"
+ "/" +
"<?php echo $lease['id']; ?>" +
'">#' +
"<?php echo $lease['number']; ?>" +
@@ -400,6 +416,12 @@ Configure::write('debug', '0');
<?php else: ?>
onGridState(null, 'visible');
<?php endif; ?>
<?php if ($this->params['dev']): ?>
$('#output-debug').html('Post Output');
$('#output-debug').show();
<?php endif; ?>
});
--></script>

View File

@@ -128,7 +128,7 @@ if ($move_type !== 'out') {
),
'include' => array('Deposit'),
'exclude' => array('Balance'),
'action' => 'unoccupied',
'action' => 'vacant',
'nolinks' => true,
'limit' => 10,
)));

View File

@@ -39,7 +39,6 @@ $rows[] = array('Notice Received', FormatHelper::date($lease['notice_received_d
$rows[] = array('Closed', FormatHelper::date($lease['close_date'], true));
$rows[] = array('Deposit', FormatHelper::currency($lease['deposit']));
$rows[] = array('Rent', FormatHelper::currency($lease['rent']));
$rows[] = array('Paid Through', FormatHelper::date($lease['paid_through'], true));
$rows[] = array('Comment', $lease['comment']);
@@ -58,6 +57,9 @@ echo '<div class="infobox">' . "\n";
$rows = array();
$rows[] = array('Security Deposit:', FormatHelper::currency($outstandingDeposit));
$rows[] = array('Balance Owed:', FormatHelper::currency($outstandingBalance));
$rows[] = array('Paid Through:', FormatHelper::date($lease['paid_through_date'], false));
if ($lease['delinquent'])
$rows[] = array('Delinquent:', FormatHelper::age($lease['paid_through_date'], 'delinquent'));
echo $this->element('table',
array('class' => 'summary',
'rows' => $rows,

View File

@@ -14,6 +14,7 @@ $ledger = $entry['Ledger'];
$account = $ledger['Account'];
$tender = $entry['Tender'];
$matching = $entry['MatchingEntry'];
$double = $entry['DoubleEntry'];
$entry = $entry['LedgerEntry'];
$rows = array();
@@ -43,6 +44,10 @@ $rows[] = array('Cr/Dr', ($entry['crdr'] .
'action' => 'view',
$matching['id'])) .
')'));
$rows[] = array('Double Entry', $html->link('#'.$double['id'],
array('controller' => 'double_entries',
'action' => 'view',
$double['id'])));
$rows[] = array('Comment', $entry['comment']);
echo $this->element('table',

View File

@@ -15,7 +15,7 @@ $customer = $entry['Customer'];
$lease = $entry['Lease'];
$entry = $entry['StatementEntry'];
$Ttype = ucfirst(strtolower($transaction['type']));
$Ttype = ucwords(strtolower(str_replace('_', ' ', $transaction['type'])));
$rows = array();
$rows[] = array('ID', $entry['id']);
@@ -113,9 +113,9 @@ echo $this->element('statement_entries', array
// Grid configuration
'config' => array
('caption' => 'Entries Applied',
('caption' => $applied_caption,
//'filter' => array('id' => $entry['id']),
'exclude' => array('Entry'),
'exclude' => array('Transaction'),
)));

View File

@@ -30,7 +30,8 @@ foreach ($depositTypes AS $type) {
'separator' => '<BR>',
'onclick' => "switchSelection({$type['id']})",
'legend' => false,
'value' => $type['stats']['undeposited'] > 0 ? 'all' : 'none',
// REVISIT <AP>: 20080811; Make opt-in, or opt-out?
'value' => $type['stats']['undeposited'] > 0 ? 'none' : 'none',
'disabled' => $type['stats']['undeposited'] <= 0,
'options' => $radioOptions,
));

View File

@@ -26,7 +26,7 @@ if (empty($this->data['Tender']))
INTERNAL_ERROR('Creation of new Tender not allowed.');
echo $form->input('tender_type_id',
array('div' => 'tender',
array('div' => 'tender input',
// REVISIT <AP>: 20090810
// We're not ready to allow changing the type
// of a tender, since it will force us to deal
@@ -59,7 +59,6 @@ foreach ($types AS $type) {
for ($i=1; $i<=4; ++$i) {
if (!empty($type["data{$i}_name"])) {
echo ("<!-- data{$i}_name -->\n");
echo $form->input("type.{$type['id']}.data$i",
array('label' => $type["data{$i}_name"],
'div' => 'input text tender',

View File

@@ -58,7 +58,7 @@ echo $this->element('form_table',
"amount" => array('prefix' => 'Entry.0',
'opts' =>
array('value' =>
FormatHelper::currency($balance, false, ''),
FormatHelper::currency($balance),
),
),
"account_id" => array('prefix' => 'Entry.0',

View File

@@ -11,13 +11,14 @@ echo '<div class="transaction view">' . "\n";
$account = $transaction['Account'];
$ledger = $transaction['Ledger'];
$nsf_tender = $transaction['NsfTender'];
if (isset($transaction['Transaction']))
$transaction = $transaction['Transaction'];
$rows = array();
$rows[] = array('ID', $transaction['id']);
$rows[] = array('Type', $transaction['type']);
$rows[] = array('Type', str_replace('_', ' ', $transaction['type']));
$rows[] = array('Timestamp', FormatHelper::datetime($transaction['stamp']));
$rows[] = array('Amount', FormatHelper::currency($transaction['amount']));
$rows[] = array('Account', $html->link($account['name'],
@@ -28,7 +29,11 @@ $rows[] = array('Ledger', $html->link($ledger['name'],
array('controller' => 'ledgers',
'action' => 'view',
$ledger['id'])));
$rows[] = array('Comment', $transaction['comment']);
if (!empty($nsf_tender['id']))
$rows[] = array('NSF Tender', $html->link($nsf_tender['name'],
array('controller' => 'tenders',
'action' => 'view',
$nsf_tender['id'])));
echo $this->element('table',
array('class' => 'item transaction detail',
@@ -79,7 +84,8 @@ if ($transaction['type'] === 'INVOICE' ||
'caption' => 'Statement Entries',
'filter' => array('Transaction.id' => $transaction['id'],
'type !=' => 'VOID'),
'exclude' => array('Transaction', 'Account'),
'exclude' => array('Transaction', 'Debit', 'Credit'),
'include' => array('Amount'),
)));
}

View File

@@ -81,12 +81,14 @@ if (isset($current_lease['id'])) {
'config' => array
(
'caption' =>
('Current Lease Account ('
('Current Lease Statement ('
. $current_lease['Customer']['name']
. ')'),
'filter' => array('Lease.id' => $current_lease['id']),
'include' => array('Through'),
'exclude' => array('Customer', 'Lease', 'Unit'),
'sort_column' => 'Effective',
'sort_order' => 'DESC',
)));
}

View File

@@ -0,0 +1,447 @@
.GridHeader {
}
.Header {
width: 100%;
}
.Header th {
font-size: 100%; font-weight: bold; text-align: left;
padding: 2px;
background-image: url(images/headerbg.gif) ;
color: #FFFFFF;
width: 100%;
white-space: nowrap;
}
.HeaderLeft {
background-image: url(images/headerleft.gif);
}
.HeaderRight {
background-image: url(images/headerright.gif);
}
.HeaderButton {
background-image: url(images/headerbg.gif);
}
.HeaderButton img{
width: 17px;
}
.HeaderLeft img{
width: 14px;
}
.HeaderRight img{
width: 10px;
}
.GridHeader table {margin:0;}
.GridHeader td, tr {padding:0;}
/* Grid */
table.scroll {
table-layout: fixed;
/*border-right: 1px solid #D4D0C8;*/
margin-bottom:0;
}
table.scroll tbody tr {
background-color: #ffffff;
}
table.scroll tbody tr.alt {
background-color: #F9F9F9;
}
table.scroll tr.over td{
background-color: #E1DCF4;
}
table.scroll tr.selected td {
background: #3d84cc;
color: White;
}
table.scroll tbody td {
padding: 2px;
text-align: left;
border-bottom: 1px solid #D4D0C8;
border-left: 1px solid #D4D0C8;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
height : auto;
}
table.scroll thead th {
padding: 2px;
border-bottom: 1px solid #CBC7B8;
border-left: 1px solid #D4D0C8;
text-align: left;
font-weight: normal;
overflow: hidden;
white-space: nowrap;
background-image: url(images/grid-blue-hd.gif);
height : 17px;
}
table.scroll th div {
overflow: hidden;
/* white-space: nowrap;*/
word-wrap: break-word;
height : 17px;
}
table.scroll th span {
cursor: e-resize;
/* border-right: 1px solid #D6D2C2; */
width: 10px;
float: right;
display: block;
margin: -2px -1px -2px 0px;
height: 17px;
overflow: hidden;
white-space: nowrap;
}
/* End Grid */
/* Pager */
div.scroll {
vertical-align: top;
height: 23px;
white-space: nowrap;
text-align: center;
background-image: url(images/grid-blue-ft.gif);
}
div.scroll span {
vertical-align : top;
}
.selbox {
font-size: x-small;
vertical-align : top;
}
input.selbox{
font-size: x-small;
vertical-align : top;
}
.pgbuttons {
margin-top :2px;
}
.nav-table-left {
padding:1px;
float: left;
/* position:absolute;*/
}
.nav-table-right {
padding:1px;
float: right;
}
table.navtable {margin-bottom:0; width: auto;}
table.navtable tr{
background-image: url(images/grid-blue-ft.gif);
}
table.navtable td.nav-button {
border: 1px solid #E2ECF8;
white-space: nowrap;
}
table.navtable td.nav-hover {
border: 1px solid #83B4D8;
}
table.tbutton tr td{
border : none;
padding:0px;
}
img.jsHover { /*not used */
border: 1px solid #99CCFF;
}
/* End Pager */
/*multiselect checkbox */
.cbox {
height: 10px;
width: 10px;
/*border:1px solid #999;*/
}
/* end multiselect */
/* loading div */
div.loading {
position: absolute;
padding: 3px;
text-align: center;
font-weight: bold;
background: red;
color: white;
display: none;
}
div.loadingui {
display:none;
z-index:6000;
position:absolute;
}
div.loadingui div.msgbox {
position: relative;
z-index:6001;
left: 35%;
top:45%;
background: url(images/loading.gif) no-repeat left;
width: 100px;
border: 2px solid #B2D2FF;
text-align: right;
height: auto;
padding:2px;
margin: 0px;
}
/* end loading div */
/* toolbar */
div.userdata {
margin-top: 0px;
background-color : #EAF9F9;
height : 20px;
overflow: hidden;
}
/* end toolbar */
/*Subgrid text mode*/
.subgrid {
height: 100%;
overflow: auto;
}
.tablediv {
background-color: White;
border-spacing: 1px; /*cellspacing:poor IE support for this*/
border-collapse: separate;
width:100%; /* FF hack poor when scroling subgrid */
}
.celldiv {
float: left;
display: table-cell;
border: 1px dotted #CCCCCC;
overflow: auto;
white-space: normal;
}
.celldivth {
float: left; /*fix for buggy browsers*/
border: 1px solid #CCCCCC;
background-color: #99CCFF;
border-bottom: 1px solid #CBC7B8;
text-align: left;
overflow: auto;
}
.rowdiv {
display: table-row;
background: #F9F9F9 none;
color: #000000;
width: 100%;
overflow:auto;
}
/* End Subgrid */
/* InLine editing */
input.editable[type="text"] {
font-size: x-small;
overflow: hidden;
height : 15px;
}
input.editable[type="checkbox"] {
}
textarea.editable {
overflow: hidden;
}
select.editable {
font-size: x-small;
}
/* End Inline Editing */
/*Modal Window */
.modaltext{
text-align : left;
}
.modalwin{
border:1px solid #555555;
background:#F9F9F9;
text-align:left;
margin: 0 auto;
overflow: auto;
}
.modalhead {
background-image: url(images/grid-blue-hd.gif);
height: 20px;
}
.modalcontent {
overflow: auto;
margin-bottom: 9px;
margin-left: 5px;
}
/* end Modal window*/
/* Search window */
input.search {
margin: 2px;
width: 70px;
font-size: 10px;
color: #15428B;
}
select.search {
margin: 2px;
width: 70px;
font-size: 10px;
color: #15428B;
}
.buttonsearch {
width : 50px;
font-size: 10px;
color: #15428B;
}
/*End search */
/* Form edit */
.FormGrid {
margin: 0px;
}
.EditTable {
width: 100%;
}
.FormData { /* tr */
}
#FormError td {
font-size: 90%;
color: #FF0000;
vertical-align: top;
background-color: #f7f7f7;
}
.CaptionTD{ /* td */
font-weight: normal; text-align: left; vertical-align: top;
padding: 1px;
border-top: 1px solid #D4D0C8;
white-space: nowrap;
color: #000000;
}
.DataTD { /* td */
padding: 1px;
border-top: 1px solid #D4D0C8;
vertical-align: top;
}
.navButton{
border-top: 1px solid #D4D0C8;
border-bottom: 1px solid #D4D0C8;
text-align: center;
}
.navButton input{
width:17px;
}
input.EditButton { /* buttons are at footer tr */
font-size: 10px;
color: #15428B;
}
td.EditButton {
text-align: right;
border-top: 1px solid #D4D0C8;
border-bottom: 1px solid #D4D0C8;
}
.FormElement { /* form element - input -text,textarea,checkbox - select */
}
.FormElement {
font-size: 10px;
}
input[type="text"].FormElement{
color: #15428B;
}
input[type="checkbox"].FormElement{
width: 15px;
color: #15428B;
}
input[type="textarea"].FormElement{
color: #15428B;
}
select.FormElement {
font-size: 10px;
color: #15428B;
}
/* End Eorm edit */
/* Delete Dialog */
.DelButton > input { /* buttons are at footer tr */
font-size: 10px;
color: #15428B;
}
.DelButton {
text-align: right;
}
/* End Delete Dialog */
img.jqResize {
position:absolute;
bottom: 0px;
right: 0px;
cursor :se-resize;
}
.dirty-cell {
background: transparent url(images/dirty.gif) no-repeat 0 0;
}
#DelError td {
font-size: 90%;
color: #FF0000;
vertical-align: top;
background-color: #f7f7f7;
}
/* Tree Grid */
.tree-wrap
{
float: left;
position: relative;
height: 18px;
white-space: nowrap;
overflow: hidden;
}
.tree-minus
{
position: absolute;
height: 18px;
width: 16px;
overflow: hidden;
background: url(images/tree_minus.gif) no-repeat;
}
.tree-plus
{
position: absolute;
height: 18px;
width: 16px;
overflow: hidden;
background: url(images/tree_plus.gif) no-repeat;
}
.tree-leaf
{
position: absolute;
height: 18px;
width: 16px;
overflow: hidden;
background: url(images/tree_leaf.gif) no-repeat;
}
.treeclick
{
cursor: pointer;
}
.edit-cell {
background-color: #E1DCF4 !important;
}
.selected-row, .selected-row TD {
background-color: #3d84cc;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 879 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

View File

@@ -0,0 +1,457 @@
.GridHeader {
}
.Header {
width: 100%;
}
.Header th {
font-size: 100%; font-weight: bold; text-align: left;
padding: 2px;
background-image: url(images/headerbg.gif); color: #ffffff;
width: 100%;
white-space: nowrap;
}
.HeaderLeft {
background-image: url(images/headerleft.gif);
}
.HeaderRight {
background-image: url(images/headerright.gif);
}
.HeaderButton {
background-image: url(images/headerbg.gif);
}
.HeaderButton img{
width: 21px;
}
.HeaderLeft img{
width: 4px;
}
.HeaderRight img{
width: 9px;
}
.GridHeader table {margin:0;}
.GridHeader td, tr {padding:0;}
/* Grid */
table.scroll {
border-right: 1px solid #FFFFFF;
table-layout: fixed;
margin-bottom:0;
}
table.scroll tbody tr {
background-color: #eceae3;
}
table.scroll tbody tr.alt{
background-color: #e3dfd1;
}
table.scroll tr.over td{
background-color: #D2B48C;
}
table.scroll tr.selected td {
background-color: #c9b9b1;
color: Black;
}
table.scroll tbody tr td {
font-size: 90%;
padding: 2px;
text-align: left;
border-left: 1px solid #FFFFFF;
border-bottom: 1px solid #FFFFFF;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
height : auto;
}
table.scroll thead tr th {
font-size: 90%;
font-weight: normal;
padding: 2px;
border-left: 1px solid #FFFFFF;
border-bottom: 1px solid #FFFFFF;
text-align: left;
overflow: hidden;
white-space: nowrap;
background: url(images/grid-blue-hd.gif) transparent repeat-x;
height : 18px;
}
table.scroll thead tr th div img {
width: 9px;
}
table.scroll th div {
overflow: hidden;
/* white-space: nowrap;*/
word-wrap: break-word;
height : 18px;
}
table.scroll th span {
cursor: e-resize;
/* border-right: 1px solid #D6D2C2; */
width: 5px;
float: right;
position: relative;
display: block;
margin: -1px -1px -1px 0px;
height: 18px;
overflow: hidden;
white-space: nowrap;
}
table.scroll thead {
}
/* End Grid */
/* Pager */
div.scroll {
vertical-align: top;
height: 23px;
text-align: center;
white-space: nowrap;
background-image: url(images/grid-blue-ft.gif);
border-right: 1px solid #FFFFFF;
border-left: 1px solid #FFFFFF;
border-bottom: 1px solid #FFFFFF;
}
div.scroll span {
vertical-align : top;
}
.selbox {
font-size: x-small;
vertical-align : top;
}
input.selbox{
font-size: x-small;
vertical-align : top;
}
.pgbuttons {
margin-top :1px;
}
.nav-table-left {
padding:1px;
float: left;
margin-top:2px;
/* position:absolute;*/
}
.nav-table-right {
padding:1px;
float: right;
margin-top:2px;
}
table.navtable {margin-bottom:0; width: auto;}
table.navtable tbody tr {
background-image: url(images/grid-blue-ft.gif);
}
table.navtable tbody tr td.nav-button {
border: 1px solid #FFFFFF;
white-space: nowrap;
}
table.navtable tbody tr td.nav-hover {
border: 1px solid #c9b9b1;
}
table.tbutton tbody tr td {
border : none;
padding:0px;
}
img.jsHover { /*not used */
border: 1px solid #99CCFF;
}
/* End Pager */
/*multiselect checkbox */
.cbox {
height: 10px;
width: 10px;
/* text-align: center;*/
/*border:1px solid #999;*/
}
/* end multiselect */
/* loading div */
div.loading {
position: absolute;
padding: 3px;
text-align: center;
font-weight: bold;
background: red;
color: white;
display: none;
}
div.loadingui {
display:none;
z-index:6000;
position:absolute;
}
div.loadingui div.msgbox {
position: relative;
z-index:6001;
left: 35%;
top:45%;
background: url(images/loading.gif) no-repeat left;
width: 100px;
border: 2px solid #B2D2FF;
text-align: right;
height: auto;
padding:2px;
margin: 0px;
}
/* end loading div */
/*toolbar */
div.userdata {
margin-top: 0px;
background-color : #e3dfd1;
height : 20px;
overflow: hidden;
}
/* end toolbar */
/*Subgrid text mode*/
.subgrid {
height: 100%;
overflow: auto;
}
.tablediv {
background-color: White;
border-spacing: 1px; /*cellspacing:poor IE support for this*/
border-collapse: separate;
width:100%; /* FF hack poor when scroling subgrid */
}
.celldiv {
float: left;
display: table-cell;
border: 1px dotted #CCCCCC;
overflow: auto;
white-space: normal;
}
.celldivth {
float: left; /*fix for buggy browsers*/
border: 1px solid #CCCCCC;
background-color: #99CCFF;
border-bottom: 1px solid #CBC7B8;
text-align: left;
overflow: auto;
}
.rowdiv {
display: table-row;
background: #F9F9F9 none;
color: #000000;
width: 100%;
overflow:auto;
}
/* End Subgrid */
/* InLine editing */
input.editable[type="text"] {
font-size: x-small;
overflow: hidden;
}
input.editable[type="checkbox"] {
}
textarea.editable {
overflow: hidden;
}
select.editable {
font-size: x-small;
}
/* End Inline Editing */
/*Modal Window */
.modaltext{
text-align : left;
}
.modalwin{
border:1px solid #555555;
background:#F9F9F9;
text-align:left;
margin: 0 auto;
overflow: auto;
}
.modalhead {
background-image: url(images/grid-blue-hd.gif);
height: 20px;
}
.modalcontent {
overflow: auto;
margin-bottom: 9px;
margin-left: 5px;
}
/* end Modal window*/
/* Search window */
input.search {
margin: 2px;
width: 70px;
font-size: 10px;
color: #15428B;
}
select.search {
margin: 2px;
width: 70px;
font-size: 10px;
color: #15428B;
}
.buttonsearch {
width : 50px;
font-size: 10px;
color: #15428B;
}
/*End search */
/* Form edit */
.FormGrid {
margin: 0px;
}
.EditTable {
width: 100%;
}
.FormData { /* tr */
}
#FormError td {
font-size: 90%;
color: #FF0000;
vertical-align: top;
background-color: #f7f7f7;
}
.CaptionTD{ /* td */
font-weight: normal; text-align: left; vertical-align: top;
padding: 1px;
border-top: 1px solid #D4D0C8;
white-space: nowrap;
color: #000000;
}
.DataTD { /* td */
padding: 1px;
border-top: 1px solid #D4D0C8;
vertical-align: top;
}
.navButton{
border-top: 1px solid #D4D0C8;
border-bottom: 1px solid #D4D0C8;
text-align: center;
}
.navButton input{
width:19px;
}
input.EditButton { /* buttons are at footer tr */
font-size: 10px;
color: #15428B;
}
td.EditButton {
text-align: right;
border-top: 1px solid #D4D0C8;
border-bottom: 1px solid #D4D0C8;
}
.FormElement { /* form element - input -text,textarea,checkbox - select */
}
.FormElement {
font-size: 10px;
}
input[type="text"].FormElement{
color: #15428B;
}
input[type="checkbox"].FormElement{
width: 15px;
color: #15428B;
}
input[type="textarea"].FormElement{
color: #15428B;
}
select.FormElement {
font-size: 10px;
color: #15428B;
}
/* End Eorm edit */
/* Delete Dialog */
.DelButton > input { /* buttons are at footer tr */
font-size: 10px;
color: #15428B;
}
.DelButton {
text-align: right;
}
/* End Delete Dialog */
img.jqResize {
position:absolute;
bottom: 0px;
right: 0px;
cursor :se-resize;
}
.dirty-cell {
background: transparent url(images/dirty.gif) no-repeat 0 0;
}
#DelError td {
font-size: 90%;
color: #FF0000;
vertical-align: top;
background-color: #f7f7f7;
}
/* Tree Grid */
.tree-wrap
{
float: left;
position: relative;
height: 18px;
white-space: nowrap;
overflow: hidden;
}
.tree-minus
{
position: absolute;
height: 18px;
width: 16px;
overflow: hidden;
background: url(images/tree_minus.gif) no-repeat;
}
.tree-plus
{
position: absolute;
height: 18px;
width: 16px;
overflow: hidden;
background: url(images/tree_plus.gif) no-repeat;
}
.tree-leaf
{
position: absolute;
height: 18px;
width: 16px;
overflow: hidden;
background: url(images/tree_leaf.gif) no-repeat;
}
.treeclick
{
cursor: pointer;
}
.edit-cell {
background-color: #D2B48C !important;
}
.selected-row, .selected-row TD {
background-color: #c9b9b1;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Some files were not shown because too many files have changed in this diff Show More