Big Postal Address Change — First steps toward new fields.

These are various changes to support the changes to the postal
address table to include split-out fields for parts of the address
instead of just the formatted_address field.  The changes herein are
not comprehensive to solve all the problems and issues associated
with this update, but they are enough changes to get most daily
operations with the database more-or-less working.
This commit is contained in:
Bradley M. Kuhn 2022-12-16 11:36:15 -08:00
parent aab0b132b2
commit 8f54ebb54c
3 changed files with 142 additions and 153 deletions

View file

@ -403,6 +403,8 @@ returned; it means the preferred email address wasn't selected for some reason.
sub setPreferredPostalAddress($$$) {
my($self, $donorId, $postalAddress) = @_;
die "Postal address stuff not fixed yet";
die "setPreferredPostalAddress: invalid supporter id, $donorId" unless $self->_verifyId($donorId);
die "setPreferredPostalAddress: email address not defined" unless defined $postalAddress;
@ -495,15 +497,9 @@ Arguments:
Valid supporter id number currently in the database. die() will occur if
the id number is not in the database already as a supporter id.
=item $postalAddress
Scalar string that contains an postalAddress. undef is returned if the
postal address is not already in the database for this supporter.
=back
Returns the postal_address_id of the preferred postal address. undef can be
Returns the of the preferred postal address. undef can be
returned; it means the preferred postal address wasn't selected for some reason.
=cut
@ -511,21 +507,22 @@ returned; it means the preferred postal address wasn't selected for some reason.
sub getPreferredPostalAddress($$) {
my($self, $donorId) = @_;
die "setPreferredPostalAddress: invalid supporter id, $donorId" unless $self->_verifyId($donorId);
die "getPreferredPostalAddress: invalid supporter id, $donorId" unless $self->_verifyId($donorId);
my $ems = $self->dbh()->selectall_hashref("SELECT formatted_address FROM postal_address em, donor_postal_address_mapping sem " .
"WHERE preferred AND sem.postal_address_id = em.id AND " .
"sem.donor_id = " . $self->dbh->quote($donorId, 'SQL_INTEGER'),
'formatted_address');
my $ems = $self->dbh()->selectall_hashref("SELECT pa.*, at.name as type, map.date_valid_from, map.date_valid_to " .
"FROM donor_postal_address_mapping map, address_type at, postal_address pa " .
"WHERE at.id = map.type_id AND pa.id = map.postal_address_id AND " .
"map.date_valid_to is NULL AND " .
"map.donor_id = " . $self->dbh->quote($donorId, 'SQL_INTEGER'),
'id');
my $rowCount = scalar keys %{$ems};
die "setPreferredPostalAddress: DATABASE INTEGRITY ERROR: more than one postal address is preferred for supporter, \"$donorId\""
die "getPreferredPostalAddress: DATABASE INTEGRITY ERROR: more than one postal address is preferred for supporter, \"$donorId\""
if $rowCount > 1;
if ($rowCount != 1) {
return undef;
} else {
my ($postalAddress) = keys %$ems;
return $postalAddress;
return $ems;
}
}
######################################################################
@ -716,11 +713,11 @@ Returns the id value of the postal_address table entry.
=cut
sub addPostalAddress($$$$;$) {
my($self, $id, $formattedPostalAddress, $addressType, $dateEncountered) = @_;
my($self, $donorId, $postalFields, $addressType, $dateEncountered) = @_;
die "addPostalAddress: invalid id, $id" unless $self->_verifyId($id);
die "addPostalAddress: the formatted postal address must be defined"
unless defined $formattedPostalAddress;
die "addPostalAddress: invalid id, $donorId" unless $self->_verifyId($donorId);
die "addPostalAddress: the postalFields must be provided and must be a hash ref"
unless defined $postalFields and ref $postalFields eq 'HASH';
$self->_beginWork();
@ -734,30 +731,35 @@ sub addPostalAddress($$$$;$) {
$self->_rollback();
die $@ if $@;
}
my $insertStr = "INSERT INTO postal_address(formatted_address, type_id, date_encountered)" .
"VALUES( ?, ?, ";
my $sth;
if (defined $dateEncountered) {
$insertStr .= "?)";
$sth = $self->dbh->prepare($insertStr);
$sth->execute($formattedPostalAddress, $addressTypeId, $dateEncountered);
} else {
$insertStr .= "date('now'))";
$sth = $self->dbh->prepare($insertStr);
$sth->execute($formattedPostalAddress, $addressTypeId);
}
my $addressId = $self->dbh->last_insert_id("","","","");
my $insertPostalStr = <<INSERT_IT
INSERT INTO postal_address(first_name, middle_name, last_name, organization, address_1, address_2,
address_3, city, state_province_or_region, postcode, country)
VALUES( ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?)
INSERT_IT
;
my $sth = $self->dbh->prepare($insertPostalStr);
$dateEncountered = "date('now')" if not defined $dateEncountered;
$sth->execute($postalFields->{first_name}, $postalFields->{middle_name}, $postalFields->{last_name},
$postalFields->{organization}, $postalFields->{address_1},
$postalFields->{address_2}, $postalFields->{address_3},
$postalFields->{city}, $postalFields->{state_province_or_region},
$postalFields->{postcode}, $postalFields->{country});
my $newAddressId = $self->dbh->last_insert_id("","","","");
$sth->finish();
$sth = $self->dbh->prepare("UPDATE donor_postal_address_mapping SET date_valid_to = ? " .
"WHERE date_valid_to is NULL AND donor_id = " . $self->dbh->quote($donorId, 'SQL_INTEGER'));
$sth->execute($dateEncountered);
$sth->finish();
$sth = $self->dbh->prepare("INSERT INTO donor_postal_address_mapping" .
"(donor_id, postal_address_id) " .
"VALUES( ?, ?)");
$sth->execute($id, $addressId);
"(donor_id, postal_address_id, type_id, date_valid_from) " .
"VALUES( ?, ?, ?, ?)");
$sth->execute($donorId, $newAddressId, $addressTypeId, $dateEncountered);
$sth->finish();
$self->_commit();
return $addressId;
return $newAddressId;
}
######################################################################
@ -772,13 +774,12 @@ Arguments:
Valid supporter id number currently in the database. die() will occur if
the id number is not in the database already as a supporter id.
=item $formattedPostalAddress
Scalar string that contains a multi-line, fully formatted, postal address.
=back
Returns the id value of the postal_address table entry.
Returns a hash with the postal_address records for the donor with id, $id.
Also returns keys of type, and date_valid_from and date_valid_to from the
donor_postal_address_mapping table.
=cut
@ -787,12 +788,12 @@ sub getPostalAddresses($) {
die "addPostalAddress: invalid id, $id" unless $self->_verifyId($id);
my $val = $self->dbh()->selectall_hashref("SELECT pa.formatted_address, at.name, pa.date_encountered, pa.invalid, at.name, pa.id " .
my $val = $self->dbh()->selectall_hashref("SELECT pa.*, at.name as type, map.date_valid_from, map.date_valid_to " .
"FROM donor_postal_address_mapping map, address_type at, postal_address pa " .
"WHERE at.id = pa.type_id AND pa.id = map.postal_address_id AND " .
"WHERE at.id = map.type_id AND pa.id = map.postal_address_id AND " .
"map.donor_id = " . $self->dbh->quote($id, 'SQL_INTEGER'),
'formatted_address');
foreach my $key (keys %{$val}) { delete $val->{$key}{formatted_address}; }
'id');
foreach my $key (keys %{$val}) { delete $val->{$key}{id}; }
return %{$val};
}
@ -821,13 +822,15 @@ various concerns it might have about determining the best postal address.
sub getBestPostalAddress($) {
my($self, $id) = @_;
die "Postal address stuff not fixed yet";
die "getBestPostalAddress: invalid id, $id" unless $self->_verifyId($id);
my $pref = $self->getPreferredPostalAddress($id);
my $entries = $self->dbh()->selectall_hashref("SELECT pa.id, pa.formatted_address, at.name, pa.date_encountered " .
"FROM donor_postal_address_mapping map, address_type at, postal_address pa " .
"WHERE at.id = pa.type_id AND pa.id = map.postal_address_id AND " .
"WHERE at.id = map.type_id AND pa.id = map.postal_address_id AND " .
"(pa.invalid is NULL OR pa.invalid != 1) " .
"AND map.donor_id = " . $self->dbh->quote($id, 'SQL_INTEGER'),
'id');

View file

@ -68,11 +68,15 @@ foreach my $id (@supporterIds) {
print "\n NO POSTAL ADDRESSES.\n";
} else {
print "\n Postal Addresses:\n";
foreach my $address (keys %postalAddresses) {
print " PREFERRED FOLLOWS:\n" if (defined $preferredPostal) and $address eq $preferredPostal;
print " Encountered $postalAddresses{$address}{id} on $postalAddresses{$address}{date_encountered} via $postalAddresses{$address}{name}\n";
foreach my $addrLine (split("\n", $address)) {
print " $addrLine\n";
foreach my $addrID (sort
{ $postalAddresses{$a}{date_valid_from} cmp $postalAddresses{$b}{date_valid_from} or
$postalAddresses{$a}{date_valid_to} cmp $postalAddresses{$b}{date_valid_to};
} keys %postalAddresses) {
print " Address $addrID, found in $postalAddresses{$addrID}{type}, valid from $postalAddresses{$addrID}{date_valid_from} until ",
(not defined $postalAddresses{$addrID}{date_valid_to}) ? "RIGHT NOW " : $postalAddresses{$addrID}{date_valid_to}, ":\n";
foreach my $key (qw/first_name middle_name last_name organization address_1 address_2 address_3 city
state_province_or_region postcode country/) {
print " $key: $postalAddresses{$addrID}{$key}\n" if (defined $postalAddresses{$addrID}{$key});
}
}
}
@ -106,6 +110,6 @@ print "No entries found\n" unless $found;
###############################################################################
#
# Local variables:
# compile-command: "perl -c send-mass-email.plx"
# compile-command: "perl -I../Supporters/lib -c find-supporter.plx"
# End:

View file

@ -109,9 +109,9 @@ CREATE TABLE "address_type" (
"name" varchar(50) NOT NULL UNIQUE
);
DROP TABLE IF EXISTS "postal_address";
DROP TABLE IF EXISTS "old_postal_address";
CREATE TABLE "postal_address" (
CREATE TABLE "old_postal_address" (
"id" integer NOT NULL PRIMARY KEY,
"formatted_address" varchar(5000),
"type_id" INTEGER NOT NULL,
@ -119,17 +119,48 @@ CREATE TABLE "postal_address" (
"invalid" bool
);
DROP TABLE IF EXISTS "old_donor_postal_address_mapping";
CREATE TABLE "old_donor_postal_address_mapping" (
"donor_id" integer NOT NULL,
"old_postal_address_id" integer NOT NULL,
"preferred" bool,
PRIMARY KEY(donor_id, postal_address_id)
);
CREATE UNIQUE INDEX old_donor_postal_address_mapping_single_prefferred_per_donor
ON old_donor_postal_address_mapping(donor_id, preferred);
DROP TABLE IF EXISTS "postal_address";
CREATE TABLE "postal_address" (
"id" integer NOT NULL PRIMARY KEY,
"first_name" varchar(64),
"middle_name" varchar(64),
"last_name" varchar(64),
"organization" varchar(64),
"address_1" varchar(128),
"address_2" varchar(128),
"address_3" varchar(128),
"city" varchar(64),
"state_province_or_region" varchar(64),
"postcode" varchar(64),
"country" varchar(64)
);
DROP TABLE IF EXISTS "donor_postal_address_mapping";
CREATE TABLE "donor_postal_address_mapping" (
"donor_id" integer NOT NULL,
"postal_address_id" integer NOT NULL,
"preferred" bool,
"type_id" INTEGER NOT NULL,
"date_valid_from" date NOT NULL,
"date_valid_to" date,
PRIMARY KEY(donor_id, postal_address_id)
);
CREATE UNIQUE INDEX donor_postal_address_mapping_single_prefferred_per_donor
ON donor_postal_address_mapping(donor_id, preferred);
CREATE UNIQUE INDEX donor_postal_address_mapping_single_open_ended_addr
ON donor_postal_address_mapping(donor_id, date_valid_to);
-- ####################################### VIEWS FOR EASIER EDITING ##########################
@ -174,125 +205,76 @@ SELECT CASE
END;
-- -------------- VIEW: UserOperation_add_postal_for_donor_with_none ---------------------
DROP VIEW IF EXISTS UserOperation_add_postal_for_donor_with_none;
CREATE VIEW UserOperation_add_postal_for_donor_with_none AS
SELECT ledger_entity_id as entity, map.preferred as preferred_override, pa.date_encountered as entered_date,
pa.formatted_address as formatted_postal_address, pa.id as address_id, dd.id as donor_id, pa.invalid as is_invalid
FROM donor dd
LEFT JOIN donor_postal_address_mapping map ON dd.id = map.donor_id
LEFT JOIN postal_address pa ON pa.id = map.postal_address_id
WHERE map.donor_id is NULL
AND dd.is_supporter = 1
AND pa.id is NULL
ORDER BY ledger_entity_id;
DROP TRIGGER IF EXISTS create_formatted_postal_address_for_donor_with_none;
CREATE TRIGGER create_formatted_postal_for_donor_with_none
INSTEAD OF UPDATE OF formatted_postal_address on UserOperation_add_postal_for_donor_with_none
BEGIN
INSERT INTO postal_address(formatted_address, type_id, date_encountered)
VALUES(NEW.formatted_postal_address, 21, date("now"));
-- WITH wt(new_postal_address_id) AS (SELECT last_insert_rowid() AS new_postal_address_id)
INSERT INTO donor_postal_address_mapping(donor_id, postal_address_id)
VALUES(OLD.donor_id, (SELECT last_insert_rowid() ));
-- VALUES(OLD.donor_id, (SELECT new_postal_address_id FROM wt) );
SELECT CASE
WHEN ( (SELECT formatted_address FROM donor_postal_address_mapping dp, postal_address pa
WHERE dp.donor_id = OLD.donor_id
AND pa.id = (SELECT last_insert_rowid() )
AND dp.postal_address_id = (SELECT last_insert_rowid() )
AND pa.formatted_address = NEW.formatted_postal_address
!= NEW.formatted_postal_address ) )
THEN RAISE(FAIL, "Error encountered while adding new postal address; please verify your changes worked!")
END;
END;
-- -------------- VIEW: UserOperation_update_postal_address ---------------------
DROP TRIGGER IF EXISTS update_postal_address_preferred_override;
DROP TRIGGER IF EXISTS update_postal_address_is_invalid;
DROP TRIGGER IF EXISTS update_formatted_postal_address;
DROP VIEW IF EXISTS UserOperation_postal_address_change;
CREATE VIEW UserOperation_postal_address_change AS
SELECT ledger_entity_id as entity, map.preferred as preferred_override, pa.date_encountered as entered_date,
pa.formatted_address as formatted_postal_address, pa.id as address_id, dd.id as donor_id, pa.invalid as is_invalid
FROM donor dd, donor_postal_address_mapping map, postal_address pa
WHERE dd.id = map.donor_id AND pa.id = map.postal_address_id
AND map.preferred is NOT NULL
SELECT dd.id as donor_id, ledger_entity_id as entity, map.date_valid_from as entered_date, map.date_valid_to as invalidated_date,
pa.first_name as first_n, pa.middle_name as middle_n, pa.last_name as last_n, pa.organization as org,
pa.address_1 as addr1, pa.address_2 as addr2, pa.address_3 as addr3, pa.city as city,
pa.state_province_or_region as state_or_region, pa.postcode as postcode, pa.country as country, pa.id AS postal_id
FROM donor dd
LEFT JOIN donor_postal_address_mapping map ON dd.id = map.donor_id
LEFT JOIN postal_address pa ON pa.id = map.postal_address_id
WHERE dd.is_supporter = 1
AND (map.date_valid_to is NULL OR map.date_valid_to = (SELECT max(date_valid_to) FROM donor_postal_address_mapping where donor_id = dd.id
AND donor_id NOT IN (SELECT donor_id FROM donor_postal_address_mapping WHERE date_valid_to IS NULL)))
ORDER BY ledger_entity_id;
UNION
SELECT ledger_entity_id as entity, map.preferred as preferred_override, pa.date_encountered as entered_date,
pa.formatted_address as formatted_postal_address, pa.id as address_id, dd.id as donor_id, pa.invalid as is_invalid
FROM donor dd, donor_postal_address_mapping map, postal_address pa
WHERE dd.id = map.donor_id AND pa.id = map.postal_address_id
AND map.preferred is NULL
AND pa.date_encountered = (SELECT max(sub_pa.date_encountered)
FROM donor_postal_address_mapping sub_map, postal_address sub_pa
WHERE sub_map.donor_id = dd.id AND sub_map.postal_address_id = sub_pa.id)
ORDER BY ledger_entity_id, pa.date_encountered;
DROP TRIGGER IF EXISTS update_postal_address_preferred_override;
CREATE TRIGGER update_postal_address_preferred_override
INSTEAD OF UPDATE OF preferred_override on UserOperation_postal_address_change
DROP TRIGGER IF EXISTS update_postal_address_from_add_UserOperation;
CREATE TRIGGER update_postal_address_from_add_UserOperation
INSTEAD OF INSERT ON UserOperation_postal_address_change
BEGIN
UPDATE donor_postal_address_mapping SET date_valid_to = date("now")
WHERE donor_id = NEW.donor_id AND date_valid_to IS NULL;
UPDATE donor_postal_address_mapping SET preferred = NEW.preferred_override
WHERE donor_id = OLD.donor_id and postal_address_id = OLD.address_id;
SELECT CASE
WHEN ( (SELECT preferred FROM donor_postal_address_mapping
WHERE donor_id = OLD.donor_id AND postal_address_Id = OLD.address_id)
!= NEW.preferred_override )
THEN RAISE(FAIL, "Error encountered while updating preferred setting; please verify your changes worked!")
END;
END;
DROP TRIGGER IF EXISTS update_postal_address_is_invalid;
CREATE TRIGGER update_postal_address_is_invalid
INSTEAD OF UPDATE OF is_invalid on UserOperation_postal_address_change
BEGIN
UPDATE postal_address SET invalid = NEW.is_invalid
WHERE id = OLD.address_id;
SELECT CASE
WHEN ( (SELECT invalid FROM postal_address
WHERE id = OLD.address_id)
!= NEW.is_invalid )
THEN RAISE(FAIL, "Error encountered while updating invalid setting; please verify your changes worked!")
END;
END;
DROP TRIGGER IF EXISTS update_formatted_postal_address;
CREATE TRIGGER update_formatted_postal_address
INSTEAD OF UPDATE OF formatted_postal_address on UserOperation_postal_address_change
BEGIN
INSERT INTO postal_address(formatted_address, type_id, date_encountered)
VALUES(NEW.formatted_postal_address, 21, date("now"));
INSERT INTO postal_address(first_name, middle_name, last_name, organization, address_1, address_2,
address_3, city, state_province_or_region, postcode, country)
VALUES(NEW.first_n, NEW.middle_n, NEW.last_n, NEW.org, NEW.addr1, NEW.addr2,
NEW.addr3, NEW.city, NEW.state_or_region, NEW.postcode, NEW.country);
-- WITH wt(new_postal_address_id) AS (SELECT last_insert_rowid() AS new_postal_address_id)
INSERT INTO donor_postal_address_mapping(donor_id, postal_address_id)
VALUES(OLD.donor_id, (SELECT last_insert_rowid() ));
INSERT INTO donor_postal_address_mapping(donor_id, postal_address_id, type_id, date_valid_from)
VALUES(NEW.donor_id, (SELECT last_insert_rowid()), 21, date("now"));
-- VALUES(OLD.donor_id, (SELECT new_postal_address_id FROM wt) );
SELECT CASE
WHEN ( (SELECT formatted_address FROM donor_postal_address_mapping dp, postal_address pa
WHERE dp.donor_id = OLD.donor_id
WHEN ( (SELECT address_1 FROM donor_postal_address_mapping dp, postal_address pa
WHERE dp.donor_id = NEW.donor_id
AND pa.id = (SELECT last_insert_rowid() )
AND dp.postal_address_id = (SELECT last_insert_rowid() )
AND pa.formatted_address = NEW.formatted_postal_address
!= NEW.formatted_postal_address ) )
AND pa.address_1 = NEW.addr1
!= NEW.addr1 ) )
THEN RAISE(FAIL, "Error encountered while adding new postal address; please verify your changes worked!")
END;
END;
DROP TRIGGER IF EXISTS invalidate_postal_address_from_UserOperation;
CREATE TRIGGER invalidate_postal_address_from_UserOperation
INSTEAD OF UPDATE OF invalidated_date
ON UserOperation_postal_address_change
BEGIN
SELECT CASE
WHEN ( OLD.postal_id is NULL )
THEN RAISE(FAIL, "Donor has no postal address yet; cannot invalidate non existent address.")
END;
UPDATE donor_postal_address_mapping SET date_valid_to = NEW.invalidated_date WHERE donor_id = OLD.donor_id AND postal_address_id = OLD.postal_id;
-- FIXME, WHY DOES THE BELOW NOT WORK IN sqlitebrowser
-- SELECT CASE
-- WHEN ( (SELECT date_valid_to FROM donor_postal_address_mapping
-- WHERE donor_id = OLD.donor_id
-- AND postal_address_id = OLD.postal_id
-- != NEW.invalidated_date ) )
-- THEN RAISE(FAIL, "Error encountered while invalidating postal address; please verify your changes worked!")
-- END;
END;
-- -------------- VIEW: UserOperation_fix_tshirt_size_request ---------------------
DROP TABLE IF EXISTS "zz__temp_request_configuration_change";