diff --git a/db/schema.sql b/db/schema.sql index 0ba4220..c8933f1 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -922,13 +922,6 @@ UNLOCK TABLES; -- ---------------------------------------------------------------------- -- ---------------------------------------------------------------------- -- TABLE pmgr_ledgers --- --- REVISIT : 20090605 --- We may not really need a ledgers table. --- It's not clear to me though, as we very --- possibly need to close out certain --- ledgers every so often, and just carry --- the balance forward (year end, etc). DROP TABLE IF EXISTS `pmgr_ledgers`; CREATE TABLE `pmgr_ledgers` ( @@ -969,123 +962,144 @@ CREATE TABLE `pmgr_transactions` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `type` ENUM('INVOICE', - 'CREDIT', 'RECEIPT', - 'REFUND', + 'DEPOSIT', +-- 'CREDIT', +-- 'REFUND', 'TRANSFER') NOT NULL, `stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + -- The account/ledger of the transaction set + -- (e.g. A/R, Bank, etc) + `account_id` INT(10) UNSIGNED NOT NULL, + `ledger_id` INT(10) UNSIGNED NOT NULL, + + -- For convenience. Actually, INVOICE will always set crdr + -- to DEBIT, RECEIPT will use CREDIT, and DEPOSIT will use + -- DEBIT + `crdr` ENUM('DEBIT', + 'CREDIT') + NOT NULL, + -- amount is for convenience. It can always be calculated from -- the associated double entries (and therefore will need to be -- updated if they should change in any way). `amount` FLOAT(12,2) DEFAULT NULL, --- `customer_id` INT(10) UNSIGNED DEFAULT NULL, --- `lease_id` INT(10) UNSIGNED DEFAULT NULL, - `comment` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; --- -- -- ---------------------------------------------------------------------- --- -- -- ---------------------------------------------------------------------- --- -- -- TABLE pmgr_charge_details - --- DROP TABLE IF EXISTS `pmgr_charge_details`; --- CREATE TABLE `pmgr_charge_details` ( --- `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, --- -- Through date is used if/when a charge covers a certain time period, --- -- like rent. A security deposit, for example, would not use the --- -- through date. --- `through_date` DATE DEFAULT NULL, -- last day --- `due_date` DATE DEFAULT NULL, - --- PRIMARY KEY (`id`) --- ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - --- -- -- ---------------------------------------------------------------------- --- -- -- ---------------------------------------------------------------------- --- -- -- TABLE pmgr_payment_details - --- DROP TABLE IF EXISTS `pmgr_payment_details`; --- CREATE TABLE `pmgr_payment_details` ( --- `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - --- -- name may (or may not) be used to clarify in reports --- -- for example, 'Check #1234' as the payment name. --- `name` VARCHAR(80) DEFAULT NULL, - --- -- REVISIT : 20090716 --- -- Type may needs to be another table, so that the user --- -- can add new types. If so, they will also have to --- -- specify the required data1/2/3/4 fields. --- -- For now, this will work. --- `monetary_type` ENUM('CASH', --- 'CHECK', --- 'MONEYORDER', --- 'ACH', --- 'DEBITCARD', --- 'CREDITCARD') --- DEFAULT NULL, - --- -- REVISIT : 20090605 --- -- Check Number; --- -- Routing Number, Account Number; --- -- Card Number, Expiration Date; CVV2 Code --- -- etc. --- -- REVISIT 20090630 --- -- I _think_ that CVV2 is NEVER supposed to --- -- be stored ANYWHERE. Merchants agree to --- -- use it only to verify the transaction and --- -- then leave no record of it, so that even --- -- if their security is compromised, no one --- -- will know the CVV2 code unless they are --- -- in physical possession of the card. --- `data1` VARCHAR(80) DEFAULT NULL, --- `data2` VARCHAR(80) DEFAULT NULL, --- `data3` VARCHAR(80) DEFAULT NULL, --- `data4` VARCHAR(80) DEFAULT NULL, - --- PRIMARY KEY (`id`) --- ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - -- -- ---------------------------------------------------------------------- -- -- ---------------------------------------------------------------------- --- -- TABLE pmgr_entries +-- -- TABLE pmgr_ledger_entries -DROP TABLE IF EXISTS `pmgr_entries`; -CREATE TABLE `pmgr_entries` ( +DROP TABLE IF EXISTS `pmgr_ledger_entries`; +CREATE TABLE `pmgr_ledger_entries` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `type` ENUM('CHARGE', - 'PAYMENT', - 'DEPOSIT', - 'TRANSFER') - NOT NULL, + `transaction_id` INT(10) UNSIGNED NOT NULL, -- The account/ledger of the entry `account_id` INT(10) UNSIGNED NOT NULL, `ledger_id` INT(10) UNSIGNED NOT NULL, + -- For convenience. Actually, CHARGE will always set crdr + -- to CREDIT and PAYMENT will use DEBIT. `crdr` ENUM('DEBIT', 'CREDIT') NOT NULL, - -- The actual ledgers where this entry is recorded - `double_entry_id` INT(10) UNSIGNED DEFAULT NULL, + `amount` FLOAT(12,2) NOT NULL, + `comment` VARCHAR(255) DEFAULT NULL, + + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +-- -- ---------------------------------------------------------------------- +-- -- ---------------------------------------------------------------------- +-- -- TABLE pmgr_double_entries + +DROP TABLE IF EXISTS `pmgr_double_entries`; +CREATE TABLE `pmgr_double_entries` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + + -- The two entries that make up a "double entry" + `debit_entry_id` INT(10) UNSIGNED NOT NULL, + `credit_entry_id` INT(10) UNSIGNED NOT NULL, + + -- REVISIT : 20090720 + -- The amount from ledger_entries should be moved here to + -- eliminate duplication, and crdr should just be deleted. + -- However, it can always be changed later, and I thinks + -- those fields will come in handy when generating a + -- a ledger report. So, duplication for now. + + `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, + + `transaction_id` INT(10) UNSIGNED NOT NULL, + + -- Effective date is when the charge/payment/transfer actually + -- takes effect (since it may not be at the time of the transaction). -- Through date is used if/when a charge covers a certain time period, -- like rent. A security deposit, for example, would not use the -- through date. + `effective_date` DATE DEFAULT NULL, -- first day `through_date` DATE DEFAULT NULL, -- last day `due_date` DATE DEFAULT NULL, + `customer_id` INT(10) UNSIGNED NOT NULL, + `lease_id` INT(10) UNSIGNED NOT NULL, + + `amount` FLOAT(12,2) NOT NULL, + + -- The account of the entry + -- REVISIT : 20090720 + -- We don't want to confuse statement entries with ledger entries, + -- yet we're including account here. It doesn't feel right, but at + -- the same time, it will allow us to show _what_ was charged for + -- in the statement. Keeping it for now... + `account_id` INT(10) UNSIGNED DEFAULT NULL, + + -- Allow the payment to reconcile against the charge + `charge_entry_id` INT(10) UNSIGNED DEFAULT NULL, + + `comment` VARCHAR(255) DEFAULT NULL, + + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +-- ---------------------------------------------------------------------- +-- ---------------------------------------------------------------------- +-- TABLE pmgr_payments + +DROP TABLE IF EXISTS `pmgr_payments`; +CREATE TABLE `pmgr_payments` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + -- name may (or may not) be used to clarify in reports -- for example, 'Check #1234' as the payment name. `name` VARCHAR(80) DEFAULT NULL, @@ -1095,7 +1109,7 @@ CREATE TABLE `pmgr_entries` ( -- can add new types. If so, they will also have to -- specify the required data1/2/3/4 fields. -- For now, this will work. - `monetary_type` ENUM('CASH', + `type` ENUM('CASH', 'CHECK', 'MONEYORDER', 'ACH', @@ -1121,48 +1135,13 @@ CREATE TABLE `pmgr_entries` ( `data3` VARCHAR(80) DEFAULT NULL, `data4` VARCHAR(80) DEFAULT NULL, - `comment` VARCHAR(255) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - --- ---------------------------------------------------------------------- --- ---------------------------------------------------------------------- --- TABLE pmgr_reconciliations - -DROP TABLE IF EXISTS `pmgr_reconciliations`; -CREATE TABLE `pmgr_reconciliations` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - - `debit_entry_id` INT(10) UNSIGNED NOT NULL, - `credit_entry_id` INT(10) UNSIGNED NOT NULL, - `amount` FLOAT(12,2) NOT NULL, - - PRIMARY KEY (`id`) -) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - --- ---------------------------------------------------------------------- --- ---------------------------------------------------------------------- --- TABLE pmgr_double_entries - -DROP TABLE IF EXISTS `pmgr_double_entries`; -CREATE TABLE `pmgr_double_entries` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - - `transaction_id` INT(10) UNSIGNED DEFAULT NULL, - - -- Effective date is when the charge/payment/transfer actually - -- takes effect (since it may not be at the time of the transaction). - `effective_date` DATE DEFAULT NULL, -- first day - - `customer_id` INT(10) UNSIGNED DEFAULT NULL, - `lease_id` INT(10) UNSIGNED DEFAULT NULL, - - `debit_ledger_id` INT(10) UNSIGNED NOT NULL, - `credit_ledger_id` INT(10) UNSIGNED NOT NULL, - `amount` FLOAT(12,2) NOT NULL, + -- The ledger entry this physical payment applies to + `ledger_entry_id` INT(10) UNSIGNED NOT NULL, + -- The deposit that included these monies + `deposit_entry_id` INT(10) UNSIGNED DEFAULT NULL, + -- The NSF entry, should this come back from the bank. + `nsf_ledger_entry_id` INT(10) UNSIGNED DEFAULT NULL, `comment` VARCHAR(255) DEFAULT NULL, @@ -1172,193 +1151,33 @@ CREATE TABLE `pmgr_double_entries` ( -- ---------------------------------------------------------------------- -- ---------------------------------------------------------------------- --- TABLE pmgr_charges_payments +-- TABLE pmgr_deposits -DROP TABLE IF EXISTS `pmgr_charges_payments`; -CREATE TABLE `pmgr_charges_payments` ( +DROP TABLE IF EXISTS `pmgr_deposits`; +CREATE TABLE `pmgr_deposits` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - -- Mark which charge the payment reconciles, and how much was applied - `charge_entry_id` INT(10) UNSIGNED NOT NULL, - `payment_entry_id` INT(10) UNSIGNED NOT NULL, + `transaction_id` INT(10) UNSIGNED NOT NULL, + + -- The account/ledger of the entry + `account_id` INT(10) UNSIGNED NOT NULL, + `ledger_id` INT(10) UNSIGNED NOT NULL, + + -- For convenience. Should always be DEBIT (unless we + -- decide to credit NSF instead of a negative debit). + `crdr` ENUM('DEBIT') + NOT NULL DEFAULT 'DEBIT', + `amount` FLOAT(12,2) NOT NULL, + `comment` VARCHAR(255) DEFAULT NULL, + PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; --- -- ---------------------------------------------------------------------- --- -- ---------------------------------------------------------------------- --- -- TABLE pmgr_folders --- -- --- -- It's a dumb name, but at the moment, it's how I'm thinking about --- -- this all. In the generic, a purchase begins with a purchase order --- -- from the customer. When you receive it, you grab a new manilla --- -- folder, drop the purchase order in, and start making the widgets. --- -- when their ready, you ship them with invoice, and drop a copy of --- -- the invoice in the folder. When they pay, you generate a receipt, --- -- and drop a copy in the folder. If the check bounces, you note --- -- that in the folder, and generate a new NSF fee invoice (which gets --- -- put into its OWN new folder). When they pay again, you generate --- -- another receipt and put it into the folder, and this process could --- -- repeat until the invoice finally gets paid, or you write it off as --- -- a bad debt, which would be noted, of course, in the folder. - --- DROP TABLE IF EXISTS `pmgr_folders`; --- CREATE TABLE `pmgr_folders` ( --- `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, --- `stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, --- `comment` VARCHAR(255) DEFAULT NULL, - --- `customer_id` INT(10) UNSIGNED DEFAULT NULL, --- `lease_id` INT(10) UNSIGNED DEFAULT NULL, - --- PRIMARY KEY (`id`) --- ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - --- -- ---------------------------------------------------------------------- --- -- ---------------------------------------------------------------------- --- -- TABLE pmgr_invoices - --- DROP TABLE IF EXISTS `pmgr_invoices`; --- CREATE TABLE `pmgr_invoices` ( --- `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - --- `stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - --- -- amount is for convenience. It can always be calculated from --- -- the attached ledger entries (and therefore will need to be --- -- updated if they should change in any way). --- `amount` FLOAT(12,2) DEFAULT NULL, - --- -- `customer_id` INT(10) UNSIGNED DEFAULT NULL, --- -- `lease_id` INT(10) UNSIGNED DEFAULT NULL, - --- -- `folder_id` INT(10) UNSIGNED NOT NULL, - --- `comment` VARCHAR(255) DEFAULT NULL, - --- PRIMARY KEY (`id`) --- ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - --- -- ---------------------------------------------------------------------- --- -- ---------------------------------------------------------------------- --- -- TABLE pmgr_charges - --- DROP TABLE IF EXISTS `pmgr_charges`; --- CREATE TABLE `pmgr_charges` ( --- `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - --- -- Effective date is when the charge actually takes effect --- -- (since it may not be at the time of the invoice). Through --- -- date is used if/when a charge covers a certain time period, --- -- like rent. A security deposit, for example, would not use --- -- the through date. --- `effective_date` DATE DEFAULT NULL, -- first day --- `through_date` DATE DEFAULT NULL, -- last day --- `due_date` DATE DEFAULT NULL, - --- `customer_id` INT(10) UNSIGNED DEFAULT NULL, --- `lease_id` INT(10) UNSIGNED DEFAULT NULL, --- `amount` FLOAT(12,2) DEFAULT NULL, --- `comment` VARCHAR(255) DEFAULT NULL, - --- PRIMARY KEY (`id`) --- ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - --- -- ---------------------------------------------------------------------- --- -- ---------------------------------------------------------------------- --- -- TABLE pmgr_payments - --- DROP TABLE IF EXISTS `pmgr_payments`; --- CREATE TABLE `pmgr_payments` ( --- `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - --- `stamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - --- -- Customer ID ensures that they payment doesn't get lost --- -- in the system if either a) it's a pre-payment, or --- -- b) the associated invoice is deleted. --- `customer_id` INT(10) UNSIGNED DEFAULT NULL, --- `amount` FLOAT(12,2) DEFAULT NULL, - --- -- name may (or may not) be used to clarify in reports --- -- for example, 'Check #1234' as the payment name. --- `name` VARCHAR(80) DEFAULT NULL, - --- -- REVISIT : 20090716 --- -- Type may needs to be another table, so that the user --- -- can add new types. If so, they will also have to --- -- specify the required data1/2/3/4 fields. --- -- For now, this will work. --- `type` ENUM('CASH', --- 'CHECK', --- 'MONEYORDER', --- 'ACH', --- 'DEBITCARD', --- 'CREDITCARD') --- DEFAULT NULL, - --- -- REVISIT : 20090605 --- -- Check Number; --- -- Routing Number, Account Number; --- -- Card Number, Expiration Date; CVV2 Code --- -- etc. --- -- REVISIT 20090630 --- -- I _think_ that CVV2 is NEVER supposed to --- -- be stored ANYWHERE. Merchants agree to --- -- use it only to verify the transaction and --- -- then leave no record of it, so that even --- -- if their security is compromised, no one --- -- will know the CVV2 code unless they are --- -- in physical possession of the card. --- `data1` VARCHAR(80) DEFAULT NULL, --- `data2` VARCHAR(80) DEFAULT NULL, --- `data3` VARCHAR(80) DEFAULT NULL, --- `data4` VARCHAR(80) DEFAULT NULL, - --- `comment` VARCHAR(255) DEFAULT NULL, - --- PRIMARY KEY (`id`) --- ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - --- -- ---------------------------------------------------------------------- --- -- ---------------------------------------------------------------------- --- -- TABLE pmgr_transfers - --- DROP TABLE IF EXISTS `pmgr_transfers`; --- CREATE TABLE `pmgr_transfers` ( --- `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - --- -- Effective date is when the charge actually takes effect --- -- (since it may not be at the time of the invoice). Through --- -- date is used if/when a charge covers a certain time period, --- -- like rent. A security deposit, for example, would not use --- -- the through date. --- `effective_date` DATE DEFAULT NULL, -- first day --- `through_date` DATE DEFAULT NULL, -- last day --- `due_date` DATE DEFAULT NULL, - --- `customer_id` INT(10) UNSIGNED DEFAULT NULL, --- `lease_id` INT(10) UNSIGNED DEFAULT NULL, --- `amount` FLOAT(12,2) DEFAULT NULL, --- `comment` VARCHAR(255) DEFAULT NULL, - --- PRIMARY KEY (`id`) --- ) ENGINE=MyISAM DEFAULT CHARSET=utf8; - - - - - - - -- ###################################################################### -- ###################################################################### -- ###################################################################### diff --git a/scripts/sitelink2pmgr.pl b/scripts/sitelink2pmgr.pl index a851bde..50abe08 100644 --- a/scripts/sitelink2pmgr.pl +++ b/scripts/sitelink2pmgr.pl @@ -914,6 +914,9 @@ foreach $row (@{query($sdbh, $query)}) { addRow('transactions', { 'type' => 'INVOICE', 'stamp' => $stamp, + 'account_id' => $newdb{'lookup'}{'account'}{'A/R'}{'account_id'}, + 'ledger_id' => $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'}, + 'crdr' => 'DEBIT', #'amount' => $row->{'InvoiceAmount'}, #'comment' => "Invoice Transaction", }); @@ -965,49 +968,47 @@ foreach $row (@{query($sdbh, $query)}) { = $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'}; # debit: A/R credit: Rent/LateCharge/Etc + foreach ('debit', 'credit') { + my $CRDR = $_; + $CRDR =~ tr/a-z/A-Z/; + addRow('ledger_entries', { + 'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'id'}, + 'account_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{$_.'_account_id'}, + 'ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{$_.'_ledger_id'}, + 'crdr' => $CRDR, + 'amount' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'amount'}, + 'comment' => "$_ Ledger Entry: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}", + }); + + $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{$_.'_entry_id'} + = $newdb{'tables'}{'ledger_entries'}{'autoid'}; + } + addRow('double_entries', { - 'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'id'}, - 'effective_date' => $effective_date, - 'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}, - 'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}, - 'debit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'}, - 'credit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'}, - 'amount' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'amount'}, + #'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'id'}, + 'debit_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_entry_id'}, + 'credit_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_entry_id'}, 'comment' => "Double Entry: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}", }); $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'double_entry_id'} = $newdb{'tables'}{'double_entries'}{'autoid'}; - # Add the Charge Entry - addRow('entries', { + # Add the Charge Statement Entry + addRow('statement_entries', { + 'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'id'}, 'type' => 'CHARGE', + 'effective_date' => $effective_date, 'through_date' => $through_date, - 'double_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'double_entry_id'}, + 'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}, + 'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}, 'account_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_account_id'}, - 'ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'}, - 'crdr' => 'CREDIT', + 'amount' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'amount'}, 'comment' => "Charge: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}", }); - $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'entry_id'} - = $newdb{'tables'}{'entries'}{'autoid'}; - - # Add the A/R entry - addRow('entries', { - 'type' => 'TRANSFER', - 'double_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'double_entry_id'}, - 'account_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_account_id'}, - 'ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'}, - 'crdr' => 'DEBIT', - 'comment' => "Charge A/R: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}", - }); - - $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ar_entry_id'} - = $newdb{'tables'}{'entries'}{'autoid'}; - - $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'amount'} - = $row->{'ChargeAmount'}; + $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_entry_id'} + = $newdb{'tables'}{'statement_entries'}{'autoid'}; next unless $row->{'TaxAmount'}; @@ -1043,86 +1044,6 @@ foreach $row (@{query($sdbh, $query)}) { $newdb{'lookup'}{'receipt'} = {}; -############################################################ -############################################################ -############################################################ -# REVISIT 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| | | | | 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 -# 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. @@ -1153,6 +1074,8 @@ foreach $row (@{query($sdbh, $query)}) { $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}} = { 'date' => $stamp, + 'effective_date' => $effective_date, + 'through_date' => $through_date, 'amount' => $row->{'ReceiptAmount'}, }; @@ -1185,6 +1108,9 @@ foreach $row (@{query($sdbh, $query)}) { addRow('transactions', { 'type' => 'RECEIPT', 'stamp' => $stamp, + 'account_id' => $newdb{'lookup'}{'account'}{'A/R'}{'account_id'}, + 'ledger_id' => $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'}, + 'crdr' => 'CREDIT', }); $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'} @@ -1206,48 +1132,45 @@ foreach $row (@{query($sdbh, $query)}) { $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'} = $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'}; - # debit: Cash/Check/Etc credit: A/R + + foreach ('debit', 'credit') { + my $CRDR = $_; + $CRDR =~ tr/a-z/A-Z/; + addRow('ledger_entries', { + 'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'}, + 'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{$_.'_account_id'}, + 'ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{$_.'_ledger_id'}, + 'crdr' => $CRDR, + 'amount' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'amount'}, + 'comment' => "$_ Entry Receipt: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", + }); + + $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{$_.'_entry_id'} + = $newdb{'tables'}{'ledger_entries'}{'autoid'}; + } + addRow('double_entries', { - 'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'}, - 'effective_date' => $effective_date, - 'customer_id' => undef, # This is set later... - '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' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'amount'}, - 'comment' => "Double Entry Receipt: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", + #'transaction_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'}, + + 'debit_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_entry_id'}, + 'credit_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_entry_id'}, + 'comment' => "Double Entry: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", }); $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'} = $newdb{'tables'}{'double_entries'}{'autoid'}; - # Add the Payment Entry - addRow('entries', { - 'type' => 'PAYMENT', - 'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_account_id'}, - 'ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_ledger_id'}, - 'crdr' => 'DEBIT', - 'double_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'}, + # Add the physical payment + addRow('payments', { + 'ledger_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'debit_entry_id'}, 'name' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'name'}, - 'monetary_type' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'type'}, + 'type' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'type'}, 'data1' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'data1'}, - 'comment' => "Receipt: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", + 'comment' => "Physical Payment: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", }); - $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'entry_id'} - = $newdb{'tables'}{'entries'}{'autoid'}; - - # Add the A/R Entry - addRow('entries', { - 'type' => 'TRANSFER', - 'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_account_id'}, - 'ledger_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_ledger_id'}, - 'crdr' => 'CREDIT', - 'double_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'}, - 'comment' => "Receipt A/R: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}", - }); - - $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ar_entry_id'} - = $newdb{'tables'}{'entries'}{'autoid'}; + $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'payment_id'} + = $newdb{'tables'}{'payments'}{'autoid'}; } @@ -1260,49 +1183,40 @@ $newdb{'lookup'}{'payment'} = {}; $query = "SELECT * FROM Payments ORDER BY PaymentID"; foreach $row (@{query($sdbh, $query)}) { - $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}} - = { 'receipt_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'}, - 'ar_entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ar_entry_id'}, - 'entry_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'entry_id'}, - 'amount' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'amount'}, - }; - - # Ensure Payment has the right customer - $newdb{'tables'}{'double_entries'}{'rows'}[ - $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'double_entry_id'} - ]{'customer_id'} = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}; - - next - if ($newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'reconciled'}); - # Figure out how much of the charge can be reconciled my $charge_amount = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'amount'}; - my $payment_amount = $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'amount'}; + my $payment_amount = $row->{'PaymentAmount'}; my $reconcile_amount = ($charge_amount < $payment_amount) ? $charge_amount : $payment_amount; - # Reconcile the A/R Account - addRow('reconciliations', { - 'debit_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ar_entry_id'}, - 'credit_entry_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'ar_entry_id'}, - 'amount' => $reconcile_amount, + $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}} + = { 'receipt_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'receipt_id'}, + 'effective_date' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'effective_date'}, + 'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}, + 'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}, + 'amount' => $reconcile_amount, + + 'account_id' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'credit_account_id'}, + }; + + + my $comment = $row->{'Memo'} || "Payment: $row->{'ReceiptNum'}; Type: $row->{'PaymentType'}"; + + # Add the Payment Statement Entry + addRow('statement_entries', { + 'transaction_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'receipt_id'}, + 'type' => 'PAYMENT', + 'effective_date' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'effective_date'}, + 'customer_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'customer_id'}, + 'lease_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'lease_id'}, + 'account_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'account_id'}, + 'amount' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'amount'}, + 'charge_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'statement_entry_id'}, + 'comment' => $comment, }); - # Reconcile the payment to the charge - addRow('charges_payments', { - 'charge_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'entry_id'}, - 'payment_entry_id' => $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'entry_id'}, - 'amount' => $reconcile_amount, - }); - - $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'reconciled'} = 1; - - # Update the transaction to use the memo from this payment - if ($row->{'Memo'}) { - my $id = $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'entry_id'}; - $newdb{'tables'}{'entries'}{'rows'}[$id]{'comment'} = $row->{'Memo'}; - } - + $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'statement_entry_id'} + = $newdb{'tables'}{'statement_entries'}{'autoid'}; } @@ -1328,93 +1242,95 @@ print("Set up Petty Cash...\n"); addRow('transactions', { 'type' => 'TRANSFER', 'stamp' => datefmt('03/25/2009 16:00'), - }); -addRow('double_entries', { - 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, - 'effective_date' => $effective_date, - 'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, - 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'}, - 'amount' => 5000, + 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, + 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, + 'crdr' => 'DEBIT', 'comment' => "HTP Loan #1", }); -addRow('entries', { - 'type' => 'TRANSFER', +addRow('ledger_entries', { + 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, 'crdr' => 'DEBIT', - 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, + 'amount' => 5000, 'comment' => "Equity: HTP Loan #1", }); -addRow('entries', { - 'type' => 'TRANSFER', +addRow('ledger_entries', { + 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, 'account_id' => $newdb{'lookup'}{'account'}{'Loan'}{'account_id'}, 'ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'}, 'crdr' => 'CREDIT', - 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, + 'amount' => 5000, 'comment' => "Loan: HTP Loan #1", }); +addRow('double_entries', { + 'debit_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'}-1, + 'credit_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'}, + }); # Add the second loan # debit: Equity credit: Loan addRow('transactions', { 'type' => 'TRANSFER', 'stamp' => datefmt('04/01/2009 16:00'), - }); -addRow('double_entries', { - 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, - 'effective_date' => $effective_date, - 'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, - 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'}, - 'amount' => 1000, + 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, + 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, + 'crdr' => 'DEBIT', 'comment' => "HTP Loan #2", }); -addRow('entries', { - 'type' => 'TRANSFER', +addRow('ledger_entries', { + 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, 'crdr' => 'DEBIT', - 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, + 'amount' => 1000, 'comment' => "Equity: HTP Loan #2", }); -addRow('entries', { - 'type' => 'TRANSFER', +addRow('ledger_entries', { + 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, 'account_id' => $newdb{'lookup'}{'account'}{'Loan'}{'account_id'}, 'ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'}, 'crdr' => 'CREDIT', - 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, + 'amount' => 1000, 'comment' => "Loan: HTP Loan #2", }); +addRow('double_entries', { + 'debit_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'}-1, + 'credit_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'}, + }); + # Cheat for now, using equity for Petty Cash # debit: Petty Cash credit: Equity addRow('transactions', { 'type' => 'TRANSFER', 'stamp' => datefmt('03/25/2009 16:00'), - }); -addRow('double_entries', { - 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, - 'effective_date' => $effective_date, - 'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'ledger_id'}, - 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, - 'amount' => 750, - 'comment' => "Petty Cash Funding", - }); -addRow('entries', { - 'type' => 'TRANSFER', 'account_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'account_id'}, 'ledger_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'ledger_id'}, 'crdr' => 'DEBIT', - 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, + 'comment' => "Petty Cash Funding", + }); +addRow('ledger_entries', { + 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, + 'account_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'account_id'}, + 'ledger_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'ledger_id'}, + 'crdr' => 'DEBIT', + 'amount' => 750, 'comment' => "Petty Cash: Petty Cash Funding", }); -addRow('entries', { - 'type' => 'TRANSFER', +addRow('ledger_entries', { + 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, 'account_id' => $newdb{'lookup'}{'account'}{'Equity'}{'account_id'}, 'ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'}, 'crdr' => 'CREDIT', - 'double_entry_id' => $newdb{'tables'}{'double_entries'}{'autoid'}, + 'amount' => 750, 'comment' => "Equity: Petty Cash Funding", }); +addRow('double_entries', { + 'debit_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'}-1, + 'credit_entry_id' => $newdb{'tables'}{'ledger_entries'}{'autoid'}, + }); + ###################################################################### ## Build the Database @@ -1481,9 +1397,9 @@ query($db_handle, $query); print("Set Invoice/Receipt Totals...\n"); -$query = "UPDATE pmgr_transactions T, pmgr_double_entries DE - SET T.`amount` = COALESCE(T.`amount`,0) + DE.amount - WHERE DE.transaction_id = T.id"; +$query = "UPDATE pmgr_transactions T, pmgr_ledger_entries E + SET T.`amount` = COALESCE(T.`amount`,0) + E.amount + WHERE E.transaction_id = T.id AND E.account_id = T.account_id"; query($db_handle, $query);