Compare commits

..

3 Commits

Author SHA1 Message Date
abijah
861eabc6b5 Forgot the new files on the last checkin
git-svn-id: file:///svn-source/pmgr/branches/statements_20090623@185 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-06-24 17:18:25 +00:00
abijah
6baa7fa6e6 Nowhere near complete, but must snapshot prior to my Boston trip.
git-svn-id: file:///svn-source/pmgr/branches/statements_20090623@184 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-06-24 17:16:39 +00:00
abijah
8cb45ad1d6 Branch for adding the concepts of statements. I suspect I'll end up killing it.
git-svn-id: file:///svn-source/pmgr/branches/statements_20090623@183 97e9348a-65ac-dc4b-aefc-98561f571b83
2009-06-24 17:14:39 +00:00
17 changed files with 747 additions and 581 deletions

View File

@@ -633,6 +633,9 @@ CREATE TABLE `pmgr_customers` (
-- If NULL, rely on the contact info exclusively
`name` VARCHAR(80) DEFAULT NULL,
-- Statement of charges/payments
`statement_id` INT(10) UNSIGNED NOT NULL,
-- Primary Contact... every customer must have one
-- (and presumably, most customers will _be_ one).
-- REVISIT <AP> 20090612:
@@ -694,11 +697,13 @@ CREATE TABLE `pmgr_leases` (
-- Allow user to specify their own lease numbers
-- If NULL, `id` will be used
`number` VARCHAR(20) DEFAULT NULL,
-- `number` VARCHAR(20) DEFAULT NULL,
`number` INT(10) UNSIGNED DEFAULT NULL,
`lease_type_id` INT(10) UNSIGNED NOT NULL,
`unit_id` INT(10) UNSIGNED NOT NULL,
`customer_id` INT(10) UNSIGNED NOT NULL,
`statement_id` INT(10) UNSIGNED NOT NULL,
`late_schedule_id` INT(10) UNSIGNED DEFAULT NULL,
`lease_date` DATE NOT NULL,
@@ -838,7 +843,7 @@ CREATE TABLE `pmgr_accounts` (
-- For LIABILITY, EQUITY, and INCOME, the opposite
-- is true, with reconciliations posted, under
-- normal circumstances, when a debit occurs.
`trackable` INT UNSIGNED DEFAULT 1,
`trackable` INT UNSIGNED DEFAULT 0,
-- Security Level
`level` INT UNSIGNED DEFAULT 1,
@@ -859,17 +864,15 @@ INSERT INTO `pmgr_accounts` (`type`, `name`, `trackable`)
('ASSET', 'Invoice', 1),
('ASSET', 'Receipt', 1),
('LIABILITY', 'A/P', 1),
('LIABILITY', 'Tax', 1),
('LIABILITY', 'Tax', 0),
('LIABILITY', 'Customer Credit', 1),
('ASSET', 'Bank', 1),
('ASSET', 'Cash', 1),
('ASSET', 'Check', 1),
('ASSET', 'Money Order', 1),
('ASSET', 'Bank', 0),
('ASSET', 'Till', 0),
('LIABILITY', 'Security Deposit', 1),
('INCOME', 'Rent', 1),
('INCOME', 'Late Charge', 1),
('EXPENSE', 'Concession', 1),
('EXPENSE', 'Bad Debt', 1);
('INCOME', 'Rent', 0),
('INCOME', 'Late Charge', 0),
('EXPENSE', 'Concession', 0),
('EXPENSE', 'Bad Debt', 0);
UNLOCK TABLES;
@@ -934,10 +937,6 @@ DROP TABLE IF EXISTS `pmgr_transactions`;
CREATE TABLE `pmgr_transactions` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
-- `type` ENUM('INVOICE',
-- 'RECEIPT')
-- NOT NULL,
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`through_date` DATE DEFAULT NULL,
`due_date` DATE DEFAULT NULL,
@@ -993,6 +992,21 @@ CREATE TABLE `pmgr_reconciliations` (
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ##
-- ## MONETARY
-- ##
-- ----------------------------------------------------------------------
-- ----------------------------------------------------------------------
-- TABLE pmgr_monetary_sources
@@ -1047,6 +1061,59 @@ UNLOCK TABLES;
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ######################################################################
-- ##
-- ## STATEMENTS
-- ##
-- ## Statements are supplementary financial information. They should
-- ## NEVER be used in financial calculations, but rather are secondary
-- ## information, to be created where needed.
-- ----------------------------------------------------------------------
-- ----------------------------------------------------------------------
-- TABLE pmgr_statements
DROP TABLE IF EXISTS `pmgr_statements`;
CREATE TABLE `pmgr_statements` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`comment` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ----------------------------------------------------------------------
-- ----------------------------------------------------------------------
-- TABLE pmgr_statement_entries
DROP TABLE IF EXISTS `pmgr_statement_entries`;
CREATE TABLE `pmgr_statement_entries` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`type` ENUM('CHARGE',
'PAYMENT')
NOT NULL,
`statement_id` INT(10) UNSIGNED NOT NULL,
`ledger_entry_id` INT(10) UNSIGNED NOT NULL,
`amount` FLOAT(12,2) NOT NULL,
`comment` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- ######################################################################
-- ######################################################################

View File

@@ -4,8 +4,6 @@ use DBI;
use Data::Dumper;
use File::Copy;
my $use_ir = 1;
# Internally adjust all numbers coming from the database to
# be in inches. Not necessary to go to this detail, but the
# actual units used is irrelevant, provided everything is to
@@ -482,10 +480,8 @@ foreach $row (@$result) {
}
# For compatibility, while deciding on account names
if (!defined $newdb{'lookup'}{'account'}{'Cash'}) {
$newdb{'lookup'}{'account'}{'Cash'}
= $newdb{'lookup'}{'account'}{'Till'};
}
$newdb{'lookup'}{'account'}{'Cash'}
= $newdb{'lookup'}{'account'}{'Till'};
$newdb{'lookup'}{'charge_type'} = {};
$newdb{'lookup'}{'charge_type'}{'Rent'} =
@@ -512,9 +508,9 @@ $newdb{'lookup'}{'payment_type'} = {};
$newdb{'lookup'}{'payment_type'}{1}
= { 'name' => 'Cash', 'account_name' => 'Cash', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Cash'} };
$newdb{'lookup'}{'payment_type'}{2}
= { 'name' => 'Check', 'account_name' => 'Check', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Check'} };
= { 'name' => 'Check', 'account_name' => 'Cash', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Check'} };
$newdb{'lookup'}{'payment_type'}{3}
= { 'name' => 'Money Order', 'account_name' => 'Money Order', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Money Order'} };
= { 'name' => 'Money Order', 'account_name' => 'Cash', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Money Order'} };
$newdb{'lookup'}{'payment_type'}{4}
= { 'name' => 'ACH', 'account_name' => 'Bank', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'ACH'} };
$newdb{'lookup'}{'payment_type'}{12}
@@ -687,6 +683,13 @@ foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}
= { 'name' => "$row->{'LastName'}, $row->{'FirstName'}" };
# Each tenants receives a statement
addRow('statements',
{ 'comment' => ("Statement for " .
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'name'}) });
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'statement_id'} =
$newdb{'tables'}{'statements'}{'autoid'};
addRow('contacts',
{ 'first_name' => $row->{'FirstName'},
'middle_name' => $row->{'MiddleName'},
@@ -699,8 +702,8 @@ foreach $row (@{query($sdbh, $query)}) {
addRow('customers',
{ 'name' => "$row->{'LastName'}, $row->{'FirstName'}",
'primary_contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'},
});
'statement_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'statement_id'},
'primary_contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'} });
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'} =
$newdb{'tables'}{'customers'}{'autoid'};
@@ -814,15 +817,23 @@ foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'ledger'} = {};
$query = "SELECT L.*, A.TenantID FROM TenantLedger L LEFT JOIN `Access` A ON A.LedgerID = L.LedgerID WHERE L.UnitID <> 'POS\$' ORDER BY L.DateIn";
$query = "SELECT L.*, A.TenantID FROM TenantLedger L LEFT JOIN `Access` A ON A.LedgerID = L.LedgerID WHERE L.UnitID <> 'POS\$' ORDER BY L.LedgerID";
foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}
= { 'customer_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'} };
= { 'customer_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'},
'customer_statement_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'statement_id'},
};
# Each lease receives a statement
addRow('statements',
{ 'comment' => ("Statement for Lease #" . $row->{'LedgerID'}) });
$newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_statement_id'} =
$newdb{'tables'}{'statements'}{'autoid'};
addRow('leases',
{ #'number' => $newdb{'tables'}{'leases'}{'autoid'}+1,
'number' => $row->{'LedgerID'},
{ 'number' => $row->{'LedgerID'},
'statement_id' => $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_statement_id'},
'lease_type_id' => $newdb{'tables'}{'lease_types'}{'autoid'},
'unit_id' => $newdb{'lookup'}{'unit'}{$row->{'UnitID'}}{'id'},
'customer_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'},
@@ -834,6 +845,7 @@ foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_id'} =
$newdb{'tables'}{'leases'}{'autoid'};
}
@@ -849,126 +861,66 @@ foreach $row (@{query($sdbh, $query)}) {
##
######################################################################
## Invoices
## Charges
$newdb{'lookup'}{'charge'} = {};
$query = "SELECT *, ChargeAmount+TaxAmount AS InvoiceAmount FROM Charges ORDER BY ChargeID";
$query = "SELECT * FROM Charges ORDER BY ChargeID";
foreach $row (@{query($sdbh, $query)}) {
addRow('transactions',
{ 'stamp' => datefmt($row->{'ChargeDate'}),
'through_date' => datefmt($row->{'EndDate'}) });
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}
= { 'invoice' =>
{ 'tx' => $newdb{'tables'}{'transactions'}{'autoid'},
'lease_id' => $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_id'},
'customer_id' => $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'customer_id'},
'amount' => $row->{'InvoiceAmount'},
} };
= { 'tx' => $newdb{'tables'}{'transactions'}{'autoid'},
'amount' => $row->{'ChargeAmount'},
'tax_amount' => $row->{'TaxAmount'},
'lease_statement_id' => $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_statement_id'},
'customer_statement_id' => $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'customer_statement_id'},
};
# Invoice must debit the A/R ledger...
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'debit_ledger_id'}
= $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'};
# ...and credit the Invoice ledger.
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'credit_ledger_id'}
= $newdb{'lookup'}{'account'}{'Invoice'}{'ledger_id'};
# Create the invoice entry
# debit: A/R credit: Invoice
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'tx'},
'debit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'credit_ledger_id'},
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'lease_id'},
'amount' => $row->{'InvoiceAmount'},
'comment' => "Invoice: Charge: $row->{'ChargeID'}" });
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
}
######################################################################
## Charges
$query = "SELECT * FROM Charges ORDER BY ChargeID";
foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tx'}
= $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'tx'};
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}
= $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'customer_id'};
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}
= $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_id'};
# Charge must credit the invoice ledger...
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'}
= $newdb{'lookup'}{'charge_type'}{$row->{'ChargeDescription'}}{'ledger_id'};
# ...and debit the A/R ledger.
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'}
= $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'credit_ledger_id'};
# Add the charge entry
# debit: Invoice credit: Rent/LateCharge/Etc
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tx'},
'debit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'},
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'charge_type'}{$row->{'ChargeDescription'}}{'ledger_id'},
'amount' => $row->{'ChargeAmount'},
'comment' => "Charge: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}" });
foreach ($newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_statement_id'},
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_statement_id'}) {
addRow('statement_entries',
{ 'type' => 'CHARGE',
'statement_id' => $_,
'ledger_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'},
'amount' => $row->{'ChargeAmount'} });
}
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
# Reconcile the Invoice account. Our two entries look like:
# debit: Invoice credit: Rent/LateCharge/Etc
# debit: A/R credit: Invoice
# Since this is from the perspective of the Invoice account,
# the credit entry is the Invoice<->A/R, and the debit
# entry is the actual charge ledger entry.
addRow('reconciliations',
{ 'debit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ledger_entry_id'},
'credit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'},
'amount' => $row->{'ChargeAmount'},
});
next unless $row->{'TaxAmount'};
# Add the tax charge entry
# debit: Invoice credit: Tax
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tx'},
'debit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Tax'}{'ledger_id'},
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'charge_type'}{'Tax'}{'ledger_id'},
'amount' => $row->{'TaxAmount'},
'comment' => "Tax for ChargeID:$row->{'ChargeID'}" });
'comment' => undef });
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tax_ledger_entry_id'}
foreach ($newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_statement_id'},
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_statement_id'}) {
addRow('statement_entries',
{ 'type' => 'CHARGE',
'statement_id' => $_,
'ledger_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'},
'amount' => $row->{'TaxAmount'} });
}
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tax_entry'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
# Reconcile the Invoice account. Our two entries look like:
# debit: Invoice credit: Tax
# debit: A/R credit: Invoice
# Since this is from the perspective of the Invoice account,
# the credit entry is the Invoice<->A/R, and the debit
# entry is the actual tax ledger entry.
addRow('reconciliations',
{ 'debit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tax_ledger_entry_id'},
'credit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'},
'amount' => $row->{'TaxAmount'},
});
}
@@ -977,168 +929,42 @@ foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'receipt'} = {};
############################################################
############################################################
############################################################
# REVISIT <AP> 20090630
# Handling of receipts is all backwards. The way things
# _should_ look if someone provides Cash and Check to pay
# rent & tax for two units (50 on UnitA and 40 UnitB):
#
# RENT TAX INVOICE A/R RECEIPT CASH CHECK
# ------- ------- ------- ------- ------- ------- -------
# |50 | 50| | | | |
# | | 5 5| | | | |
# | | |55 55| | | |
# |40 | 40| | | | |
# | | 4 4| | | | |
# | | |44 44| | | |
# | | | | |79 | 79|
# | | | | |20 20| |
# | | | |99 99| | |
# | | | | | | |
#
# HOWEVER,
# Our current implementation MUST match LedgerEntry to
# LedgerEntry for reconcile purposes. Thus, although there
# is a way to reconcile that $50 was received, there is no
# way to flag the payment as being for UnitA, unless we
# either rely on the charge to determine the fact (a
# solution that has proven very messy), or we add it to
# the reconciliations table. The hope, for simplicity's
# sake, was to ensure there was a single ledger entry in
# A/R for each payment that could correspond to a lease/unit,
# so that no A/R ledger entry ever represented payment for
# more than one lease. However, to do so, our ledgers
# appear like this instead:
#
# RENT TAX INVOICE A/R RECEIPT CASH CHECK
# ------- ------- ------- ------- ------- ------- -------
# |50 | 50| | | | |
# | | 5 5| | | | |
# | | |55 55| | | |
# |40 | 40| | | | |
# | | 4 4| | | | |
# | | |44 44| | | |
# | | | | |79 | 79|
# | | | | |20 20| |
# | | | |50 50| | |
# | | | | 5 5| | |
# | | | |40 40| | |
# | | | | 4 4| | |
# | | | | | | |
#
# There is another possible solution, although it is
# very probably ledger overkill (even invoice/receipt
# already fall into the overkill category).
#
# RENT TAX INVOICE A/R MERGE RECEIPT CASH CHECK
# ------- ------- ------- ------- ------- ------- ------- -------
# |50 | 50| | | | | |
# | | 5 5| | | | | |
# | | |55 55| | | | |
# |40 | 40| | | | | |
# | | 4 4| | | | | |
# | | |44 44| | | | |
# | | | | | |79 | 79|
# | | | | | |20 20| |
# | | | | |50 50| | |
# | | | | | 5 5| | |
# | | | | |40 40| | |
# | | | | | 4 4| | |
# | | | |99 99| | | |
# | | | | | | | |
#
# I might opt for this last option, but ultimately, none
# of these are really correct. We need a better solution.
# Until then, I'll go with the easiest.
############################################################
############################################################
############################################################
# Sitelink splits one physical payment into multiple "payments" to match each charge.
# The solution here is kludgy, but for our cases at least, it brings those pseudo-payments
# back into a single one. This presumes there is only one PaymentType per receipt.
$query =
"SELECT R.ReceiptNum, R.ReceiptDate, P.PaymentType, P.CheckNum, SUM(P.PaymentAmount) AS ReceiptAmount" .
" FROM Receipts R, Payments P" .
"SELECT R.ReceiptNum, C.LedgerID, R.ReceiptDate" .
" FROM Receipts R, Payments P, Charges C" .
" WHERE P.ReceiptNum = R.ReceiptNum" .
" GROUP BY R.ReceiptNum, R.ReceiptDate, P.PaymentType, P.CheckNum" .
" ORDER BY R.ReceiptNum, P.PaymentType";
" AND C.ChargeID = P.ChargeID" .
" GROUP BY R.ReceiptNum, C.LedgerID, R.ReceiptDate" .
" ORDER BY R.ReceiptNum";
foreach $row (@{query($sdbh, $query)}) {
# if ($newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}) {
# next;
# }
if (!$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}) {
addRow('transactions',
{ 'stamp' => datefmt($row->{'ReceiptDate'}),
'through_date' => undef });
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}
= {'tx' => $newdb{'tables'}{'transactions'}{'autoid'} };
}
if ($row->{'ReceiptDate'} =~ m%3/25/2009%) {
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'}
= $newdb{'ids'}{'monetary_source'}{'Closing'};
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'account_name'}
= 'Bank';
}
else {
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'}
= $newdb{'ids'}{'monetary_source'}{
$newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'name'}
};
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'account_name'}
= $newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'account_name'};
if ($newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}) {
die unless ($newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'customer_id'}
== $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'customer_id'});
push(@{$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'ledgers'}},
$newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'ledger_id'});
#print Dumper $receipt_map{$row->{'ReceiptNum'}};
next;
}
# Set up a monetary source for the receipt,
if (!$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'}) {
my $name = $newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'name'};
if ($name eq 'Check') {
$name = 'Check #' . $row->{'CheckNum'};
}
addRow('monetary_sources',
{ 'monetary_type_id' => $newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'monetary_type'}{'id'},
'name' => $name,
'comment' => "Receipt:$row->{'ReceiptNum'}; Payment:$row->{'PaymentType'}; Check:$row->{'CheckNum'}" });
addRow('transactions',
{ 'stamp' => datefmt($row->{'ReceiptDate'}),
'through_date' => undef });
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'}
= $newdb{'tables'}{'monetary_sources'}{'autoid'};
}
# Receipt must debit the "money" asset (bank, cash, check, etc)...
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_ledger_id'}
= $newdb{'lookup'}{'account'}{
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'account_name'}
}{'ledger_id'};
# ...and credit the Receipt ledger
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'}
= $newdb{'lookup'}{'account'}{'Receipt'}{'ledger_id'};
# NOTE THE ABOVE LARGE COMMENT BLOCK
# The choice of credit/debit ledgers does not mirror the
# choices for invoice. This _should_ be A/R to Receipt,
# but it is Money to Receipt instead.
# debit: Cash/Check/Etc credit: Receipt
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'},
'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'tx'},
'debit_ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'},
'amount' => $row->{'ReceiptAmount'},
'comment' => "Receipt: $row->{'ReceiptNum'}; " });
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}
= {'tx' => $newdb{'tables'}{'transactions'}{'autoid'},
# NOTE: Use of 'lease_id' would be invalid, since the
# receipt could be for multiple leases. 'customer_id'
# could be OK, but better to avoid this issue all
# together by excluding them both from this lookup.
'ledgers' => [ $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'ledger_id'} ] };
}
# sub idkeys { [ sort( {$a <=> $b} keys(%{$_[0]})) ] }
# $Data::Dumper::Sortkeys = \&idkeys;
######################################################################
## Payments
@@ -1150,68 +976,104 @@ foreach $row (@{query($sdbh, $query)})
{
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}} = {};
# NOTE THE ABOVE LARGE COMMENT BLOCK
# The choice of credit/debit ledgers does not mirror the
# choices for charges. This _should_ be Money to Receipt,
# but it is A/R to Receipt instead.
if ($row->{'PaymentDate'} =~ m%3/25/2009%) {
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'monetary_source_id'}
= $newdb{'ids'}{'monetary_source'}{'Closing'};
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'account_name'}
= 'Bank';
}
else {
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'monetary_source_id'}
= $newdb{'ids'}{'monetary_source'}{
$newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'name'}
};
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'account_name'}
= $newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'account_name'};
}
if (!$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'}) {
if (!defined $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'monetary_source_id'}) {
my $name;
$name = $newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'name'};
if ($name eq 'Check') {
$name = 'Check #' . $row->{'CheckNum'};
}
addRow('monetary_sources',
{ 'monetary_type_id' => $newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'monetary_type'}{'id'},
'name' => $name,
'comment' => "Payment: $row->{'PaymentID'}; Type: $row->{'PaymentType'}; Check: $row->{'CheckNum'}; $row->{'Memo'}" });
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'monetary_source_id'}
= $newdb{'tables'}{'monetary_sources'}{'autoid'};
}
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'}
= $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'monetary_source_id'};
}
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'debit_ledger_id'}
= $newdb{'lookup'}{'account'}{$newdb{'lookup'}{'payment'}{
$row->{'PaymentID'}}{'account_name'}
}{'ledger_id'};
# Sitelink splits one physical payment into multiple "payments" to match each charge
# This is kludgy, but for our cases at least, brings those pseudo-payments back into
# a single one. It presumes that there is only one PaymentType per receipt.
if (!$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}) {
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'monetary_source_id'},
'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'tx'},
'debit_ledger_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'},
'amount' => 0,
'comment' => "Receipt: $row->{'ReceiptNum'}; " });
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
addRow('statement_entries',
{ 'type' => 'PAYMENT',
'statement_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_statement_id'},
'ledger_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'},
'amount' => 0 });
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'statement_entry_id'}
= $newdb{'tables'}{'statement_entries'}{'autoid'};
}
# Ensure Receipt has the right customer
$newdb{'tables'}{'ledger_entries'}{'rows'}[
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}
]{'customer_id'} = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'};
]{'amount'} += $row->{'PaymentAmount'};
# Payment must debit the associated receipt ledger (which should be Receipt)...
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'debit_ledger_id'}
= $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'};
$newdb{'tables'}{'ledger_entries'}{'rows'}[
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}
]{'comment'} .= 'P:'.$row->{'PaymentID'} . '-&gt;C:' . $row->{'ChargeID'} . '; ';
# ...and credit the A/R ledger.
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'credit_ledger_id'}
= $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'};
$newdb{'tables'}{'statement_entries'}{'rows'}[
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'statement_entry_id'}
]{'amount'} += $row->{'PaymentAmount'};
# Add the payment entry
# debit: Receipt credit: A/R
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'tx'},
'debit_ledger_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'credit_ledger_id'},
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'},
'amount' => $row->{'PaymentAmount'},
'comment' => "Receipt: $row->{'ReceiptNum'}; Charge: $row->{'ChargeID'}; Payment: $row->{'PaymentID'}" });
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
# Reconcile the Receipt account. Our two entries look like:
# debit: Cash/Check/Etc credit: Receipt
# debit: Receipt credit: A/R
# Since this is from the perspective of the Receipt account,
# the debit entry is the Receipt<->A/R, and the credit
# entry is the actual receipt ledger entry.
addRow('reconciliations',
{ 'debit_ledger_entry_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'ledger_entry_id'},
'credit_ledger_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'},
'amount' => $row->{'PaymentAmount'},
});
addRow('statement_entries',
{ 'type' => 'PAYMENT',
'statement_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_statement_id'},
'ledger_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'},
'amount' => $row->{'PaymentAmount'} });
# OK, now that the receipt is reconciled, update
# payment to reference the true ledger_entry_id
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'ledger_entry_id'}
= $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'};
# Figure out how much of the charge can be reconciled
my $reconcile_amount = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'amount'};
#print STDERR Dumper($newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}); exit;
my $reconcile_amount = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'amount'};
$reconcile_amount = $row->{'PaymentAmount'} if $row->{'PaymentAmount'} <= $reconcile_amount;
# Reconcile the A/R account. Our two entries look like:
# debit: Receipt credit: A/R
# debit: A/R credit: Invoice
# Since this is from the perspective of the A/R account,
# the debit entry is the Invoice<->A/R, and the credit
# entry is the Receipt<->A/R.
# Reconcile the A/R account. Since this is from the perspective
# of the A/R, charge is the debit, and payment is the credit
addRow('reconciliations',
{ 'debit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'},
'credit_ledger_entry_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'ledger_entry_id'},
'amount' => $reconcile_amount,
});
{ 'debit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ledger_entry_id'},
'credit_ledger_entry_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'ledger_entry_id'},
'amount' => $reconcile_amount,
});
# Update the transaction to use the memo from this payment
if ($row->{'Memo'}) {
@@ -1229,83 +1091,83 @@ my %fake =
('customer_id' => 4,
'ledger_id' => 4,
'invoice' => [ { 'id' => 1000,
'date' => '2009-05-05',
'entry' => [ { 'id' => 2100,
'account_name' => 'Security Deposit',
'amount' => 10,
'tax' => 0 },
],
},
{ 'id' => 1001,
'date' => '2009-05-01',
'thru' => '2009-05-31',
'entry' => [ { 'id' => 2110,
'account_name' => 'Rent',
'amount' => 100,
'tax' => 5 },
],
},
{ 'id' => 1002,
'date' => '2009-05-11',
'entry' => [ { 'id' => 2120,
'account_name' => 'Late Charge',
'amount' => 25,
'tax' => 0 },
],
},
],
'date' => '2009-05-05',
'entry' => [ { 'id' => 2100,
'account_name' => 'Security Deposit',
'amount' => 10,
'tax' => 0 },
],
},
{ 'id' => 1001,
'date' => '2009-05-01',
'thru' => '2009-05-31',
'entry' => [ { 'id' => 2110,
'account_name' => 'Rent',
'amount' => 100,
'tax' => 5 },
],
},
{ 'id' => 1002,
'date' => '2009-05-11',
'entry' => [ { 'id' => 2120,
'account_name' => 'Late Charge',
'amount' => 25,
'tax' => 0 },
],
},
],
'receipt' => [ { 'id' => 2000,
'date' => '2009-05-15',
'entry' => [ { 'id' => 2200,
'track' => [ { 'debit'=>2100, 'amount'=>10 },
{ 'debit'=>2110, 'amount'=>5 } ],
'type' => 1,
'amount' => 15 },
{ 'id' => 2201,
'track' => [ { 'debit'=>2110, 'amount'=>10 } ],
'type' => 2,
'amount' => 10 },
{ 'id' => 2202,
'track' => [ { 'debit'=>2110, 'amount'=>5 } ],
'type' => 3,
'amount' => 5 },
],
},
{ 'id' => 2001,
'date' => '2009-05-18',
'entry' => [ { 'id' => 2210,
'track' => [ { 'debit'=>2110, 'amount'=>30 } ],
'type' => 5,
'amount' => 30 },
{ 'id' => 2211,
'track' => [ { 'debit'=>2110, 'amount'=>20 } ],
'type' => 6,
'amount' => 20 },
],
},
{ 'id' => 2002,
'date' => '2009-05-22',
'entry' => [ { 'id' => 2220,
'track' => [ { 'debit'=>2110, 'amount'=>10 } ],
'type' => 1,
'amount' => 10 },
{ 'id' => 2221,
'track' => [ { 'debit'=>2110, 'amount'=>5 } ],
'type' => 2,
'amount' => 5 },
{ 'id' => 2222,
'track' => [ { 'debit'=>2110, 'amount'=>15 },
{ 'debit'=>2111, 'amount'=>5 },
{ 'debit'=>2120, 'amount'=>5 } ],
'type' => 7,
'amount' => 25 },
{ 'id' => 2223,
'track' => [ { 'debit'=>2120, 'amount'=>20 } ],
'type' => 8,
'amount' => 30 },
],
},
],
'date' => '2009-05-15',
'entry' => [ { 'id' => 2200,
'track' => [ { 'debit'=>2100, 'amount'=>10 },
{ 'debit'=>2110, 'amount'=>5 } ],
'type' => 1,
'amount' => 15 },
{ 'id' => 2201,
'track' => [ { 'debit'=>2110, 'amount'=>10 } ],
'type' => 2,
'amount' => 10 },
{ 'id' => 2202,
'track' => [ { 'debit'=>2110, 'amount'=>5 } ],
'type' => 3,
'amount' => 5 },
],
},
{ 'id' => 2001,
'date' => '2009-05-18',
'entry' => [ { 'id' => 2210,
'track' => [ { 'debit'=>2110, 'amount'=>30 } ],
'type' => 5,
'amount' => 30 },
{ 'id' => 2211,
'track' => [ { 'debit'=>2110, 'amount'=>20 } ],
'type' => 6,
'amount' => 20 },
],
},
{ 'id' => 2002,
'date' => '2009-05-22',
'entry' => [ { 'id' => 2220,
'track' => [ { 'debit'=>2110, 'amount'=>10 } ],
'type' => 1,
'amount' => 10 },
{ 'id' => 2221,
'track' => [ { 'debit'=>2110, 'amount'=>5 } ],
'type' => 2,
'amount' => 5 },
{ 'id' => 2222,
'track' => [ { 'debit'=>2110, 'amount'=>15 },
{ 'debit'=>2111, 'amount'=>5 },
{ 'debit'=>2120, 'amount'=>5 } ],
'type' => 7,
'amount' => 25 },
{ 'id' => 2223,
'track' => [ { 'debit'=>2120, 'amount'=>20 } ],
'type' => 8,
'amount' => 30 },
],
},
],
);
@@ -1332,7 +1194,7 @@ sub fakeTesting {
}
addRow('transactions',
{ 'stamp' => '2009-04-30' });
{ 'stamp' => '2009-05-10' });
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
@@ -1359,10 +1221,10 @@ sub fakeTesting {
1);
foreach my $e (@{$tx->{'entry'}}) {
my $ircrdr = ($ir eq 'invoice') ? 'Invoice' : 'Receipt';
my $crdr = ($ir eq 'invoice') ? 'Invoice' : 'Receipt';
my $dr = ($ir eq 'invoice') ? 'A/R' : 'Cash';
$crdr = $dr;
my $cr = ($ir eq 'invoice') ? $e->{'account_name'} : 'A/R';
my $crdr;
my $monetary_source_id;
if (defined $e->{'type'}) {
@@ -1376,41 +1238,29 @@ sub fakeTesting {
$monetary_source_id = $newdb{'ids'}{'monetary_source'}{'internal'};
}
$crdr = $dr;
if ($use_ir) {
$crdr = $ircrdr;
addRow('ledger_entries',
{ 'id' => $e->{'id'}+10000,
'monetary_source_id' => ($ir eq 'invoice' ? undef : $monetary_source_id),
'transaction_id' => $tx->{'id'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{$dr}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'},
'customer_id' => $fake{'customer_id'},
#'lease_id' => $fake{'lease_id'},
'amount' => $e->{'amount'},
'comment' => "Fake $ir entry" },
1);
}
addRow('ledger_entries',
{ 'id' => $e->{'id'},
'monetary_source_id' => ($ir eq 'invoice' ? $monetary_source_id : undef),
'monetary_source_id' => $monetary_source_id,
'transaction_id' => $tx->{'id'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{$cr}{'ledger_id'},
'customer_id' => $fake{'customer_id'},
'lease_id' => $fake{'lease_id'},
'amount' => $e->{'amount'},
'comment' => "Fake Entry" },
'comment' => "Fake $ir entry" },
1);
if ($use_ir) {
addRow('reconciliations',
{ 'debit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 10000 : 0),
'credit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 0 : 10000),
'amount' => $e->{'amount'},
});
}
# addRow('ledger_entries',
# { 'id' => $e->{'id'},
# 'monetary_source_id' => $monetary_source_id,
# 'transaction_id' => $tx->{'id'},
# 'debit_ledger_id' => $newdb{'lookup'}{'account'}{$dr}{'ledger_id'},
# 'credit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'},
# 'customer_id' => $fake{'customer_id'},
# 'lease_id' => $fake{'lease_id'},
# 'amount' => $e->{'amount'},
# 'comment' => "Fake $ir entry" },
# 1);
foreach my $t (@{$e->{'track'}}) {
addRow('reconciliations',
@@ -1422,41 +1272,29 @@ sub fakeTesting {
next unless $e->{'tax'};
$crdr = $dr;
if ($use_ir) {
$crdr = $ircrdr;
addRow('ledger_entries',
{ 'id' => $e->{'id'}+10001,
'monetary_source_id' => ($ir eq 'invoice' ? undef : $monetary_source_id),
'transaction_id' => $tx->{'id'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{$dr}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'},
'customer_id' => $fake{'customer_id'},
#'lease_id' => $fake{'lease_id'},
'amount' => $e->{'tax'},
'comment' => "Fake Tax Invoice Entry" },
1);
}
addRow('ledger_entries',
{ 'id' => $e->{'id'}+1,
'monetary_source_id' => ($ir eq 'invoice' ? $monetary_source_id : undef),
'monetary_source_id' => $monetary_source_id,
'transaction_id' => $tx->{'id'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Tax'}{'ledger_id'},
'customer_id' => $fake{'customer_id'},
'lease_id' => $fake{'lease_id'},
'amount' => $e->{'tax'},
'comment' => "Fake Tax Entry" },
'amount' => $e->{'amount'},
'comment' => "Fake Tax" },
1);
if ($use_ir) {
addRow('reconciliations',
{ 'debit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 10001 : 1),
'credit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 1 : 10001),
'amount' => $e->{'tax'},
});
}
# addRow('ledger_entries',
# { 'id' => $e->{'id'}+1,
# 'monetary_source_id' => $monetary_source_id,
# 'transaction_id' => $tx->{'id'},
# 'debit_ledger_id' => $newdb{'lookup'}{'account'}{$dr}{'ledger_id'},
# 'credit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'},
# 'customer_id' => $fake{'customer_id'},
# 'lease_id' => $fake{'lease_id'},
# 'amount' => $e->{'tax'},
# 'comment' => "Fake Tax" },
# 1);
}
}
}

View File

@@ -78,6 +78,24 @@ class AppModel extends Model {
/**************************************************************************
**************************************************************************
**************************************************************************
* function: nameToID
* - Returns the ID of the named item
*/
function nameToID($name) {
$this->cacheQueries = true;
$item = $this->find('first', array
('recursive' => -1,
'conditions' => compact('name'),
));
$this->cacheQueries = false;
$item = current($item);
return $item['id'];
}
/**************************************************************************
**************************************************************************
**************************************************************************

View File

@@ -162,41 +162,11 @@ class LedgerEntriesController extends AppController {
if (isset($params['custom']['customer_id'])) {
$conditions[] =
array('Customer.id' => $params['custom']['customer_id']);
/* $Account = new Account(); */
/* if (isset($params['custom']['account_ftype']) || */
/* isset($params['custom']['ledger_id'])) { */
/* $conditions[] = */
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
/* 'Account.id' => $Account->receiptAccountID())); */
/* } else { */
/* $conditions[] = */
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
/* )); */
/* } */
}
if (isset($params['custom']['lease_id'])) {
$conditions[] =
array('Lease.id' => $params['custom']['lease_id']);
/* $Account = new Account(); */
/* if (isset($params['custom']['account_ftype']) || */
/* isset($params['custom']['ledger_id'])) { */
/* $conditions[] = */
/* array('OR' => array('Account.id' => $Account->invoiceAccountID(), */
/* 'Account.id' => $Account->receiptAccountID())); */
/* } else { */
/* $conditions[] = */
/* array('OR' => array('DebitAccount.id' => $Account->invoiceAccountID(), */
/* //'CreditAccount.id' => $Account->invoiceAccountID(), */
/* //'DebitAccount.id' => $Account->receiptAccountID(), */
/* 'CreditAccount.id' => $Account->receiptAccountID(), */
/* )); */
/* } */
}
if (isset($params['custom']['transaction_id'])) {
@@ -304,7 +274,7 @@ class LedgerEntriesController extends AppController {
'conditions' => array('LedgerEntry.id' => $id),
));
//pr($entry);
pr($entry);
// Because 'DebitLedger' and 'CreditLedger' both relate to 'Account',
// CakePHP will not include them in the LedgerEntry->find (or so it

View File

@@ -0,0 +1,116 @@
<?php
class StatementEntriesController extends AppController {
var $sidemenu_links = array();
/**************************************************************************
**************************************************************************
**************************************************************************
* override: sideMenuLinks
* - Generates controller specific links for the side menu
*/
function sideMenuLinks() {
return array_merge(parent::sideMenuLinks(), $this->sidemenu_links);
}
/**************************************************************************
**************************************************************************
**************************************************************************
* virtuals: jqGridData
* - With the application controller handling the jqGridData action,
* these virtual functions ensure that the correct data is passed
* to jqGrid.
*/
function jqGridDataTables(&$params, &$model) {
$link =
array(// Models
'LedgerEntry' =>
array('fields' => array('id'),
// Models
'Transaction' =>
array('fields' => array('id', 'stamp'),
),
'Ledger' =>
array('fields' => array('id', 'sequence'),
'conditions' => ("Ledger.id = IF(StatementEntry.type = 'CHARGE'," .
" LedgerEntry.credit_ledger_id," .
" LedgerEntry.debit_ledger_id)"),
// Models
'Account' => array('fields' => array('id', 'name'),
),
),
),
);
return array('link' => $link);
}
function jqGridDataFields(&$params, &$model) {
$fields = array('id', 'type', 'comment');
$fields[] = "IF(StatementEntry.type = 'CHARGE', StatementEntry.amount, NULL) AS 'charge'";
$fields[] = "IF(StatementEntry.type = 'PAYMENT', StatementEntry.amount, NULL) AS 'payment'";
$fields[] = "IF(StatementEntry.type = 'CHARGE', 1, -1) * StatementEntry.amount AS 'amount'";
return $fields;
}
function jqGridDataConditions(&$params, &$model) {
$conditions = parent::jqGridDataConditions($params, $model);
if (isset($params['custom']['statement_id'])) {
$conditions[] =
array('StatementEntry.statement_id' => $params['custom']['statement_id']);
}
return $conditions;
}
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
$links['Transaction'] = array('id');
$links['StatementEntry'] = array('id');
$links['Account'] = array('controller' => 'accounts', 'name');
$links['LedgerEntry'] = array('id');
return parent::jqGridRecordLinks($params, $model, $records, $links);
}
function jqGridRecordsPostProcess(&$params, &$model, &$records) {
parent::jqGridRecordsPostProcess($params, $model, $records);
$subtotal = 0;
foreach ($records AS &$record) {
$amount = $record['StatementEntry']['amount'];
$record['StatementEntry']['amount'] = $amount;
$record['StatementEntry']['subtotal'] = ($subtotal += $amount);
}
}
/**************************************************************************
**************************************************************************
**************************************************************************
* action: view
* - Displays information about a specific entry
*/
function view($id = null) {
if (!$id) {
$this->Session->setFlash(__('Invalid Item.', true));
$this->redirect(array('controller' => 'accounts', 'action'=>'index'));
}
// Get the StatementEntry and related fields
$entry = $this->StatementEntry->find
('first',
array('contain' => array(),
'conditions' => array('StatementEntry.id' => $id),
));
// Prepare to render.
$title = "Statement Entry #{$entry['StatementEntry']['id']}";
$this->set(compact('entry', 'title'));
}
}

View File

@@ -55,7 +55,7 @@ class UnitsController extends AppController {
$link = array
('link' =>
array(// Models
'UnitSize' => array('fields' => array('name')),
'UnitSize' => array('fields' => array('id', 'name')),
),
);
@@ -93,6 +93,11 @@ class UnitsController extends AppController {
return parent::jqGridDataOrder($params, $model, $index, $direction);
}
function jqGridRecordLinks(&$params, &$model, &$records, $links) {
//$links['UnitSize'] = array('name');
$links['Unit'] = array('name', 'id');
return parent::jqGridRecordLinks($params, $model, $records, $links);
}
/**************************************************************************
**************************************************************************

View File

@@ -82,26 +82,13 @@ class Account extends AppModel {
/**************************************************************************
**************************************************************************
**************************************************************************
* function: accountNameToID
* - Returns the ID of the named account
* function: Account IDs
* - Returns the ID of the desired account
*/
function accountNameToID($name) {
$this->cacheQueries = true;
$account = $this->find('first', array
('recursive' => -1,
'conditions' => compact('name'),
));
$this->cacheQueries = false;
return $account['Account']['id'];
}
function securityDepositAccountID() { return $this->accountNameToID('Security Deposit'); }
function rentAccountID() { return $this->accountNameToID('Rent'); }
function accountReceivableAccountID() { return $this->accountNameToID('A/R'); }
function invoiceAccountID() { return $this->accountReceivableAccountID(); }
function receiptAccountID() { return $this->accountReceivableAccountID(); }
//function invoiceAccountID() { return $this->accountNameToID('Invoice'); }
//function receiptAccountID() { return $this->accountNameToID('Receipt'); }
function securityDepositAccountID() { return $this->nameToID('Security Deposit'); }
function rentAccountID() { return $this->nameToID('Rent'); }
function accountReceivableAccountID() { return $this->nameToID('A/R'); }
/**************************************************************************

View File

@@ -8,6 +8,7 @@ class Customer extends AppModel {
);
var $belongsTo = array(
'Statement',
'PrimaryContact' => array(
'className' => 'Contact',
),
@@ -43,13 +44,26 @@ class Customer extends AppModel {
*/
function accountId($id) {
$this->cacheQueries = true;
$customer = $this->find('first',
array('contain' => false,
'fields' => array('account_id'),
'conditions' => array(array('Customer.id' => $id))));
$item = $this->find('first',
array('contain' => false,
'fields' => array('account_id'),
'conditions' => compact('id')));
$this->cacheQueries = false;
return $customer['Customer']['account_id'];
$item = current($item);
return $item['account_id'];
}
function statementID($id) {
$this->cacheQueries = true;
$item = $this->find('first', array
('recursive' => -1,
'conditions' => compact('id'),
));
$this->cacheQueries = false;
$item = current($item);
return $item['statement_id'];
}
@@ -75,6 +89,7 @@ class Customer extends AppModel {
return $ids;
}
/**************************************************************************
**************************************************************************
**************************************************************************
@@ -82,22 +97,10 @@ class Customer extends AppModel {
* - Returns an array of security deposit entries
*/
function findSecurityDeposits($id, $link = null) {
/* pr(array('function' => 'Customer::findSecurityDeposits', */
/* 'args' => compact('id', 'link'), */
/* )); */
$entries = $this->Account->findLedgerEntriesRelatedToAccount
($this->Account->invoiceAccountID(),
return $this->Statement->findEntriesRelatedToAccount
($this->statementId($id),
$this->Account->securityDepositAccountID(),
true, array('LedgerEntry.customer_id' => $id), $link);
/* pr(array('function' => 'Customer::findSecurityDeposits', */
/* 'args' => compact('id', 'link'), */
/* 'vars' => compact('customer'), */
/* 'return' => compact('entries'), */
/* )); */
return $entries;
null, $link);
}
@@ -191,16 +194,11 @@ class Customer extends AppModel {
* - Returns summary data from the requested customer.
*/
function stats($id = null) {
function stats($id = null, $cond = null, $link = null) {
if (!$id)
return null;
$stats = $this->Account->stats($this->Account->accountReceivableAccountID(), true,
array('LedgerEntry.customer_id' => $id));
// Pull to the top level and return
$stats = $stats['Ledger'];
return $stats;
return $this->Statement->stats($this->statementID($id), $cond, $link);
}
}

View File

@@ -26,6 +26,7 @@ class Lease extends AppModel {
'LeaseType',
'Unit',
'Customer',
'Statement',
'LateSchedule',
);
@@ -40,13 +41,23 @@ class Lease extends AppModel {
/**************************************************************************
**************************************************************************
**************************************************************************
* function: accountId
* - Returns the accountId of the given lease
* function: accessors
*/
function accountId($id) {
return $this->Account->invoiceAccountID();
}
function statementID($id) {
$this->cacheQueries = true;
$item = $this->find('first', array
('recursive' => -1,
'conditions' => compact('id'),
));
$this->cacheQueries = false;
$item = current($item);
return $item['statement_id'];
}
/**************************************************************************
**************************************************************************
@@ -83,21 +94,10 @@ class Lease extends AppModel {
* - Returns an array of security deposit entries
*/
function findSecurityDeposits($id, $link = null) {
/* pr(array('function' => 'Lease::findSecurityDeposits', */
/* 'args' => compact('id', 'link'), */
/* )); */
$entries = $this->Account->findLedgerEntriesRelatedToAccount
($this->accountId($id),
return $this->Statement->findEntriesRelatedToAccount
($this->statementId($id),
$this->Account->securityDepositAccountID(),
true, array('LedgerEntry.lease_id' => $id), $link);
/* pr(array('function' => 'Lease::findSecurityDeposits', */
/* 'args' => compact('id', 'link'), */
/* 'vars' => compact('lease'), */
/* 'return' => compact('entries'), */
/* )); */
return $entries;
null, $link);
}
@@ -142,16 +142,11 @@ class Lease extends AppModel {
* - Returns summary data from the requested lease.
*/
function stats($id = null) {
function stats($id = null, $cond = null, $link = null) {
if (!$id)
return null;
$stats = $this->Account->stats($this->Account->accountReceivableAccountID(), true,
array('LedgerEntry.lease_id' => $id));
// Pull to the top level and return
$stats = $stats['Ledger'];
return $stats;
return $this->Statement->stats($this->statementID($id), $cond, $link);
}
}

123
site/models/statement.php Normal file
View File

@@ -0,0 +1,123 @@
<?php
class Statement extends AppModel {
var $hasMany = array(
'Lease',
'Customer',
'StatementEntry',
'ChargeStatementEntry' => array(
'className' => 'StatementEntry',
'conditions' => array('type' => 'CHARGE'),
),
'PaymentStatementEntry' => array(
'className' => 'StatementEntry',
'conditions' => array('type' => 'PAYMENT'),
),
);
/**************************************************************************
**************************************************************************
**************************************************************************
* function: findStatementEntries
* - Returns an array of statement entries that belong to a given
* statement. There is extra work done... see the StatementEntry model.
*/
function findStatementEntries($id, $cond = null, $link = null) {
/* pr(array('function' => 'Statement::findStatementEntries', */
/* 'args' => compact('id', 'cond', 'link'), */
/* )); */
if (!isset($cond))
$cond = array();
$cond[] = array('Statement.id' => $id);
$entries = $this->find('all', array('link' => $link, 'conditions' => $cond));
/* pr(array('function' => 'Statement::findStatementEntries', */
/* 'args' => compact('id', 'cond', 'link'), */
/* 'return' => compact('entries'), */
/* )); */
return $entries;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: findEntriesRelatedToAccount
* - Returns an array of statement entries that belong to the given
* account, and are related to a specific account.
*/
function findEntriesRelatedToAccount($id, $rel_ids, $cond = null, $link = null) {
/* pr(array('function' => 'Statement::findEntriesRelatedToAccount', */
/* 'args' => compact('id', 'rel_ids', 'cond', 'link'), */
/* )); */
if (!isset($cond))
$cond = array();
if (!isset($link))
$link = array();
if (!is_array($rel_ids))
$rel_ids = array($rel_ids);
$link['StatementEntry'] = array('LedgerEntry' => array('Ledger' => array('Account')));
$cond[] = array('Account.id' => $rel_ids);
$entries = $this->findStatementEntries($id, $cond, $link);
$stats = $this->stats($id, $cond, $link);
$entries = array('Entries' => $entries,
'summary' => $stats);
/* pr(array('function' => 'Statement::findEntriesRelatedToAccount', */
/* 'args' => compact('id', 'relid', 'cond', 'link'), */
/* 'return' => compact('entries'), */
/* )); */
return $entries;
}
/**************************************************************************
**************************************************************************
**************************************************************************
* function: stats
* - Returns summary data from the requested statement.
*/
function stats($id, $cond = null, $link = null) {
if (!isset($cond))
$cond = array();
if (!isset($link))
$link = array();
if (!isset($link['StatementEntry']))
$link['StatementEntry'] = array('fields' => array());
$cond[] = array('Statement.id' => $id);
$stats = $this->find
('first', array
('link' => $link,
'fields' =>
array("SUM(IF(StatementEntry.type = 'CHARGE',
StatementEntry.amount, NULL)) AS charges",
"SUM(IF(StatementEntry.type = 'PAYMENT',
StatementEntry.amount, NULL)) AS payments",
"SUM(IF(StatementEntry.type = 'CHARGE', 1, -1)
* IF(StatementEntry.amount, StatementEntry.amount, 0)
) AS balance",
"COUNT(StatementEntry.id) AS entries"),
'conditions' => $cond,
'group' => 'Statement.id',
));
// The fields are all tucked into the [0] index,
// and the rest of the array is useless (empty).
return $stats[0];
}
}
?>

View File

@@ -0,0 +1,9 @@
<?php
class StatementEntry extends AppModel {
var $belongsTo = array(
'Statement',
'LedgerEntry',
);
}

View File

@@ -63,13 +63,12 @@ echo $this->element('leases',
/**********************************************************************
* Customer Account History
* Customer Statement
*/
echo $this->element('ledger_entries',
array('caption' => 'Account',
'customer_id' => $customer['Customer']['id'],
'ar_account' => true,
echo $this->element('statement_entries',
array('caption' => 'Statement',
'statement_id' => $customer['Customer']['statement_id'],
));

View File

@@ -47,9 +47,6 @@ else {
$cols['Debit Account'] = array('index' => 'DebitAccount.name', 'formatter' => 'name');
$cols['Credit Account'] = array('index' => 'CreditAccount.name', 'formatter' => 'name');
}
$cols['Customer'] = array('index' => 'Customer.name', 'formatter' => 'longname');
$cols['Lease'] = array('index' => 'Lease.number', 'formatter' => 'id');
$cols['Unit'] = array('index' => 'Unit.name', 'formatter' => 'name');
$cols['Source'] = array('index' => 'MonetarySource.name', 'formatter' => 'name');
$cols['Comment'] = array('index' => 'LedgerEntry.comment', 'formatter' => 'comment', 'width'=>150);

View File

@@ -0,0 +1,50 @@
<?php /* -*- mode:PHP -*- */
if (0) {
$subtotal_amount = false;
} else {
$subtotal_amount = true;
}
// Define the table columns
$cols = array();
$cols['ID'] = array('index' => 'StatementEntry.id', 'formatter' => 'id');
$cols['Entry'] = array('index' => 'LedgerEntry.id', 'formatter' => 'id');
$cols['Date'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
//$cols['Type'] = array('index' => 'StatementEntry.type', 'formatter' => 'name');
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'longname');
//$cols['Comment'] = array('index' => 'StatementEntry.comment', 'formatter' => 'comment', 'width'=>150);
//$cols['Amount'] = array('index' => 'StatementEntry.amount', 'formatter' => 'currency');
$cols['Charge'] = array('index' => 'charge', 'formatter' => 'currency');
$cols['Payment'] = array('index' => 'payment', 'formatter' => 'currency');
if ($subtotal_amount) {
$cols['Sub-Total'] = array('index' => 'subtotal', 'formatter' => 'currency', 'sortable' => false);
}
$custom_post_data = compact('statement_id');
$jqGrid_options = array('jqGridColumns' => $cols,
'controller' => 'statement_entries',
);
$jqGrid_options += compact('grid_div_id', 'grid_id', 'caption', 'grid_setup', 'limit');
if (isset($statement_entries)) {
$jqGrid_options += array('custom_ids' =>
array_map(create_function('$data',
'return $data["id"];'),
$statement_entries),
'limit' => 10);
}
else {
$jqGrid_options += array('action' => 'statement',
'limit' => 50);
}
$jqGrid_options += compact('custom_post_data');
$jqGrid_options['sort_column'] = 'Date';
echo $this->element('jqGrid', $jqGrid_options);

View File

@@ -78,10 +78,9 @@ echo '<div CLASS="detail supporting">' . "\n";
* Lease Account History
*/
echo $this->element('ledger_entries',
array('caption' => 'Account',
'lease_id' => $lease['id'],
'ar_account' => true,
echo $this->element('statement_entries',
array('caption' => 'Statement',
'statement_id' => $lease['statement_id'],
));

View File

@@ -81,16 +81,12 @@ echo $this->element('table',
echo '<div class="infobox">' . "\n";
$rows = array();
if ($debit_ledger['Account']['trackable']) {
$rows[] = array("Applied from {$debit_ledger['Account']['name']}:",
FormatHelper::currency($stats['debit_amount_reconciled']));
$rows[] = array("{$debit_ledger['Account']['name']} Amount Remaining:",
FormatHelper::currency($stats['debit_amount_remaining']));
$rows[] = array('Payments Received:', FormatHelper::currency($stats['debit_amount_reconciled']));
$rows[] = array('Amount Owing:', FormatHelper::currency($stats['debit_amount_remaining']));
}
if ($credit_ledger['Account']['trackable']) {
$rows[] = array("Applied to {$credit_ledger['Account']['name']}:",
FormatHelper::currency($stats['credit_amount_reconciled']));
$rows[] = array("{$credit_ledger['Account']['name']} Amount Remaining:",
FormatHelper::currency($stats['credit_amount_remaining']));
$rows[] = array('Charges Reconciled:', FormatHelper::currency($stats['credit_amount_reconciled']));
$rows[] = array('Unapplied Amount:', FormatHelper::currency($stats['credit_amount_remaining']));
}
echo $this->element('table',
array('class' => 'summary',
@@ -117,7 +113,7 @@ echo '<div CLASS="detail supporting">' . "\n";
if ($debit_ledger['Account']['trackable']) {
echo $this->element('ledger_entries',
array('caption' => "Applied to " . $debit_ledger['Account']['name'],
array('caption' => "Payments Received",
'grid_div_id' => 'debit_reconciliation_ledger_entries',
'account_ftype' => 'debit',
'reconcile_id' => $entry['id'],
@@ -127,7 +123,7 @@ if ($debit_ledger['Account']['trackable']) {
if ($credit_ledger['Account']['trackable']) {
echo $this->element('ledger_entries',
array('caption' => "Applied to " . $credit_ledger['Account']['name'],
array('caption' => "Charges Paid",
'grid_div_id' => 'credit_reconciliation_ledger_entries',
'account_ftype' => 'credit',
'reconcile_id' => $entry['id'],

View File

@@ -57,15 +57,14 @@ echo $this->element('leases',
/**********************************************************************
* Current Tenant Lease Account History
* Current Lease Statement History
*/
echo $this->element('ledger_entries',
array('caption' => ('Current Lease Account (' .
$unit['CurrentLease']['Customer']['name']
. ')'),
'ar_account' => true,
'lease_id' => $unit['CurrentLease']['id'],
echo $this->element('statement_entries',
array('caption' => ('Current Lease Statement (' .
$unit['CurrentLease']['Customer']['name'] .
')'),
'statement_id' => $unit['CurrentLease']['statement_id']
));