#perl -w use strict; use DBI; use Data::Dumper; use File::Copy; my $use_ir = 1; # Internally adjust all numbers coming from the database to # be in inches. Not necessary to go to this detail, but the # actual units used is irrelevant, provided everything is to # 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'); 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 (values %{$newdb{'tables'}}) { my $count = 0; foreach (@{$table->{'rows'}}) { next unless defined $_; ++$count; } printf(STDERR "%-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] : ""); } ###################################################################### ###################################################################### ###################################################################### ###################################################################### ## BUILD THE DATABASE open(SCHEMA, "<$schema_file") || die ("Can't open schema ($!)\n"); my $schema_query = ""; my $table; while () { 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' => 'POST', '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' => 'POST', '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' => 'POST', '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.com' }); 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'}{'monetary_type'} = {}; $query = "SELECT * FROM pmgr_monetary_types"; $result = query($db_handle, $query); foreach $row (@$result) { $newdb{'lookup'}{'monetary_type'}{$row->{'name'}} = {'id' => $row->{'id'} }; } $newdb{'lookup'}{'payment_type'} = {}; $newdb{'lookup'}{'payment_type'}{1} = { 'name' => 'Cash', 'account_name' => 'Cash', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Cash'} }; $newdb{'lookup'}{'payment_type'}{2} = { 'name' => 'Check', 'account_name' => 'Check', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Check'} }; $newdb{'lookup'}{'payment_type'}{3} = { 'name' => 'Money Order', 'account_name' => 'Money Order', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Money Order'} }; $newdb{'lookup'}{'payment_type'}{4} = { 'name' => 'ACH', 'account_name' => 'Bank', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'ACH'} }; $newdb{'lookup'}{'payment_type'}{12} = { 'name' => 'Concession', 'account_name' => 'Concession', 'monetary_type' => $newdb{'lookup'}{'monetary_type'}{'Other Non-Tillable'} }; $newdb{'ids'}{'monetary_source'} = {}; $newdb{'ids'}{'monetary_source'}{'internal'} = undef; addRow('monetary_sources', { 'monetary_type_id' => $newdb{'lookup'}{'monetary_type'}{'Cash'}{'id'}, 'name' => 'Cash', 'comment' => 'Monetary source used for any cash transaction' }); $newdb{'ids'}{'monetary_source'}{'Cash'} = $newdb{'tables'}{'monetary_sources'}{'autoid'}; addRow('monetary_sources', { 'monetary_type_id' => $newdb{'lookup'}{'monetary_type'}{'Other Non-Tillable'}{'id'}, 'name' => 'Closing', 'comment' => 'Credited at the closing table' }); $newdb{'ids'}{'monetary_source'}{'Closing'} = $newdb{'tables'}{'monetary_sources'}{'autoid'}; ###################################################################### ###################################################################### ###################################################################### ###################################################################### ###################################################################### ###################################################################### ## ## UNITS ## ###################################################################### ## 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'}, 'amount' => $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' => $newdb{'tables'}{'units'}{'autoid'}, 'walk_order' => $newdb{'tables'}{'units'}{'autoid'}, 'deposit' => $row->{'StdSecDep'}, 'amount' => $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' => 'POST', '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' => 'POST', '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'} }; 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'}), 'amount' => $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)}) { addRow('transactions', { 'stamp' => datefmt($row->{'ChargeDate'}), 'through_date' => datefmt($row->{'EndDate'}) }); $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}} = { 'invoice' => { 'tx' => $newdb{'tables'}{'transactions'}{'autoid'}, 'lease_id' => $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'lease_id'}, 'customer_id' => $newdb{'lookup'}{'ledger'}{$row->{'LedgerID'}}{'customer_id'}, 'amount' => $row->{'InvoiceAmount'}, } }; addRow('transactions', { 'stamp' => datefmt($row->{'ChargeDate'}), 'through_date' => datefmt($row->{'EndDate'}) }); $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'charge_tx'} = $newdb{'tables'}{'transactions'}{'autoid'}; # Invoice must debit the A/R ledger... $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'debit_ledger_id'} = $newdb{'lookup'}{'account'}{'A/R'}{'ledger_id'}; # ...and credit the Invoice ledger. $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'credit_ledger_id'} = $newdb{'lookup'}{'account'}{'Invoice'}{'ledger_id'}; # Create the invoice entry # debit: A/R credit: Invoice addRow('ledger_entries', { 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'}, 'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'tx'}, 'debit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'debit_ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'credit_ledger_id'}, 'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'customer_id'}, 'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'lease_id'}, 'amount' => $row->{'InvoiceAmount'}, 'comment' => "Invoice: Charge: $row->{'ChargeID'}" }); $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'} = $newdb{'tables'}{'ledger_entries'}{'autoid'}; } ###################################################################### ## Charges $query = "SELECT * FROM Charges ORDER BY ChargeID"; foreach $row (@{query($sdbh, $query)}) { $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tx'} = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'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 invoice ledger... $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'} = $newdb{'lookup'}{'charge_type'}{$row->{'ChargeDescription'}}{'ledger_id'}; # ...and debit the A/R ledger. $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'} = $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'credit_ledger_id'}; # Add the charge entry # debit: Invoice credit: Rent/LateCharge/Etc addRow('ledger_entries', { 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'}, 'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tx'}, 'debit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'credit_ledger_id'}, 'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}, 'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}, 'amount' => $row->{'ChargeAmount'}, 'comment' => "Charge: $row->{'ChargeID'}; Ledger: $row->{'LedgerID'}" }); $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ledger_entry_id'} = $newdb{'tables'}{'ledger_entries'}{'autoid'}; # Reconcile the Invoice account. Our two entries look like: # debit: Invoice credit: Rent/LateCharge/Etc # debit: A/R credit: Invoice # Since this is from the perspective of the Invoice account, # the credit entry is the Invoice<->A/R, and the debit # entry is the actual charge ledger entry. addRow('reconciliations', { 'debit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'ledger_entry_id'}, 'credit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'}, 'amount' => $row->{'ChargeAmount'}, }); next unless $row->{'TaxAmount'}; # Add the tax charge entry # debit: Invoice credit: Tax addRow('ledger_entries', { 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'}, 'transaction_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tx'}, 'debit_ledger_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'debit_ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Tax'}{'ledger_id'}, 'customer_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'customer_id'}, 'lease_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'lease_id'}, 'amount' => $row->{'TaxAmount'}, 'comment' => "Tax for ChargeID:$row->{'ChargeID'}" }); $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tax_ledger_entry_id'} = $newdb{'tables'}{'ledger_entries'}{'autoid'}; # Reconcile the Invoice account. Our two entries look like: # debit: Invoice credit: Tax # debit: A/R credit: Invoice # Since this is from the perspective of the Invoice account, # the credit entry is the Invoice<->A/R, and the debit # entry is the actual tax ledger entry. addRow('reconciliations', { 'debit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'tax_ledger_entry_id'}, 'credit_ledger_entry_id' => $newdb{'lookup'}{'charge'}{$row->{'ChargeID'}}{'invoice'}{'ledger_entry_id'}, 'amount' => $row->{'TaxAmount'}, }); } ###################################################################### ## Receipts $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| | | | | # | | 5 5| | | | | # | | |55 55| | | | # |40 | 40| | | | | # | | 4 4| | | | | # | | |44 44| | | | # | | | | |79 | 79| # | | | | |20 20| | # | | | |50 50| | | # | | | | 5 5| | | # | | | |40 40| | | # | | | | 4 4| | | # | | | | | | | # # There is another possible solution, although it is # very probably ledger overkill (even invoice/receipt # already fall into the overkill category). # # RENT TAX INVOICE A/R MERGE RECEIPT CASH CHECK # ------- ------- ------- ------- ------- ------- ------- ------- # |50 | 50| | | | | | # | | 5 5| | | | | | # | | |55 55| | | | | # |40 | 40| | | | | | # | | 4 4| | | | | | # | | |44 44| | | | | # | | | | | |79 | 79| # | | | | | |20 20| | # | | | | |50 50| | | # | | | | | 5 5| | | # | | | | |40 40| | | # | | | | | 4 4| | | # | | | |99 99| | | | # | | | | | | | | # # I might opt for this last option, but ultimately, none # of these are really correct. We need a better solution. # Until then, I'll go with the easiest. ############################################################ ############################################################ ############################################################ # Sitelink splits one physical payment into multiple "payments" to match each charge. # The solution here is kludgy, but for our cases at least, it brings those pseudo-payments # back into a single one. This presumes there is only one PaymentType per receipt. $query = "SELECT R.ReceiptNum, R.ReceiptDate, P.PaymentType, P.CheckNum, SUM(P.PaymentAmount) AS ReceiptAmount" . " FROM Receipts R, Payments P" . " 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; # } if (!$newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}) { addRow('transactions', { 'stamp' => datefmt($row->{'ReceiptDate'}), 'through_date' => undef }); $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}} = {'tx' => $newdb{'tables'}{'transactions'}{'autoid'}, 'date' => datefmt($row->{'ReceiptDate'}), }; addRow('transactions', { 'stamp' => datefmt($row->{'ReceiptDate'}), 'through_date' => undef }); $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', { 'monetary_type_id' => $newdb{'lookup'}{'payment_type'}{$row->{'PaymentType'}}{'monetary_type'}{'id'}, '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', { '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)}) { # addRow('transactions', # { 'stamp' => $newdb{'lookup'}{'receipt'}{$row->{'ReceiptNum'}}{'date'}, # 'through_date' => undef }); # $newdb{'lookup'}{'payment'}{$row->{'PaymentID'}} # = {'tx' => $newdb{'tables'}{'transactions'}{'autoid'} }; $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', { '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' => "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 STDERR 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'}; } } ###################################################################### ## Fake Data for Testing my %fake = ('customer_id' => 4, 'ledger_id' => 4, 'invoice' => [ { 'id' => 1000, 'date' => '2009-05-05', 'entry' => [ { 'id' => 2100, 'account_name' => 'Security Deposit', 'amount' => 10, 'tax' => 0 }, ], }, { 'id' => 1001, 'date' => '2009-05-01', 'thru' => '2009-05-31', 'entry' => [ { 'id' => 2110, 'account_name' => 'Rent', 'amount' => 100, 'tax' => 5 }, ], }, { 'id' => 1002, 'date' => '2009-05-11', 'entry' => [ { 'id' => 2120, 'account_name' => 'Late Charge', 'amount' => 25, 'tax' => 0 }, ], }, ], 'receipt' => [ { 'id' => 2000, 'date' => '2009-05-15', 'entry' => [ { 'id' => 2200, 'track' => [ { 'debit'=>2100, 'amount'=>10 }, { 'debit'=>2110, 'amount'=>5 } ], 'type' => 1, 'amount' => 15 }, { 'id' => 2201, 'track' => [ { 'debit'=>2110, 'amount'=>10 } ], 'type' => 2, 'amount' => 10 }, { 'id' => 2202, 'track' => [ { 'debit'=>2110, 'amount'=>5 } ], 'type' => 3, 'amount' => 5 }, ], }, { 'id' => 2001, 'date' => '2009-05-18', 'entry' => [ { 'id' => 2210, 'track' => [ { 'debit'=>2110, 'amount'=>30 } ], 'type' => 5, 'amount' => 30 }, { 'id' => 2211, 'track' => [ { 'debit'=>2110, 'amount'=>20 } ], 'type' => 6, 'amount' => 20 }, ], }, { 'id' => 2002, 'date' => '2009-05-22', 'entry' => [ { 'id' => 2220, 'track' => [ { 'debit'=>2110, 'amount'=>10 } ], 'type' => 1, 'amount' => 10 }, { 'id' => 2221, 'track' => [ { 'debit'=>2110, 'amount'=>5 } ], 'type' => 2, 'amount' => 5 }, { 'id' => 2222, 'track' => [ { 'debit'=>2110, 'amount'=>15 }, { 'debit'=>2111, 'amount'=>5 }, { 'debit'=>2120, 'amount'=>5 } ], 'type' => 7, 'amount' => 25 }, { 'id' => 2223, 'track' => [ { 'debit'=>2120, 'amount'=>20 } ], 'type' => 8, 'amount' => 30 }, ], }, ], ); sub fakeTesting { $newdb{'ids'}{'ledger'}{'Cash-Old'} = $newdb{'lookup'}{'account'}{'Cash'}{'ledger_id'}; addRow('ledgers', { 'account_id' => $newdb{'lookup'}{'account'}{'Cash'}{'account_id'}, 'open_stamp' => 'NOW()', 'sequence' => 2, 'comment' => 'Opened new ledger for testing' }); $newdb{'lookup'}{'account'}{'Cash'}{'ledger_id'} = $newdb{'tables'}{'ledgers'}{'autoid'}; my $balance = 0; foreach $row (@{$newdb{'tables'}{'ledger_entries'}{'rows'}}) { next unless defined $row; $balance += $row->{'amount'} if $row->{'debit_ledger_id'} == $newdb{'ids'}{'ledger'}{'Cash-Old'}; $balance -= $row->{'amount'} if $row->{'credit_ledger_id'} == $newdb{'ids'}{'ledger'}{'Cash-Old'}; } addRow('transactions', { 'stamp' => '2009-04-30' }); addRow('ledger_entries', { 'monetary_source_id' => $newdb{'ids'}{'monetary_source'}{'internal'}, 'transaction_id' => $newdb{'tables'}{'transactions'}{'autoid'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{'Cash'}{'ledger_id'}, 'credit_ledger_id' => $newdb{'ids'}{'ledger'}{'Cash-Old'}, 'amount' => $balance, 'name' => "Close Out ($newdb{'ids'}{'ledger'}{'Cash-Old'} -> $newdb{'tables'}{'transactions'}{'autoid'})", 'comment' => "Carrying forward old ledger balance onto new ledger" }); # NOTE: no tracking for the Cash account $newdb{'tables'}{'ledgers'}{'rows'}[$newdb{'ids'}{'ledger'}{'Cash-Old'}]{'closed'} = 1; $newdb{'tables'}{'ledgers'}{'rows'}[$newdb{'ids'}{'ledger'}{'Cash-Old'}]{'close_stamp'} = 'NOW()'; foreach my $ir ('invoice', 'receipt') { foreach my $tx (@{$fake{$ir}}) { #print Dumper ${%$tx{'date'}}; #exit; addRow('transactions', { 'id' => $tx->{'id'}, 'stamp' => $tx->{'date'}, 'through_date' => $tx->{'thru'}, 'comment' => "Fake $ir" }, 1); foreach my $e (@{$tx->{'entry'}}) { my $ircrdr = ($ir eq 'invoice') ? 'Invoice' : 'Receipt'; my $dr = ($ir eq 'invoice') ? 'A/R' : 'Cash'; my $cr = ($ir eq 'invoice') ? $e->{'account_name'} : 'A/R'; my $crdr; my $monetary_source_id; if (defined $e->{'type'}) { addRow('monetary_sources', { 'monetary_type_id' => $e->{'type'}, 'name' => "Money of type " . $e->{'type'}, 'comment' => "Fake Money For " . $e->{'id'} }); $monetary_source_id = $newdb{'tables'}{'monetary_sources'}{'autoid'}; } else { $monetary_source_id = $newdb{'ids'}{'monetary_source'}{'internal'}; } $crdr = $dr; if ($use_ir) { $crdr = $ircrdr; addRow('ledger_entries', { 'id' => $e->{'id'}+10000, 'monetary_source_id' => ($ir eq 'invoice' ? undef : $monetary_source_id), 'transaction_id' => $tx->{'id'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{$dr}{'ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'}, 'customer_id' => $fake{'customer_id'}, #'lease_id' => $fake{'lease_id'}, 'amount' => $e->{'amount'}, 'comment' => "Fake $ir entry" }, 1); } addRow('ledger_entries', { 'id' => $e->{'id'}, 'monetary_source_id' => ($ir eq 'invoice' ? $monetary_source_id : undef), 'transaction_id' => $tx->{'id'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{$cr}{'ledger_id'}, 'customer_id' => $fake{'customer_id'}, 'lease_id' => $fake{'lease_id'}, 'amount' => $e->{'amount'}, 'comment' => "Fake Entry" }, 1); if ($use_ir) { addRow('reconciliations', { 'debit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 10000 : 0), 'credit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 0 : 10000), 'amount' => $e->{'amount'}, }); } foreach my $t (@{$e->{'track'}}) { addRow('reconciliations', { 'debit_ledger_entry_id' => $t->{'debit'}, 'credit_ledger_entry_id' => $e->{'id'}, 'amount' => $t->{'amount'} }); } next unless $e->{'tax'}; $crdr = $dr; if ($use_ir) { $crdr = $ircrdr; addRow('ledger_entries', { 'id' => $e->{'id'}+10001, 'monetary_source_id' => ($ir eq 'invoice' ? undef : $monetary_source_id), 'transaction_id' => $tx->{'id'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{$dr}{'ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'}, 'customer_id' => $fake{'customer_id'}, #'lease_id' => $fake{'lease_id'}, 'amount' => $e->{'tax'}, 'comment' => "Fake Tax Invoice Entry" }, 1); } addRow('ledger_entries', { 'id' => $e->{'id'}+1, 'monetary_source_id' => ($ir eq 'invoice' ? $monetary_source_id : undef), 'transaction_id' => $tx->{'id'}, 'debit_ledger_id' => $newdb{'lookup'}{'account'}{$crdr}{'ledger_id'}, 'credit_ledger_id' => $newdb{'lookup'}{'account'}{'Tax'}{'ledger_id'}, 'customer_id' => $fake{'customer_id'}, 'lease_id' => $fake{'lease_id'}, 'amount' => $e->{'tax'}, 'comment' => "Fake Tax Entry" }, 1); if ($use_ir) { addRow('reconciliations', { 'debit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 10001 : 1), 'credit_ledger_entry_id' => $e->{'id'} + ($ir eq 'invoice' ? 1 : 10001), 'amount' => $e->{'tax'}, }); } } } } } #fakeTesting(); $Data::Dumper::Sortkeys = 1; print Dumper \%newdb; # exit; buildTables(); ###################################################################### ## Security Deposits $query = "SELECT L.LedgerID, L.UnitID, C.ChargeAmount FROM TenantLedger L INNER JOIN Charges C ON C.LedgerID = L.LedgerID WHERE L.UnitID <> 'POS\$' AND C.ChargeDescription = 'Security Deposit' ORDER BY UnitID"; foreach $row (@{query($sdbh, $query)}) { #my $uid = $newdb{'lookup'}{'unit'}{$row->{'UnitID'}}; $query = "UPDATE pmgr_leases SET deposit = $row->{'ChargeAmount'} WHERE `number` = '$row->{'LedgerID'}'"; query($db_handle, $query, $row); } ###################################################################### ## Ledger Assignments # $query = # "UPDATE pmgr_accounts A, pmgr_ledgers L SET A.ledger_id = L.id" . # " WHERE A.id = L.account_id"; # query($db_handle, $query); ###################################################################### ## Unit Lease Assignments $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);