First pass at making ACH items auto-deposit. Things are really set up for a separate deposit transaction though, and I should just bite the bullet and do that instead. I don't want them to show up as Deposits though, but perhaps it would be easiest just to make a new type 'AUTO_DEPOSIT' or something.
git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716@607 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
@@ -1148,6 +1148,9 @@ CREATE TABLE `pmgr_tender_types` (
|
|||||||
-- include credit cards, debit cards, and ACH transfers.
|
-- include credit cards, debit cards, and ACH transfers.
|
||||||
`tillable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
`tillable` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
|
||||||
|
|
||||||
|
-- Should these items be deposited automatically?
|
||||||
|
`auto_deposit` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
-- Names of the 4 data fields (or NULL if not used)
|
-- Names of the 4 data fields (or NULL if not used)
|
||||||
-- Not the most robust of solutions, especially since
|
-- Not the most robust of solutions, especially since
|
||||||
-- it requires (or strongly implicates) that all fields
|
-- it requires (or strongly implicates) that all fields
|
||||||
@@ -1171,6 +1174,10 @@ CREATE TABLE `pmgr_tender_types` (
|
|||||||
-- account, either distinct or non-distinct from others
|
-- account, either distinct or non-distinct from others
|
||||||
`account_id` INT(10) UNSIGNED NOT NULL,
|
`account_id` INT(10) UNSIGNED NOT NULL,
|
||||||
|
|
||||||
|
-- Which account should these items be deposited in?
|
||||||
|
-- This may or may not actually be used for all types
|
||||||
|
-- but will likely get used for auto deposit items.
|
||||||
|
`deposit_account_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
|
||||||
`comment` VARCHAR(255) DEFAULT NULL,
|
`comment` VARCHAR(255) DEFAULT NULL,
|
||||||
|
|
||||||
|
|||||||
@@ -567,7 +567,9 @@ foreach my $tender_name ('Cash', 'Check', 'Money Order', 'ACH',
|
|||||||
addRow('tender_types', {
|
addRow('tender_types', {
|
||||||
'name' => $tender_name,
|
'name' => $tender_name,
|
||||||
'account_id' => $newdb{'lookup'}{'account'}{$tender_name}{'account_id'},
|
'account_id' => $newdb{'lookup'}{'account'}{$tender_name}{'account_id'},
|
||||||
|
'deposit_account_id' => $newdb{'lookup'}{'account'}{'Bank'}{'account_id'},
|
||||||
'tillable' => $tillable,
|
'tillable' => $tillable,
|
||||||
|
'auto_deposit' => ($tender_name eq 'ACH') ? 1 : 0,
|
||||||
'data1_name' => $name1,
|
'data1_name' => $name1,
|
||||||
'data2_name' => $name2,
|
'data2_name' => $name2,
|
||||||
'data3_name' => $name3,
|
'data3_name' => $name3,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class Transaction extends AppModel {
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
//var $default_log_level = array('log' => 30, 'show' => 15);
|
var $default_log_level = array('log' => 30, 'show' => 15);
|
||||||
//var $max_log_level = 10;
|
//var $max_log_level = 10;
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
@@ -121,23 +121,180 @@ class Transaction extends AppModel {
|
|||||||
'lease_id' => $lease_id,
|
'lease_id' => $lease_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Go through the statement entries and flag as disbursements
|
// Go through the statement entries, making sure the tender
|
||||||
foreach ($data['Entry'] AS &$entry)
|
// is recorded into the correct account, and then performing
|
||||||
$entry += array('type' => 'DISBURSEMENT', // not used
|
// an auto-deposit if necessary.
|
||||||
'account_id' =>
|
$deposit = array();
|
||||||
(isset($entry['Tender']['tender_type_id'])
|
foreach ($data['Entry'] AS &$entry) {
|
||||||
? ($this->LedgerEntry->Tender->TenderType->
|
if (empty($entry['Tender']['tender_type_id']))
|
||||||
accountID($entry['Tender']['tender_type_id']))
|
continue;
|
||||||
: null),
|
|
||||||
);
|
$ttype = $this->LedgerEntry->Tender->TenderType->find
|
||||||
|
('first', array('contain' => false,
|
||||||
|
'conditions' =>
|
||||||
|
array('id' => $entry['Tender']['tender_type_id'])));
|
||||||
|
$ttype = $ttype['TenderType'];
|
||||||
|
|
||||||
|
// Set the account for posting.
|
||||||
|
$entry += array('account_id' => $ttype['account_id']);
|
||||||
|
|
||||||
|
/* // Check for auto deposit */
|
||||||
|
/* if (!empty($ttype['auto_deposit'])) { */
|
||||||
|
/* $deposit[] = array('id' => 0, */
|
||||||
|
/* 'account_id' => $ttype['deposit_account_id']); */
|
||||||
|
/* } */
|
||||||
|
}
|
||||||
|
unset($entry); // prevent trouble since $entry is reference
|
||||||
|
|
||||||
$ids = $this->addTransaction($data['control'], $data['Transaction'], $data['Entry']);
|
$ids = $this->addTransaction($data['control'], $data['Transaction'], $data['Entry']);
|
||||||
if (isset($ids['transaction_id']))
|
if (isset($ids['transaction_id']))
|
||||||
$ids['receipt_id'] = $ids['transaction_id'];
|
$ids['receipt_id'] = $ids['transaction_id'];
|
||||||
|
if (!empty($ids['error']))
|
||||||
|
return $this->prReturn(array('error' => true) + $ids);
|
||||||
|
|
||||||
|
$tender_ids = array();
|
||||||
|
foreach ($ids['entries'] AS $entry) {
|
||||||
|
$entry1 = $entry['DoubleEntry']['Entry1'];
|
||||||
|
if (!empty($entry1['Tender']['tender_id']))
|
||||||
|
$tender_ids[] = $entry1['Tender']['tender_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$deposit_tenders = $this->LedgerEntry->Tender->find
|
||||||
|
('all', array('contain' => array('TenderType' => array('fields' => array()),
|
||||||
|
'LedgerEntry' => array('fields' => array()),
|
||||||
|
),
|
||||||
|
'fields' => array('TenderType.deposit_account_id',
|
||||||
|
'TenderType.account_id',
|
||||||
|
'CONCAT("CREDIT") AS crdr',
|
||||||
|
'CONCAT("Auto Deposit") AS comment',
|
||||||
|
'SUM(LedgerEntry.amount) AS amount'),
|
||||||
|
'conditions' => array('Tender.id' => $tender_ids,
|
||||||
|
'TenderType.auto_deposit' => true,
|
||||||
|
),
|
||||||
|
'group' => 'TenderType.deposit_account_id',
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!empty($deposit_tenders)) {
|
||||||
|
foreach ($deposit_tenders AS &$tender)
|
||||||
|
$tender = $tender[0] + array_diff_key($tender['TenderType'], array('id'=>1));
|
||||||
|
|
||||||
|
$this->pr(10, compact('tender_ids', 'deposit_tenders'));
|
||||||
|
|
||||||
|
// REVISIT <AP>: 20090817
|
||||||
|
// Multiple tenders could result in deposits to more than one
|
||||||
|
// account. We're already mucking with things by having a
|
||||||
|
// ledger entry that's not involved with the account_id of the
|
||||||
|
// transaction. We could handle this by not using the helper
|
||||||
|
// _splitEntries function, and just building or individual
|
||||||
|
// entries right here (which we should probably do anyway).
|
||||||
|
// However, I'm ignoring the issue for now...
|
||||||
|
if (count($deposit_tenders) > 1)
|
||||||
|
$this->INTERNAL_ERROR("Only expecting one tender type");
|
||||||
|
|
||||||
|
$deposit_ids = $this->addTransactionEntries
|
||||||
|
(array('include_ledger_entry' => true,
|
||||||
|
'include_statement_entry' => false,
|
||||||
|
),
|
||||||
|
|
||||||
|
array('id' => $ids['transaction_id'],
|
||||||
|
// REVISIT <AP>: 20090817
|
||||||
|
// This is an awful cheat, and we're going to
|
||||||
|
// get burned from it someday.
|
||||||
|
'type' => 'DEPOSIT',
|
||||||
|
'crdr' => 'DEBIT',
|
||||||
|
'account_id' => $deposit_tenders[0]['deposit_account_id'],
|
||||||
|
),
|
||||||
|
|
||||||
|
$deposit_tenders);
|
||||||
|
|
||||||
|
$ids['deposit'] = $deposit_ids;
|
||||||
|
if (!empty($deposit_ids['error']))
|
||||||
|
return $this->prReturn(array('error' => true) + $ids);
|
||||||
|
|
||||||
|
if (!empty($tender_ids))
|
||||||
|
$this->LedgerEntry->Tender->updateAll
|
||||||
|
(array('Tender.deposit_transaction_id' => $ids['transaction_id']),
|
||||||
|
array('Tender.id' => $tender_ids)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->prReturn($ids);
|
return $this->prReturn($ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// REVISIT <AP>: 20090817
|
||||||
|
// Delete after rolling up the old items
|
||||||
|
function temp_auto_deposit_old_ach_items() {
|
||||||
|
$all_tenders = $this->LedgerEntry->Tender->find
|
||||||
|
('all', array('link' => array('TenderType',
|
||||||
|
'LedgerEntry' =>
|
||||||
|
array('Transaction')),
|
||||||
|
'conditions' => array(array('TenderType.auto_deposit' => true),
|
||||||
|
array('Tender.deposit_transaction_id' => null)),
|
||||||
|
));
|
||||||
|
|
||||||
|
//$this->pr(10, compact('all_tenders'));
|
||||||
|
|
||||||
|
foreach ($all_tenders AS $cur_tender) {
|
||||||
|
$tender_ids = array($cur_tender['Tender']['id']);
|
||||||
|
$ids = array('transaction_id' => $cur_tender['Transaction']['id']);
|
||||||
|
|
||||||
|
$tenders = $this->LedgerEntry->Tender->find
|
||||||
|
('all', array('contain' => array('TenderType' => array('fields' => array()),
|
||||||
|
'LedgerEntry' => array('fields' => array()),
|
||||||
|
),
|
||||||
|
'fields' => array('TenderType.deposit_account_id',
|
||||||
|
'TenderType.account_id',
|
||||||
|
'CONCAT("CREDIT") AS crdr',
|
||||||
|
'CONCAT("Auto Deposit") AS comment',
|
||||||
|
'SUM(LedgerEntry.amount) AS amount'),
|
||||||
|
'conditions' => array('Tender.id' => $tender_ids,
|
||||||
|
'TenderType.auto_deposit' => true,
|
||||||
|
),
|
||||||
|
'group' => 'TenderType.deposit_account_id',
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($tenders AS &$tender)
|
||||||
|
$tender = $tender[0] + array_diff_key($tender['TenderType'], array('id'=>1));
|
||||||
|
|
||||||
|
$this->pr(10, compact('tender_ids', 'tenders'));
|
||||||
|
|
||||||
|
// REVISIT <AP>: 20090817
|
||||||
|
// Multiple tenders could result in deposits to more than one
|
||||||
|
// account. We're already mucking with things by having a
|
||||||
|
// ledger entry that's not involved with the account_id of the
|
||||||
|
// transaction. We could handle this by not using the helper
|
||||||
|
// _splitEntries function, and just building or individual
|
||||||
|
// entries right here (which we should probably do anyway).
|
||||||
|
// However, I'm ignoring the issue for now...
|
||||||
|
if (count($tenders) > 1)
|
||||||
|
$this->INTERNAL_ERROR("Only expecting one tender type");
|
||||||
|
|
||||||
|
$deposit_ids = $this->addTransactionEntries
|
||||||
|
(array('include_ledger_entry' => true,
|
||||||
|
'include_statement_entry' => false,
|
||||||
|
),
|
||||||
|
|
||||||
|
array('id' => $ids['transaction_id'],
|
||||||
|
// REVISIT <AP>: 20090817
|
||||||
|
// This is an awful cheat, and we're going to
|
||||||
|
// get burned from it someday.
|
||||||
|
'type' => 'DEPOSIT',
|
||||||
|
'crdr' => 'DEBIT',
|
||||||
|
'account_id' => $tenders[0]['deposit_account_id'],
|
||||||
|
),
|
||||||
|
|
||||||
|
$tenders);
|
||||||
|
|
||||||
|
if (empty($deposit_ids['error'])) {
|
||||||
|
if (!empty($tender_ids))
|
||||||
|
$this->LedgerEntry->Tender->updateAll
|
||||||
|
(array('Tender.deposit_transaction_id' => $ids['transaction_id']),
|
||||||
|
array('Tender.id' => $tender_ids)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
**************************************************************************
|
**************************************************************************
|
||||||
@@ -279,6 +436,7 @@ class Transaction extends AppModel {
|
|||||||
array('assign' => false,
|
array('assign' => false,
|
||||||
'include_ledger_entry' => true,
|
'include_ledger_entry' => true,
|
||||||
'include_statement_entry' => false,
|
'include_statement_entry' => false,
|
||||||
|
'allow_no_entries' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Establish the transaction as a close
|
// Establish the transaction as a close
|
||||||
@@ -656,7 +814,7 @@ class Transaction extends AppModel {
|
|||||||
|
|
||||||
// Verify that we have a transaction and entries
|
// Verify that we have a transaction and entries
|
||||||
if (empty($transaction) ||
|
if (empty($transaction) ||
|
||||||
($transaction['type'] !== 'CLOSE' && empty($entries)))
|
(empty($entries) && empty($control['allow_no_entries'])))
|
||||||
return $this->prReturn(array('error' => true));
|
return $this->prReturn(array('error' => true));
|
||||||
|
|
||||||
// set ledger ID as the current ledger of the specified account
|
// set ledger ID as the current ledger of the specified account
|
||||||
@@ -902,16 +1060,20 @@ class Transaction extends AppModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the sole ledger entry for this transaction
|
// Add the sole ledger entry for this transaction. If there
|
||||||
$rollback['Entry'][] =
|
// is not a transaction amount, then there is no point in
|
||||||
array('include_ledger_entry' => true,
|
// recording a ledger entry of $0.00
|
||||||
'include_statement_entry' => false,
|
if (!empty($rollback['Transaction']['amount'])) {
|
||||||
'amount' => $rollback['Transaction']['amount'],
|
$rollback['Entry'][] =
|
||||||
'account_id' => $this->Account->accountReceivableAccountID(),
|
array('include_ledger_entry' => true,
|
||||||
);
|
'include_statement_entry' => false,
|
||||||
|
'amount' => $rollback['Transaction']['amount'],
|
||||||
|
'account_id' => $this->Account->accountReceivableAccountID(),
|
||||||
|
);
|
||||||
|
|
||||||
// Set the transaction amount to be negative
|
// Set the transaction amount to be negative
|
||||||
$rollback['Transaction']['amount'] *= -1;
|
$rollback['Transaction']['amount'] *= -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Record the transaction, which will un-pay previously paid
|
// Record the transaction, which will un-pay previously paid
|
||||||
// charges, void any credits, and other similar work.
|
// charges, void any credits, and other similar work.
|
||||||
@@ -966,8 +1128,12 @@ class Transaction extends AppModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (empty($ret['rollback']['error']) && empty($ret['nsf_ledger_entry_id']))
|
if (empty($ret['rollback']['error']) && empty($ret['nsf_ledger_entry_id'])) {
|
||||||
$this->INTERNAL_ERROR("NSF LE ID not found under rollback entries");
|
//$this->INTERNAL_ERROR("NSF LE ID not found under rollback entries");
|
||||||
|
// Actually, this can happen if an item is NSF without having ever
|
||||||
|
// been applied to any charges.
|
||||||
|
$ret['nsf_ledger_entry_id'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
$ret['nsf_transaction_id'] = $ret['bounce']['transaction_id'];
|
$ret['nsf_transaction_id'] = $ret['bounce']['transaction_id'];
|
||||||
return $this->prReturn($ret + array('error' => false));
|
return $this->prReturn($ret + array('error' => false));
|
||||||
|
|||||||
28
todo.notes
28
todo.notes
@@ -2,12 +2,18 @@ Fix ACH deposits into bank. Make it happen automatically,
|
|||||||
perhaps after 3 days. Without this, we cannot NSF an ACH
|
perhaps after 3 days. Without this, we cannot NSF an ACH
|
||||||
transaction.
|
transaction.
|
||||||
|
|
||||||
|
Add NSF Fee to the NSF entry page (It's hardcoded right now
|
||||||
|
in Transaction to $35).
|
||||||
|
|
||||||
Sub-Total is broken, since it will only subtotal the current
|
Sub-Total is broken, since it will only subtotal the current
|
||||||
page of the grid. It needs to be implemented in SQL as it
|
page of the grid. It needs to be implemented in SQL as it
|
||||||
was in early (VERY early) implementations. At that time, I
|
was in early (VERY early) implementations. At that time, I
|
||||||
had to a use temporary variable to keep a running total. It
|
had to a use temporary variable to keep a running total. It
|
||||||
worked, but was MySQL specific.
|
worked, but was MySQL specific.
|
||||||
|
|
||||||
|
Unit Size has no controller. Either remove the link from the
|
||||||
|
units grid, or implement the controller.
|
||||||
|
|
||||||
Add a move-out charges field to the move-out page.
|
Add a move-out charges field to the move-out page.
|
||||||
Otherwise, if the balance is zero, the lease will automatically
|
Otherwise, if the balance is zero, the lease will automatically
|
||||||
be closed and no more charges are possible. The other option
|
be closed and no more charges are possible. The other option
|
||||||
@@ -44,10 +50,32 @@ Change the menu structure to be $menu['section']['item'], so that
|
|||||||
items don't have to be added in order of section. Perhaps even
|
items don't have to be added in order of section. Perhaps even
|
||||||
array(array(name, priority, items => array(name, priority, link)))
|
array(array(name, priority, items => array(name, priority, link)))
|
||||||
|
|
||||||
|
Change menu to be
|
||||||
|
'Reports' (or 'Overview', or 'Summary')
|
||||||
|
'Activities'
|
||||||
|
- New Receipt
|
||||||
|
- New Customer
|
||||||
|
- Move-in
|
||||||
|
|
||||||
|
Change New Customer form to have contact 'New' radio pre-checked
|
||||||
|
|
||||||
|
Add explanatory information on the New Customer page
|
||||||
|
- Customer name can be omitted and will come from primary tenant.
|
||||||
|
- Phone numbers, etc can be added later directly to the contact
|
||||||
|
|
||||||
|
Add dynamic check to see if customer already exists before being
|
||||||
|
created. Ideally, check +/- a few characters to check for
|
||||||
|
alternate spellings. Same for contact.
|
||||||
|
|
||||||
Reduce the number of cached items. Figure out how to get Cake to
|
Reduce the number of cached items. Figure out how to get Cake to
|
||||||
automatically make CONCAT(TenderType.name, ' #', Tender.id) part
|
automatically make CONCAT(TenderType.name, ' #', Tender.id) part
|
||||||
of each returned query.
|
of each returned query.
|
||||||
|
|
||||||
|
Implement, as part of the User model, a function to return the
|
||||||
|
security level. Have it be a static function, so that we don't
|
||||||
|
need to instantiate it, and right now, return a level based on
|
||||||
|
the route.
|
||||||
|
|
||||||
|
|
||||||
MUST reports:
|
MUST reports:
|
||||||
- Delinquent customers
|
- Delinquent customers
|
||||||
|
|||||||
Reference in New Issue
Block a user