Files
pmgr/scripts/sitelink2pmgr.pl

1495 lines
53 KiB
Perl

#perl -w
use strict;
use DBI;
use Data::Dumper;
use File::Copy;
my $use_invoice = 0;
my $use_receipt = 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
# scale, and this factor ensures that any fractional units
# become whole (e.g. 7.5 "feet" becomes 90 "units")
my $internal_adjustment_factor = 12;
my $schema_file = shift || die("Must specify schema file\n");
my $slink_file = shift || die("Must specify sitelink file\n");
my $slink_file = ";Data Source=$slink_file";
my $slink_pass = ";Jet OLEDB:Database Password=2web";
my $sdsn="Provider=Microsoft.Jet.OLEDB.4.0$slink_pass$slink_file";
my $sdbh = DBI->connect("dbi:ADO:$sdsn", undef, undef, {PrintError => 1,
RaiseError => 1});
# Connect to the database.
my($hostname, $database, $user, $password) = ('localhost',
'property_manager',
'pmgr', 'pmgruser');
$database = shift if @ARGV;
$user = shift if @ARGV;
$password = shift if @ARGV;
print "Connecting to $database as $user\n";
my $db_handle = DBI->connect("DBI:mysql:database=$database;host=$hostname",
$user, $password,
{'PrintError' => 1,
'RaiseError' => 0});
$SIG{__DIE__} = \&Die;
my ($query, $result, $nrows, $row);
my (%newdb) = ( 'schema' => [],
'tables' => {},
'ids' => {},
'lookup' => {'state' => { 1=>'AK', 14=>'ID', 48=>'WA' }} );
######################################################################
######################################################################
## Die
sub Die {
my @text = @_;
warn "----------------------------------------------------------\n";
warn "FATAL ERROR: @text\n";
my $count = 0;
{
my ($package, $filename, $line, $sub) = caller($count);
last unless defined $line;
warn sprintf("%02i %5i %-35s %-20s\n", $count++, $line, $sub,
$filename);
redo;
}
exit 1;
}
######################################################################
######################################################################
## addRow
sub addRow {
my ($table, $cols, $noid) = @_;
die unless $table;
die unless ref($cols) eq 'HASH';
die "Table `$table` is unknown!"
unless defined $newdb{'tables'}{$table};
my $id;
if ($noid) {
$id = $cols->{'id'};
}
if (!defined $id) {
$id = ++$newdb{'tables'}{$table}{'autoid'};
}
$cols->{'id'} = $id
unless ($noid);
# For debug purposes
my $line = (caller())[2];
$cols->{'--LINE--'} = "addRow called from line: $line";
$newdb{'tables'}{$table}{'rows'}[$id] = $cols;
return $id;
}
######################################################################
######################################################################
## updateRow
sub updateRow {
my ($table, $id, $cols) = @_;
die unless $table;
die unless $id;
die unless ref($cols) eq 'HASH';
die "Table `$table` is unknown!"
unless defined $newdb{'tables'}{$table};
die "Table `$table` : ID `$id` does not exist!"
unless $newdb{'tables'}{$table}{'rows'}[$id];
# For debug purposes
my $line = (caller())[2];
$cols->{'--UPDATE-LINE--'} = [] unless $cols->{'--UPDATE-LINE--'};
push(@{$cols->{'--UPDATE-LINE--'}}, "updateRow called from line: $line");
#$newdb{'tables'}{$table}{'rows'}[$id] = $cols;
return $id;
}
######################################################################
######################################################################
## query
sub query {
my ($dbh, $sql, $data, $ignore) = @_;
#print("$sql\n\n"); #return [ { 'id' => 7 } ];
#print("$sql\n\n") if $sql =~ /^\s*UPDATE/i;
#return 1 unless $sql =~ /^\s*SELECT/i;
my ($sth, $result);
if ($sql =~ /^\s*SELECT/i) {
$sth = $dbh->prepare($sql);
$sth->execute();
$result = $sth->fetchall_arrayref({});
} else {
$result = $dbh->do($sql);
}
if (!$result && !$ignore) {
print STDERR "SQL Query FAILED:\n";
print STDERR "$sql\n\n";
print STDERR Dumper $data
if defined $data;
die;
}
return ($sth, $result);
}
######################################################################
######################################################################
## executeSchema
sub executeSchema {
foreach (@{$newdb{'schema'}}) {
query($db_handle, $_);
}
foreach my $table (values %{$newdb{'tables'}}) {
foreach (@{$table->{'schema'}}) {
query($db_handle, $_);
}
}
}
######################################################################
######################################################################
## buildTables
sub buildTables {
foreach my $table_name (sort keys %{$newdb{'tables'}}) {
my $table = $newdb{'tables'}{$table_name};
my $count = 0;
foreach (@{$table->{'rows'}}) {
next unless defined $_;
++$count;
}
printf("%-30s : %d rows\n", $table->{'name'}, $count);
foreach (@{$table->{'rows'}}) {
next unless defined $_;
my %row = %$_;
delete $row{'--LINE--'};
my $query;
$query = "INSERT INTO " . $table->{'name'};
$query .= " (" . join(", ", map({"`$_`"} keys(%row))) . ")";
$query .= " VALUES (" . join(", ", map({s/'/''/g if defined $_;
defined $_
? (/^\w+\(.*\)$/
? $_
: "'$_'" )
: "NULL" }
values(%row))) . ")";
query($db_handle, $query, $_);
}
}
}
######################################################################
######################################################################
## helper functions
sub sizeCode {
my ($width, $depth) = @_;
return "YARD"
if ($width == 12 && $depth == 40);
return "APARTMENT"
if ($width == 20 && $depth == 30);
return sprintf("%02dx%02d", $width, $depth);
}
sub datefmt {
my ($dt) = @_;
return undef unless $dt;
my @dt = split(/\/|\s/, $dt);
#print("$dt : " . sprintf("%04d-%02d-%02d", $dt[2], $dt[0], $dt[1]) . "\n");
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));
}
######################################################################
######################################################################
######################################################################
######################################################################
## BUILD THE DATABASE
open(SCHEMA, "<$schema_file") || die ("Can't open schema ($!)\n");
my $schema_query = "";
my $table;
while (<SCHEMA>) {
next if /^\s*--/;
if (/DROP\s+TABLE\s.*`?(pmgr_(\w+))/i) {
$table = $2;
$newdb{'tables'}{$table} = { 'name' => $1,
'schema' => [],
'autoid' => 0,
'rows' => [] };
}
$schema_query .= $_;
if (/;\s*$/) {
$schema_query =~ s/^\s+//;
$schema_query =~ s/\s*;\s*$//;
if (!$table) {
push(@{$newdb{'schema'}}, $schema_query);
} else {
push(@{$newdb{'tables'}{$table}{'schema'}}, $schema_query);
}
$schema_query = "";
}
}
close(SCHEMA);
executeSchema();
#################################################################
## Test Contact
addRow('contacts',
{ 'first_name' => 'Abijah',
# 'middle_name' => 'M',
'last_name' => 'Perkins' });
addRow('contact_addresses',
{ 'address' => '1324 N Liberty Lake Rd\nPMB 263',
'city' => 'Liberty Lake',
'state' => 'WA',
'postcode' => '99019',
'country' => 'USA' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_addresses'}{'autoid'},
'method' => 'ADDRESS',
'type' => 'MAIN',
'preference' => 'PRIMARY' },
1);
addRow('contact_addresses',
{ 'address' => '5221 W Myrtlewood Ct',
'city' => 'Spokane',
'state' => 'WA',
'postcode' => '99208',
'country' => 'USA' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_addresses'}{'autoid'},
'method' => 'ADDRESS',
'type' => 'HOME',
'preference' => 'ALTERNATE' },
1);
# addRow('contact_addresses',
# { 'address' => 'PO Box 69',
# 'city' => 'Granger',
# 'state' => 'WA',
# 'postcode' => '98932',
# 'country' => 'USA' });
# addRow('contacts_methods',
# { 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
# 'method_id' => $newdb{'tables'}{'contact_addresses'}{'autoid'},
# 'method' => 'ADDRESS',
# 'type' => 'HOME',
# 'preference' => 'ALTERNATE' },
# 1);
addRow('contact_phones',
{ 'type' => 'MOBILE',
'phone' => '5098445573' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
'method' => 'PHONE',
'type' => 'MAIN',
'preference' => 'PRIMARY' },
1);
addRow('contact_phones',
{ 'type' => 'MOBILE',
'phone' => '5098445973' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
'method' => 'PHONE',
'type' => 'MAIN',
'preference' => 'ALTERNATE' },
1);
addRow('contact_phones',
{ 'type' => 'VIRTUAL',
'phone' => '5095901112' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
'method' => 'PHONE',
'type' => 'BUSINESS',
'preference' => 'WORK' },
1);
# addRow('contact_phones',
# { 'type' => 'LANDLINE',
# 'phone' => '5098541491' });
# addRow('contacts_methods',
# { 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
# 'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
# 'method' => 'PHONE',
# 'type' => 'HOME',
# 'preference' => 'ALTERNATE' },
# 1);
addRow('contact_phones',
{ 'type' => 'VIRTUAL',
'phone' => '8774488664' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
'method' => 'PHONE',
'type' => 'BUSINESS',
'preference' => 'WORK' },
1);
addRow('contact_phones',
{ 'type' => 'FAX',
'phone' => '8662960131' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
'method' => 'PHONE',
'type' => 'BUSINESS',
'preference' => 'WORK' },
1);
addRow('contact_emails',
{ 'email' => 'abijah\@PerkinsHouse.com' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_emails'}{'autoid'},
'method' => 'EMAIL',
'type' => 'HOME',
'preference' => 'PRIMARY' },
1);
addRow('contact_emails',
{ 'email' => 'abijah\@PerkinsREI.com' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_emails'}{'autoid'},
'method' => 'EMAIL',
'type' => 'HOME',
'preference' => 'WORK' },
1);
addRow('contact_emails',
{ 'email' => 'abijah\@ValleyStorage.info' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'},
'method_id' => $newdb{'tables'}{'contact_emails'}{'autoid'},
'method' => 'EMAIL',
'type' => 'BUSINESS',
'preference' => 'WORK' },
1);
#################################################################
## GROUPS
addRow('groups',
{ 'code' => 'Owner',
'name' => 'Owner Group' });
addRow('group_permissions',
{ 'group_id' => $newdb{'tables'}{'groups'}{'autoid'},
'name' => 'EVERYTHING',
'access' => 'FORCED' },
1);
#################################################################
## USERS
addRow('users',
{ 'code' => 'AP',
'login' => 'abijah',
'contact_id' => $newdb{'tables'}{'contacts'}{'autoid'} });
#################################################################
## SITES
addRow('sites',
{ 'code' => 'VSS',
'name' => 'Valley Storage' });
addRow('site_memberships',
{ 'site_id' => $newdb{'tables'}{'sites'}{'autoid'},
'user_id' => $newdb{'tables'}{'users'}{'autoid'},
'group_id' => $newdb{'tables'}{'groups'}{'autoid'} },
1);
addRow('site_areas',
{ 'site_id' => $newdb{'tables'}{'sites'}{'autoid'},
'code' => 'Main',
'name' => 'Main Facility Area' });
#################################################################
## LEASES
addRow('lease_types',
{ 'code' => 'SL',
'name' => 'Storage Lease' });
#################################################################
## LEDGERS
$newdb{'lookup'}{'account'} = {};
$query = "SELECT * FROM pmgr_accounts";
$result = query($db_handle, $query);
foreach $row (@$result) {
addRow('ledgers',
{ 'account_id' => $row->{'id'},
'comment' => undef });
$newdb{'lookup'}{'account'}{$row->{'name'}}
= {'account_id' => $row->{'id'},
'tillable' => $row->{'tillable'},
'ledger_id' => $newdb{'tables'}{'ledgers'}{'autoid'} };
if ((!defined $newdb{'tables'}{'accounts'}{'autoid'}) ||
$row->{'id'} > $newdb{'tables'}{'accounts'}{'autoid'}) {
$newdb{'tables'}{'accounts'}{'autoid'} = $row->{'id'};
}
}
# For compatibility, while deciding on account names
if (!defined $newdb{'lookup'}{'account'}{'Cash'}) {
$newdb{'lookup'}{'account'}{'Cash'}
= $newdb{'lookup'}{'account'}{'Till'};
}
$newdb{'lookup'}{'charge_type'} = {};
$newdb{'lookup'}{'charge_type'}{'Rent'} =
$newdb{'lookup'}{'account'}{'Rent'};
$newdb{'lookup'}{'charge_type'}{'Late Fee'} =
$newdb{'lookup'}{'account'}{'Late Charge'};
$newdb{'lookup'}{'charge_type'}{'Security Deposit'} =
$newdb{'lookup'}{'account'}{'Security Deposit'};
#################################################################
## MONETARY
$newdb{'lookup'}{'payment_type'} = {};
$newdb{'lookup'}{'payment_type'}{1}
= { 'name' => 'Cash', 'account_name' => 'Cash' };
$newdb{'lookup'}{'payment_type'}{2}
= { 'name' => 'Check', 'account_name' => 'Check' };
$newdb{'lookup'}{'payment_type'}{3}
= { 'name' => 'Money Order', 'account_name' => 'Money Order' };
$newdb{'lookup'}{'payment_type'}{4}
= { 'name' => 'ACH', 'account_name' => 'Bank' };
$newdb{'lookup'}{'payment_type'}{12}
= { 'name' => 'Concession', 'account_name' => 'Concession' };
$newdb{'ids'}{'monetary_source'} = {};
$newdb{'ids'}{'monetary_source'}{'internal'} = undef;
addRow('monetary_sources',
{ 'name' => 'Cash',
'comment' => 'Monetary source used for any cash transaction' });
$newdb{'ids'}{'monetary_source'}{'Cash'} =
$newdb{'tables'}{'monetary_sources'}{'autoid'};
addRow('monetary_sources',
{ 'name' => 'Closing',
'comment' => 'Credited at the closing table' });
$newdb{'ids'}{'monetary_source'}{'Closing'} =
$newdb{'tables'}{'monetary_sources'}{'autoid'};
######################################################################
######################################################################
######################################################################
######################################################################
######################################################################
######################################################################
##
## UNITS
##
my @unit_sort_order =
qw(
A01 A02 A03 A04 A05 A06
B01 B02 B03 B04 B05 B06 B07
C01 C02 C03 C04 C05 C06 C07 C08 C09
10 11 12 13 14 15 16 17 18 19 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
D01 D02 D03 D04 D05 D06 D07 D08 D09 D10 D11
APT
Y01 Y02 Y03 Y04 Y05 Y06 Y07 Y08 Y09 Y10
);
my $unit_sort_order = 0;
my %unit_sort_order;
foreach (@unit_sort_order) {
$unit_sort_order += 1; # use 100 for later insertion
$unit_sort_order{$_} = $unit_sort_order;
}
my @unit_walk_order =
qw(
19 18 17 16 15 14 13 12 11 10
C01 C02 C03 C04 C05 C06 C07 C08 C09
APT
38 37 36 35 34 33 32 31 30 29 28
B01 B02 B03 B04 B05 B06 B07
A01 A02 A03 A04 A05 A06
27 26 25 24 23 22
D11 D10 D09 D08 D07 D06 D05 D04 D03 D02 D01
45 44 43 42 41 40 39
Y01 Y02 Y03 Y04 Y05 Y06 Y07 Y08 Y09 Y10
);
my $unit_walk_order = 0;
my %unit_walk_order;
foreach (@unit_walk_order) {
$unit_walk_order += 1; # use 100 for later insertion
$unit_walk_order{$_} = $unit_walk_order;
}
######################################################################
## Unit Types
$newdb{'lookup'}{'unit_type'} = {};
$query = "SELECT * FROM UnitType ORDER BY TypeID";
foreach $row (@{query($sdbh, $query)}) {
addRow('unit_types',
{ 'code' => 'xxx',
'name' => $row->{'UnitType'} });
$newdb{'lookup'}{'unit_type'}{$row->{'TypeID'}} =
$newdb{'tables'}{'unit_types'}{'autoid'};
}
######################################################################
## Unit Sizes
$newdb{'lookup'}{'unit_size'} = {};
$query = "SELECT * FROM UnitInfo WHERE UnitID <> 'POS\$' ORDER BY UnitID";
foreach $row (@{query($sdbh, $query)}) {
my $sz = sizeCode($row->{'Width'}, $row->{'Depth'});
next if defined $newdb{'lookup'}{'unit_size'}{$sz};
addRow('unit_sizes',
{ 'unit_type_id' => $row->{'Type'},
'code' => $sz,
'name' => $sz,
'width' => $internal_adjustment_factor * $row->{'Width'},
'depth' => $internal_adjustment_factor * $row->{'Depth'},
'deposit' => $row->{'StdSecDep'},
'rent' => $row->{'StdRent'} });
$newdb{'lookup'}{'unit_size'}{$sz}
= { 'id' => $newdb{'tables'}{'unit_sizes'}{'autoid'},
'rent' => $row->{'StdRent'},
'dep' => $row->{'StdSecDep'} };
}
######################################################################
## Units
$newdb{'lookup'}{'unit'} = {};
foreach $row (@{query($sdbh, $query)}) {
my $sz = sizeCode($row->{'Width'}, $row->{'Depth'});
my $szid = $newdb{'lookup'}{'unit_size'}{$sz}{'id'};
addRow('units',
{ 'unit_size_id' => $szid,
'code' => $row->{'UnitID'},
'name' => $row->{'UnitID'},
'status' => $row->{'Rented'} ?'OCCUPIED' :($row->{'Rentable'} ?'VACANT' :'UNAVAILABLE'),
'sort_order' => $unit_sort_order{$row->{'UnitID'}},
'walk_order' => $unit_walk_order{$row->{'UnitID'}},
'deposit' => $row->{'StdSecDep'},
'rent' => $row->{'StdRent'} });
$newdb{'lookup'}{'unit'}{$row->{'UnitID'}}
= { 'id' => $newdb{'tables'}{'units'}{'autoid'} };
}
######################################################################
## Map
my %info = ('extents' => {}, 'units' => {});
# Get the overall site limits
$query = "SELECT MIN(M.Top) AS mintop, MIN(M.Left) AS minlft,";
$query .= " MAX(M.Top + IIF(M.reverseWL, U.Width, U.Depth)) AS bot,";
$query .= " MAX(M.Left + IIF(M.reverseWL, U.Depth, U.Width)) AS rgt";
$query .= ' FROM UnitInfo U INNER JOIN mapUnitsV2 M ON M.unitID = U.UnitID';
$result = query($sdbh, $query);
# Fetch and verify the result
my $row = shift(@$result);
die("MIN query failed!") unless $row;
# Compute the actual boundaries, adjusting for a (0,0) origin
my $top_adjustment = 0 - $row->{'mintop'};
my $left_adjustment = 0 - $row->{'minlft'};
$info{'extents'}{'top'} = 0;
$info{'extents'}{'left'} = 0;
$info{'extents'}{'bottom'} = $internal_adjustment_factor * ($top_adjustment + $row->{'bot'} + 0);
$info{'extents'}{'right'} = $internal_adjustment_factor * ($left_adjustment + $row->{'rgt'} + 0);
addRow('maps',
{ 'name' => 'Main Facility Map',
'site_area_id' => $newdb{'tables'}{'site_areas'}{'autoid'},
'width' => $info{'extents'}{'right'} - $info{'extents'}{'left'},
'depth' => $info{'extents'}{'bottom'} - $info{'extents'}{'top'} });
# Get list of units and positions
$query = "SELECT U.UnitID, U.UnitID as name,";
$query .= " ($top_adjustment + M.Top) AS pt_t,";
$query .= " ($left_adjustment + M.Left) AS pt_l,";
$query .= " IIF(M.reverseWL, U.Depth, U.Width) AS Width,";
$query .= " IIF(M.reverseWL, U.Width, U.Depth) AS Depth,";
$query .= " M.reverseWL, U.Rented, U.Rentable";
$query .= " FROM UnitInfo U INNER JOIN mapUnitsV2 M ON M.unitID = U.UnitID";
# Go through each one, calculating the map location
foreach $row (@{query($sdbh, $query)}) {
addRow('maps_units',
{ 'map_id' => $newdb{'tables'}{'maps'}{'autoid'},
'unit_id' => $newdb{'lookup'}{'unit'}{$row->{'UnitID'}}{'id'},
'pt_top' => $internal_adjustment_factor * ($row->{'pt_t'}),
'pt_left' => $internal_adjustment_factor * ($row->{'pt_l'}),
'transpose' => $row->{'reverseWL'} });
}
######################################################################
######################################################################
######################################################################
######################################################################
######################################################################
######################################################################
##
## TENANTS
##
######################################################################
## Tenants
$newdb{'lookup'}{'tenant'} = {};
$query = "SELECT * FROM TenantInfo WHERE FirstName <> 'POS' ORDER BY TenantID";
foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}
= { 'name' => "$row->{'LastName'}, $row->{'FirstName'}" };
addRow('contacts',
{ 'first_name' => $row->{'FirstName'},
'middle_name' => $row->{'MiddleName'},
'last_name' => $row->{'LastName'},
'id_local' => $row->{'IDNum'} || undef,
'id_local_state' => $row->{'IDNum'} ? $newdb{'lookup'}{'state'}{$row->{'DLStateID'}} : undef });
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'} =
$newdb{'tables'}{'contacts'}{'autoid'};
addRow('customers',
{ 'name' => "$row->{'LastName'}, $row->{'FirstName'}",
'primary_contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'},
});
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'} =
$newdb{'tables'}{'customers'}{'autoid'};
addRow('contacts_customers',
{ 'customer_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'},
'contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'},
'type' => 'TENANT' },
1);
if ($row->{'City'}) {
addRow('contact_addresses',
{ 'address' => $row->{'HomeAddress'} . ($row->{'HomeAddr2'} ? "\n".$row->{'HomeAddr2'} : "") || undef,
'city' => $row->{'City'},
'state' => $newdb{'lookup'}{'state'}{$row->{'StateID'}},
'postcode' => $row->{'Zip'} || undef,
'country' => 'USA' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'},
'method_id' => $newdb{'tables'}{'contact_addresses'}{'autoid'},
'method' => 'ADDRESS',
'type' => 'HOME',
'preference' => 'PRIMARY' },
1);
}
foreach ({'type' => 'LANDLINE', 'preference' => 'PRIMARY', 'phone' => $row->{'Phone'}},
{'type' => 'LANDLINE', 'preference' => 'WORK',
'phone' => $row->{'BusinessPhone'}, 'ext' => $row->{'BusinessExt'}},
{'type' => 'FAX', 'preference' => 'PRIMARY', 'phone' => $row->{'FAX'}},
{'type' => 'PAGER', 'preference' => 'PRIMARY', 'phone' => $row->{'Pager'}},
{'type' => 'MOBILE', 'preference' => 'ALTERNATE', 'phone' => $row->{'CellPhone'}})
{
if ($_->{'phone'}) {
addRow('contact_phones',
{ 'type' => $_->{'type'},
'phone' => $_->{'phone'},
'ext' => $_->{'ext'} });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'},
'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
'method' => 'PHONE',
'type' => 'MAIN',
'preference' => $_->{'preference'} },
1);
}
}
if ($row->{'Email'}) {
addRow('contact_emails',
{ 'email' => $row->{'Email'} });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'id'},
'method_id' => $newdb{'tables'}{'contact_emails'}{'autoid'},
'method' => 'EMAIL',
'type' => 'MAIN',
'preference' => 'PRIMARY' },
1);
}
next unless $row->{'AltFirstName'} || $row->{'AltLastName'} || $row->{'AltAddress'} || $row->{'AltPhone'};
addRow('contacts',
{ 'first_name' => $row->{'AltFirstName'} || undef,
'middle_name' => $row->{'AltMI'} || undef,
'last_name' => $row->{'AltLastName'} || undef });
$newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'alt'} =
$newdb{'tables'}{'contacts'}{'autoid'};
addRow('contacts_customers',
{ 'customer_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'},
'contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'alt'},
'type' => 'ALTERNATE' },
1);
if ($row->{'AltCity'}) {
addRow('contact_addresses',
{ 'address' => $row->{'AltAddress'} . ($row->{'AltAddr2'} ? "\n".$row->{'AltAddr2'} : ""),
'city' => $row->{'AltCity'},
'state' => $newdb{'lookup'}{'state'}{$row->{'AltStateID'}},
'postcode' => $row->{'AltZip'},
'country' => 'USA' });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'alt'},
'method_id' => $newdb{'tables'}{'contact_addresses'}{'autoid'},
'method' => 'ADDRESS',
'type' => 'MAIN',
'preference' => 'PRIMARY' },
1);
}
if ($row->{'AltPhone'}) {
addRow('contact_phones',
{ 'type' => 'LANDLINE',
'phone' => $row->{'AltPhone'},
'ext' => undef });
addRow('contacts_methods',
{ 'contact_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'alt'},
'method_id' => $newdb{'tables'}{'contact_phones'}{'autoid'},
'method' => 'PHONE',
'type' => 'MAIN',
'preference' => 'PRIMARY' },
1);
}
}
######################################################################
## Tenant Leases
$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";
foreach $row (@{query($sdbh, $query)}) {
$newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}
= { 'customer_id' => $newdb{'lookup'}{'tenant'}{$row->{'TenantID'}}{'customer_id'},
'lease_date' => $row->{'DateIn'},
};
addRow('leases',
{ 'number' => $newdb{'tables'}{'leases'}{'autoid'}+1,
#'number' => $row->{'LedgerID'},
'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'},
'lease_date' => datefmt($row->{'DateIn'}),
'movein_date' => datefmt($row->{'DateIn'}),
'moveout_date' => datefmt($row->{'DateOut'}),
'close_date' => datefmt($row->{'DateClosed'}),
'rent' => $row->{'Rent'} });
$newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_id'} =
$newdb{'tables'}{'leases'}{'autoid'};
}
######################################################################
######################################################################
######################################################################
######################################################################
######################################################################
######################################################################
##
## TRANSACTIONS
##
######################################################################
## Invoices
$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' => $stamp,
#'comment' => "Invoice Transaction",
});
$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'},
} };
# Charges are the only way we have to figure out security
# deposit requirements for a lease. So, if we encounter
# a security deposit charge, update the lease to reflect.
if ($row->{'ChargeDescription'} eq 'Security Deposit') {
$newdb{'tables'}{'leases'}{'rows'}[
$newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_id'}
]{'deposit'} = $row->{'ChargeAmount'};
}
# Invoice must debit the A/R ledger...
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'debit_ledger_id'}
= $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'};
if ($use_invoice) {
addRow('transactions',
{ 'stamp' => $stamp,
#'comment' => "Charges Transaction",
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'charge_tx'}
= $newdb{'tables'}{'transactions'}{'autoid'};
# ...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',
{ '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'},
'root_transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'charge_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'};
}
else {
# OK, no invoice ledger
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'charge_tx'}
= $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'tx'};
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'credit_ledger_id'}
= $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'debit_ledger_id'};
}
}
######################################################################
## Charges
$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'}
= $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 Charge ledger...
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'}
= $newdb{'lookup'}{'charge_type'}{$row->{'ChargeDescription'}}{'ledger_id'};
# ...and debit the Invoice 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',
{ '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'},
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'},
'amount' => $row->{'ChargeAmount'},
#'comment' => "Charge: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}",
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
if ($use_invoice) {
# 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'},
});
}
else {
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'}
= $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ledger_entry_id'};
}
next unless $row->{'TaxAmount'};
# Add the tax charge entry
# debit: Invoice credit: Tax
addRow('ledger_entries',
{ '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'},
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'},
'amount' => $row->{'TaxAmount'},
#'comment' => "Tax for ChargeID:$row->{'ChargeID'}",
});
$newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tax_ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
if ($use_invoice) {
# 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'},
});
}
}
######################################################################
## Receipts
$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| | | | | 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.
$query =
"SELECT R.ReceiptNum, R.ReceiptDate, P.PaymentType, P.CheckNum, SUM(P.PaymentAmount) AS ReceiptAmount" .
" FROM Receipts R, Payments P" .
" WHERE P.ReceiptNum = R.ReceiptNum" .
" GROUP BY R.ReceiptNum, R.ReceiptDate, P.PaymentType, P.CheckNum" .
" ORDER BY R.ReceiptNum, P.PaymentType";
foreach $row (@{query($sdbh, $query)}) {
# if ($newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}) {
# next;
# }
my ($stamp, $effective_date, $through_date) =
dates('receipt', $row->{'ReceiptDate'}, undef);
if (!$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}) {
addRow('transactions',
{ 'stamp' => $stamp,
#'comment' => "Receipt Transaction",
});
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}
= {'tx' => $newdb{'tables'}{'transactions'}{'autoid'},
'date' => $stamp,
};
addRow('transactions',
{ 'stamp' => $stamp,
#'comment' => "Payment Split Transaction",
});
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'payment_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'};
}
# 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'};
my $data1;
if ($name eq 'Check') {
$name = 'Check #' . $row->{'CheckNum'};
$data1 = $row->{'CheckNum'};
}
addRow('monetary_sources',
{ 'name' => $name,
'data1' => $data1,
#'comment' => "Receipt:$row->{'ReceiptNum'}; Payment:$row->{'PaymentType'}; Check:$row->{'CheckNum'}",
});
$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',
{ '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'},
'amount' => $row->{'ReceiptAmount'},
#'comment' => "Receipt: $row->{'ReceiptNum'}; ",
});
$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{$row->{'PaymentType'}}{'ledger_entry_id'}
= $newdb{'tables'}{'ledger_entries'}{'autoid'};
}
######################################################################
## Payments
$newdb{'lookup'}{'payment'} = {};
$query = "SELECT * FROM Payments ORDER BY PaymentID";
foreach $row (@{query($sdbh, $query)})
{
my (undef, $effective_date, $through_date) =
dates('payment', $row->{'PaymentDate'}, undef);
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}
= { 'tx' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'payment_tx'} };
# 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.
# 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'};
# 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'};
# ...and credit the A/R ledger.
$newdb{'lookup'}{'payment'}{$row->{'PaymentID'}}{'credit_ledger_id'}
= $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'};
# Add the payment entry
# debit: Receipt credit: A/R
addRow('ledger_entries',
{ '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'},
'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'},
'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'},
'amount' => $row->{'PaymentAmount'},
#'comment' => "Split; 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'},
});
# Figure out how much of the charge can be reconciled
my $reconcile_amount = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'amount'};
#print Dumper($newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}); exit;
$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.
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,
});
# Update the transaction to use the memo from this payment
if ($row->{'Memo'}) {
my $txid = $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'tx'};
$newdb{'tables'}{'transactions'}{'rows'}[$txid]{'comment'} = $row->{'Memo'};
}
}
######################################################################
## Special case - Equities / Loans / Petty Cash
my ($stamp, $effective_date, $through_date);
print("Set up Petty Cash...\n");
# Add the first loan
# debit: Equity credit: Loan
addRow('transactions',
{ 'stamp' => datefmt('03/25/2009 16:00'),
});
addRow('ledger_entries',
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'},
'customer_id' => undef,
'lease_id' => undef,
'amount' => 5000,
'comment' => "HTP Loan #1",
});
# Add the second loan
# debit: Equity credit: Loan
addRow('transactions',
{ 'stamp' => datefmt('04/01/2009 16:00'),
});
addRow('ledger_entries',
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Loan'}{'ledger_id'},
'customer_id' => undef,
'lease_id' => undef,
'amount' => 1000,
'comment' => "HTP Loan #2",
});
# Cheat for now, using equity for Petty Cash
# debit: Petty Cash credit: Equity
addRow('transactions',
{ 'stamp' => datefmt('03/25/2009 16:00'),
});
addRow('ledger_entries',
{ 'effective_date' => $effective_date,
'through_date' => $through_date,
'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'},
'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'},
'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Petty Cash'}{'ledger_id'},
'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Equity'}{'ledger_id'},
'customer_id' => undef,
'lease_id' => undef,
'amount' => 750,
'comment' => "Petty Cash Funding",
});
######################################################################
## Build the Database
$Data::Dumper::Sortkeys = 1;
# print Dumper \%newdb;
# exit;
buildTables();
######################################################################
## Special cases - Name corrections
print("Special Cases...\n");
$query =
"UPDATE pmgr_contacts" .
" SET first_name = 'Krystan'" .
" WHERE first_name = 'Kristan' AND last_name = 'Mancini'";
query($db_handle, $query);
$query =
"UPDATE pmgr_customers" .
" SET name = 'Mancini, Krystan'" .
" WHERE name = 'Mancini, Kristan'";
query($db_handle, $query);
$query =
"UPDATE pmgr_contacts" .
" SET first_name = NULL, last_name = NULL, company_name = 'Valley Bible Church'" .
" WHERE first_name = 'VBC' AND last_name = 'Tenant'";
query($db_handle, $query);
$query =
"UPDATE pmgr_customers" .
" SET name = 'Valley Bible Church'" .
" WHERE name = 'Tenant, VBC'";
query($db_handle, $query);
######################################################################
## Contact Display Names
print("Set Contact Display Names...\n");
$query =
"UPDATE pmgr_contacts".
" SET display_name =" .
" IF(first_name IS NOT NULL AND last_name IS NOT NULL," .
" CONCAT(last_name, ', ', first_name)," .
" IF(first_name IS NOT NULL, first_name, last_name))";
query($db_handle, $query);
######################################################################
## Unit Lease Assignments
print("Set Current Leases...\n");
$query = "UPDATE pmgr_units U, pmgr_leases L
SET U.`current_lease_id` = L.id
WHERE L.unit_id = U.id AND L.close_date IS NULL";
query($db_handle, $query);