Modified effective dates to be part of the ledger entry, not the transaction. This is the logically correct way, as a transaction is simply a collection of entries which might be anywhere from completely aligned to totally disjoint from one another (more likely the former). As a practical example, consider a move-in invoice, with security deposit (effective immediately with no end date), a prorated rent (effective move-in day through the end of the first month), and first full months rent (effective beginning of next month through the end of next month). There is certainly more work to be done on this, and testing was minimal. It does seem to be functioning though, so I'm checking in.

git-svn-id: file:///svn-source/pmgr/branches/invoice_receipt_20090629@256 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-07-08 16:04:53 +00:00
parent 3aacbb94aa
commit 014fa0feb9
9 changed files with 134 additions and 60 deletions

View File

@@ -948,7 +948,6 @@ CREATE TABLE `pmgr_transactions` (
-- NOT NULL,
`stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`through_date` DATE DEFAULT NULL,
`due_date` DATE DEFAULT NULL,
-- REVISIT <AP>: 20090604
@@ -970,13 +969,28 @@ DROP TABLE IF EXISTS `pmgr_ledger_entries`;
CREATE TABLE `pmgr_ledger_entries` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(80) DEFAULT NULL,
-- Effective date may be used for a variety of entries
-- charges & payments are not always effective at the
-- time of transaction. Through date, on the other hand,
-- will probably only be relevant for charges, such as
-- rent, which is effective for a range of dates.
`effective_date` DATE DEFAULT NULL, -- first day
`through_date` DATE DEFAULT NULL, -- last day
`monetary_source_id` INT(10) UNSIGNED DEFAULT NULL, -- NULL if internal transfer
`transaction_id` INT(10) UNSIGNED NOT NULL,
`customer_id` INT(10) UNSIGNED DEFAULT NULL,
`lease_id` INT(10) UNSIGNED DEFAULT NULL,
`amount` FLOAT(12,2) NOT NULL,
-- REVISIT <AP>: 20090707
-- Experimental. Considering automatically hooking
-- charges to their invoice. This may help ease the
-- ongoing accounting dilema that we've been having.
-- It might allow us to keep the underlying invoice
-- ledgers without having to expose the user to them.
`root_transaction_id` INT(10) UNSIGNED DEFAULT NULL,
`debit_ledger_id` INT(10) UNSIGNED NOT NULL,
`credit_ledger_id` INT(10) UNSIGNED NOT NULL,

View File

@@ -224,6 +224,36 @@ sub datefmt {
return sprintf("%04d-%02d-%02d%s", $dt[2], $dt[0], $dt[1], $dt[3] ? ' '.$dt[3] : "");
}
sub dates {
my ($type, $dt, $dt_end, $comment, $ledger_id) = @_;
# Nothing should be stamped before possession
my $stamp = $dt;
if ($stamp =~ m%^0?[12]/% || ($stamp =~ m%^0?3/(\d+)/% && $1 <= 25)) {
$stamp = "3/25/2009 16:00";
}
my $effective_dt = $dt;
my $through_dt = $dt_end;
# Use different dates for security deposits
if ($type eq 'charge' && $comment eq 'Security Deposit') {
$effective_dt = $newdb{'lookup'}{'ledger'}{$ledger_id}{'lease_date'};
$through_dt = undef;
}
# REVISIT <AP>: 20090708
# Do we want to have effective dates on invoices?
# Do we want to have effective dates for payments?
# The Receipt already has an effective date.
if ($type eq 'invoice' || $type eq 'payment') {
$effective_dt = undef;
$through_dt = undef;
}
return (datefmt($stamp), datefmt($effective_dt), datefmt($through_dt));
}
######################################################################
######################################################################
@@ -854,7 +884,9 @@ $query = "SELECT L.*, A.TenantID FROM TenantLedger L LEFT JOIN `Access` A ON A.L
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'},
'lease_date' => $row->{'DateIn'},
};
addRow('leases',
{ 'number' => $newdb{'tables'}{'leases'}{'autoid'}+1,
@@ -892,9 +924,13 @@ $newdb{'lookup'}{'charge'} = {};
$query = "SELECT *, ChargeAmount+TaxAmount AS InvoiceAmount FROM Charges ORDER BY ChargeID";
foreach $row (@{query($sdbh, $query)}) {
my ($stamp, $effective_date, $through_date) =
dates('invoice', $row->{'ChargeDate'}, $row->{'EndDate'},
$row->{'ChargeDescription'}, $row->{'LedgerID'});
addRow('transactions',
{ 'stamp' => datefmt($row->{'ChargeDate'}),
'through_date' => datefmt($row->{'EndDate'}) });
{ 'stamp' => $stamp,
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}
= { 'invoice' =>
@@ -914,8 +950,8 @@ foreach $row (@{query($sdbh, $query)}) {
}
addRow('transactions',
{ 'stamp' => datefmt($row->{'ChargeDate'}),
'through_date' => datefmt($row->{'EndDate'}) });
{ 'stamp' => $stamp,
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'charge_tx'}
= $newdb{'tables'}{'transactions'}{'autoid'};
@@ -931,7 +967,9 @@ foreach $row (@{query($sdbh, $query)}) {
# Create the invoice entry
# debit: A/R credit: Invoice
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'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'},
@@ -952,6 +990,10 @@ foreach $row (@{query($sdbh, $query)}) {
$query = "SELECT * FROM Charges ORDER BY ChargeID";
foreach $row (@{query($sdbh, $query)}) {
my (undef, $effective_date, $through_date) =
dates('charge', $row->{'ChargeDate'}, $row->{'EndDate'},
$row->{'ChargeDescription'}, $row->{'LedgerID'});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tx'}
= $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'charge_tx'};
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}
@@ -970,7 +1012,9 @@ foreach $row (@{query($sdbh, $query)}) {
# Add the charge entry
# debit: Invoice credit: Rent/LateCharge/Etc
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'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'},
@@ -1000,7 +1044,9 @@ foreach $row (@{query($sdbh, $query)}) {
# Add the tax charge entry
# debit: Invoice credit: Tax
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'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'},
@@ -1069,18 +1115,18 @@ $newdb{'lookup'}{'receipt'} = {};
#
# 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| | |
# |50 | 50| | | | | t1a
# | | 5 5| | | | | t1a
# | | |55 55| | | | t1b
# |40 | 40| | | | | t2a
# | | 4 4| | | | | t2a
# | | |44 44| | | | t2b
# | | | | |79 | 79| t3a
# | | | | |20 20| | t3a
# | | | |50 50| | | t3b
# | | | | 5 5| | | t3b
# | | | |40 40| | | t3b
# | | | | 4 4| | | t3b
# | | | | | | |
#
# There is another possible solution, although it is
@@ -1127,18 +1173,21 @@ foreach $row (@{query($sdbh, $query)}) {
# next;
# }
my ($stamp, $effective_date, $through_date) =
dates('receipt', $row->{'ReceiptDate'}, undef);
if (!$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}) {
addRow('transactions',
{ 'stamp' => datefmt($row->{'ReceiptDate'}),
'through_date' => undef });
{ 'stamp' => $stamp,
});
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}
= {'tx' => $newdb{'tables'}{'transactions'}{'autoid'},
'date' => datefmt($row->{'ReceiptDate'}),
'date' => $stamp,
};
addRow('transactions',
{ 'stamp' => datefmt($row->{'ReceiptDate'}),
'through_date' => undef });
{ 'stamp' => $stamp,
});
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'payment_tx'}
= $newdb{'tables'}{'transactions'}{'autoid'};
}
@@ -1193,7 +1242,9 @@ foreach $row (@{query($sdbh, $query)}) {
# 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'},
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'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'},
@@ -1216,11 +1267,9 @@ $newdb{'lookup'}{'payment'} = {};
$query = "SELECT * FROM Payments ORDER BY PaymentID";
foreach $row (@{query($sdbh, $query)})
{
# addRow('transactions',
# { 'stamp' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'date'},
# 'through_date' => undef });
# $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}
# = {'tx' => $newdb{'tables'}{'transactions'}{'autoid'} };
my (undef, $effective_date, $through_date) =
dates('payment', $row->{'PaymentDate'}, undef);
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}
= { 'tx' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'payment_tx'} };
@@ -1247,7 +1296,9 @@ foreach $row (@{query($sdbh, $query)})
# Add the payment entry
# debit: Receipt credit: A/R
addRow('ledger_entries',
{ 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'tx'},
'debit_ledger_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'debit_ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'credit_ledger_id'},

View File

@@ -298,9 +298,7 @@ class LedgerEntriesController extends AppController {
'Lease.id',
),
'fields' => array('LedgerEntry.id',
'LedgerEntry.amount',
'LedgerEntry.comment'),
'fields' => array('LedgerEntry.*'),
'conditions' => array('LedgerEntry.id' => $id),
));

View File

@@ -91,7 +91,7 @@ class LedgerEntry extends AppModel {
* query the ledger/account to find it out.
*/
function ledgerContextFields($ledger_id = null, $account_type = null) {
$fields = array('id', 'name', 'comment', 'amount');
$fields = array('id', 'effective_date', 'through_date', 'comment', 'amount');
if (isset($ledger_id)) {
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .
@@ -114,7 +114,7 @@ class LedgerEntry extends AppModel {
}
function ledgerContextFields2($ledger_id = null, $account_id = null, $account_type = null) {
$fields = array('id', 'name', 'comment', 'amount');
$fields = array('id', 'effective_date', 'through_date', 'comment', 'amount');
if (isset($ledger_id)) {
$fields[] = ("IF(LedgerEntry.debit_ledger_id = $ledger_id," .

View File

@@ -50,6 +50,8 @@ else
}
$cols['Date'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
$cols['Effective'] = array('index' => 'LedgerEntry.effective_date', 'formatter' => 'date');
$cols['Through'] = array('index' => 'LedgerEntry.through_date', 'formatter' => 'date');
if ($single_account) {
$cols['Account'] = array('index' => 'Account.name', 'formatter' => 'name');

View File

@@ -4,8 +4,7 @@
$cols = array();
$cols['ID'] = array('index' => 'Transaction.id', 'formatter' => 'id');
//$cols['Customer'] = array('index' => 'Customer.name', 'formatter' => 'longname');
$cols['Timesamp'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
$cols['Through'] = array('index' => 'Transaction.through_date', 'formatter' => 'date');
$cols['Timestamp'] = array('index' => 'Transaction.stamp', 'formatter' => 'date');
$cols['Due'] = array('index' => 'Transaction.due_date', 'formatter' => 'date');
$cols['Comment'] = array('index' => 'Transaction.comment', 'formatter' => 'comment');

View File

@@ -106,8 +106,7 @@ function resetForm() {
$("#invoice-deposit").html("INTERNAL ERROR");
addChargeSource(false);
datepickerNow('charge_date');
$("#through_date").val('');
datepickerNow('TransactionStamp');
}
@@ -150,6 +149,7 @@ function onGridState(grid_id, state) {
}
function addChargeSource(flash) {
var id = $("#charge-entry-id").val();
addDiv('charge-entry-id', 'charge', 'charges', flash,
// HTML section
'<FIELDSET CLASS="charge subset">' +
@@ -168,6 +168,14 @@ function addChargeSource(flash) {
'value' => $defaultAccount,
),
),
"effective_date" => array('opts' =>
array('type' => 'text'),
'between' => '<A HREF="#" ONCLICK="datepickerBOM(\'TransactionStamp\',\'LedgerEntry%{id}EffectiveDate\'); return false;">BOM</A>',
),
"through_date" => array('opts' =>
array('type' => 'text'),
'between' => '<A HREF="#" ONCLICK="datepickerEOM(\'LedgerEntry%{id}EffectiveDate\',\'LedgerEntry%{id}ThroughDate\'); return false;">EOM</A>',
),
"amount" => null,
"comment" => array('opts' => array('size' => 50)),
),
@@ -176,6 +184,20 @@ function addChargeSource(flash) {
'</FIELDSET>'
);
$("#LedgerEntry"+id+"EffectiveDate")
.attr('autocomplete', 'off')
.datepicker({ constrainInput: true,
numberOfMonths: [1, 1],
showCurrentAtPos: 0,
dateFormat: 'mm/dd/yy' });
$("#LedgerEntry"+id+"ThroughDate")
.attr('autocomplete', 'off')
.datepicker({ constrainInput: true,
numberOfMonths: [1, 1],
showCurrentAtPos: 0,
dateFormat: 'mm/dd/yy' });
}
--></script>
@@ -253,14 +275,8 @@ echo $this->element('form_table',
'field_prefix' => 'Transaction',
'fields' => array
("stamp" => array('opts' =>
array('id' => 'charge_date',
'type' => 'text'),
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'charge_date\'); return false;">Now</A>',
),
"through_date" => array('opts' =>
array('id' => 'through_date',
'type' => 'text'),
'between' => '<A HREF="#" ONCLICK="datepickerEOM(\'charge_date\',\'through_date\'); return false;">EOM</A>',
array('type' => 'text'),
'between' => '<A HREF="#" ONCLICK="datepickerNow(\'TransactionStamp\'); return false;">Now</A>',
),
"comment" => array('opts' => array('size' => 50),
),
@@ -288,14 +304,7 @@ echo $form->submit('Generate Invoice') . "\n";
<script type="text/javascript"><!--
$(document).ready(function(){
$("#charge_date")
.attr('autocomplete', 'off')
.datepicker({ constrainInput: true,
numberOfMonths: [1, 1],
showCurrentAtPos: 0,
dateFormat: 'mm/dd/yy' });
$("#through_date")
$("#TransactionStamp")
.attr('autocomplete', 'off')
.datepicker({ constrainInput: true,
numberOfMonths: [1, 1],

View File

@@ -23,6 +23,8 @@ $rows = array(array('ID', $entry['id']),
'action' => 'view',
$transaction['id']))),
array('Timestamp', FormatHelper::datetime($transaction['stamp'])),
array('Effective', FormatHelper::date($entry['effective_date'])),
array('Through', FormatHelper::date($entry['through_date'])),
array('Customer', (isset($customer['name'])
? $html->link($customer['name'],
array('controller' => 'customers',

View File

@@ -11,7 +11,6 @@ echo '<div class="transaction view">' . "\n";
$rows = array(array('ID', $transaction['Transaction']['id']),
array('Timestamp', FormatHelper::datetime($transaction['Transaction']['stamp'])),
array('Through', FormatHelper::date($transaction['Transaction']['through_date'])),
array('Due', FormatHelper::date($transaction['Transaction']['due_date'])),
array('Comment', $transaction['Transaction']['comment']));