use FS::Cursor;
use FS::contact_phone;
use FS::contact_email;
+use FS::contact::Import;
use FS::queue;
use FS::phone_type; #for cgi_contact_fields
use FS::cust_contact;
Adds this record to the database. If there is an error, returns the error,
otherwise returns false.
-If the object has an C<emailaddress> field, L<FS::contact_email> records will
-be created for each (comma-separated) email address in that field. If any of
-these coincide with an existing email address, this contact will be merged with
-the contact with that address.
+If the object has an C<emailaddress> field, L<FS::contact_email> records
+will be created for each (comma-separated) email address in that field. If
+any of these coincide with an existing email address, this contact will be
+merged with the contact with that address.
Then, if the object has any fields named C<phonetypenumN> an
L<FS::contact_phone> record will be created for each of them. Those fields
$self->custnum('');
my %link_hash = ();
- for (qw( classnum comment selfservice_access invoice_dest )) {
+ for (qw( classnum comment selfservice_access invoice_dest message_dest)) {
$link_hash{$_} = $self->get($_);
$self->$_('');
}
- #look for an existing contact with this email address
+
+ ## check for an existing contact with this email address other than current customer
+ ## if found, just add that contact to cust_contact with link_hash credentials
+ ## as email can not be tied to two contacts.
+ my $no_new_contact;
my $existing_contact = '';
+ my @contact_emails = ();
+ my %contact_nums = ();
+
if ( $self->get('emailaddress') =~ /\S/ ) {
-
- my %existing_contact = ();
foreach my $email ( split(/\s*,\s*/, $self->get('emailaddress') ) ) {
- my $contact_email = qsearchs('contact_email', { emailaddress=>$email } )
- or next;
+ my $contact_email = qsearchs('contact_email', { emailaddress=>$email } );
+ unless ($contact_email) { push @contact_emails, $email; next; }
my $contact = $contact_email->contact;
- $existing_contact{ $contact->contactnum } = $contact;
+ if ($contact->contactnum eq $self->contactnum) {
+ push @contact_emails, $email;
+ }
+ else {
+ $contact_nums{$contact->contactnum} = '1';
+ }
}
- if ( scalar( keys %existing_contact ) > 1 ) {
- $dbh->rollback if $oldAutoCommit;
- return 'Multiple email addresses specified '.
- ' that already belong to separate contacts';
- } elsif ( scalar( keys %existing_contact ) ) {
- ($existing_contact) = values %existing_contact;
- }
+ my $emails = join(' , ', @contact_emails);
+ $self->emailaddress($emails);
- }
-
- my $error;
- if ( $existing_contact ) {
-
- $self->$_($existing_contact->$_())
- for qw( contactnum _password _password_encoding );
- $error = $self->SUPER::replace($existing_contact);
-
- } else {
-
- $error = $self->SUPER::insert;
+ $no_new_contact = '1' unless $self->emailaddress;
}
- $error ||= $self->insert_password_history;
+ my $error;
+ $error = $self->SUPER::insert unless $no_new_contact;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
}
+ $contact_nums{$self->contactnum} = '1' if $self->contactnum;
+
my $cust_contact = '';
+ # if $self->custnum was set, then the customer-specific properties
+ # (custnum, classnum, invoice_dest, selfservice_access, comment) are in
+ # pseudo-fields, and are now in %link_hash. otherwise, ignore all those
+ # fields.
if ( $custnum ) {
- my %hash = ( 'contactnum' => $self->contactnum,
- 'custnum' => $custnum,
- );
- $cust_contact = qsearchs('cust_contact', \%hash )
- || new FS::cust_contact { %hash, %link_hash };
- my $error = $cust_contact->custcontactnum ? $cust_contact->replace
+ foreach my $contactnum (keys %contact_nums) {
+ my %hash = ( 'contactnum' => $contactnum,
+ 'custnum' => $custnum,
+ );
+ $cust_contact = qsearchs('cust_contact', \%hash )
+ || new FS::cust_contact { %hash, %link_hash };
+ my $error = $cust_contact->custcontactnum ? $cust_contact->replace
: $cust_contact->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
}
- if ( $prospectnum ) {
+ if ( $prospectnum && !$no_new_contact) {
my %hash = ( 'contactnum' => $self->contactnum,
'prospectnum' => $prospectnum,
);
}
}
+ unless ($no_new_contact) {
foreach my $pf ( grep { /^phonetypenum(\d+)$/ && $self->get($_) =~ /\S/ }
keys %{ $self->hashref } ) {
$pf =~ /^phonetypenum(\d+)$/ or die "wtf (daily, the)";
return $error;
}
}
+ }
if ( $self->get('emailaddress') =~ /\S/ ) {
}
}
+ if ( $self->get('password') ) {
+ my $error = $self->is_password_allowed($self->get('password'))
+ || $self->change_password($self->get('password'));
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
}
}
+ # if $self->custnum was set, then we're removing the contact from this
+ # customer.
if ( $self->custnum ) {
my $cust_contact = qsearchs('cust_contact', {
'contactnum' => $self->contactnum,
my $prospectnum = $self->prospectnum;
$self->prospectnum('');
my $custnum = $self->custnum;
- $self->custnum('');
+ $self->custnum(''); $old->custnum(''); # remove because now stored cust_contact
my %link_hash = ();
- for (qw( classnum comment selfservice_access invoice_dest )) {
+ for (qw( classnum comment selfservice_access invoice_dest message_dest )) {
$link_hash{$_} = $self->get($_);
+ $old->$_(''); ##remove values from old record, causes problem with history
$self->$_('');
}
+ ## check for an existing contact with this email address other than current customer
+ ## if found, just add that contact to cust_contact with link_hash credentials
+ ## as email can not be tied to two contacts.
+ my @contact_emails = ();
+ my %contact_nums = ();
+ $contact_nums{$self->contactnum} = '1' if $self->contactnum;
+ if ( $self->get('emailaddress') =~ /\S/ ) {
+
+ foreach my $email ( split(/\s*,\s*/, $self->get('emailaddress') ) ) {
+
+ my $contact_email = qsearchs('contact_email', { emailaddress=>$email } );
+ unless ($contact_email) { push @contact_emails, $email; next; }
+
+ my $contact = $contact_email->contact;
+ if ($contact->contactnum eq $self->contactnum) {
+ push @contact_emails, $email;
+ }
+ else {
+ $contact_nums{$contact->contactnum} = '1';
+ }
+
+ }
+
+ ## were all emails duplicates? if so reset original emails
+ if (scalar @contact_emails < 1 && scalar (keys %contact_nums) > 1) {
+ foreach (qsearch('contact_email', {'contactnum' => $self->contactnum})) {
+ push @contact_emails, $_->emailaddress;
+ }
+ }
+
+ my $emails = join(' , ', @contact_emails);
+ $self->emailaddress($emails);
+
+ }
+
my $error = $self->SUPER::replace($old);
if ( $old->_password ne $self->_password ) {
$error ||= $self->insert_password_history;
}
my $cust_contact = '';
+ # if $self->custnum was set, then the customer-specific properties
+ # (custnum, classnum, invoice_dest, selfservice_access, comment) are in
+ # pseudo-fields, and are now in %link_hash. otherwise, ignore all those
+ # fields.
if ( $custnum ) {
- my %hash = ( 'contactnum' => $self->contactnum,
- 'custnum' => $custnum,
- );
- my $error;
- if ( $cust_contact = qsearchs('cust_contact', \%hash ) ) {
- $cust_contact->$_($link_hash{$_}) for keys %link_hash;
- $error = $cust_contact->replace;
- } else {
- $cust_contact = new FS::cust_contact { %hash, %link_hash };
- $error = $cust_contact->insert;
- }
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+
+ foreach my $contactnum (keys %contact_nums) {
+
+ my %hash = ( 'contactnum' => $contactnum,
+ 'custnum' => $custnum,
+ );
+ my $error;
+ if ( $cust_contact = qsearchs('cust_contact', \%hash ) ) {
+ $cust_contact->$_($link_hash{$_}) for keys %link_hash;
+ $error = $cust_contact->replace;
+ } else {
+ $cust_contact = new FS::cust_contact { %hash, %link_hash };
+ $error = $cust_contact->insert;
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
}
if ( defined($self->hashref->{'emailaddress'}) ) {
- #ineffecient but whatever, how many email addresses can there be?
-
+ my %contact_emails = ();
foreach my $contact_email ( $self->contact_email ) {
- my $error = $contact_email->delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
+ $contact_emails{$contact_email->emailaddress} = '1';
}
foreach my $email ( split(/\s*,\s*/, $self->get('emailaddress') ) ) {
- my $contact_email = new FS::contact_email {
- 'contactnum' => $self->contactnum,
- 'emailaddress' => $email,
- };
- $error = $contact_email->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+ unless ($contact_emails{$email}) {
+ my $contact_email = new FS::contact_email {
+ 'contactnum' => $self->contactnum,
+ 'emailaddress' => $email,
+ };
+ $error = $contact_email->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
+ else { delete($contact_emails{$email}); }
}
+ foreach my $contact_email ( $self->contact_email ) {
+ if ($contact_emails{$contact_email->emailaddress}) {
+ my $error = $contact_email->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
}
unless ( $skip_fuzzyfiles ) { #unless ( $import || $skip_fuzzyfiles ) {
}
}
+ if ( $self->get('password') ) {
+ my $error = $self->is_password_allowed($self->get('password'))
+ || $self->change_password($self->get('password'));
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
sub check {
my $self = shift;
- if ( $self->selfservice_access eq 'R' ) {
+ if ( $self->selfservice_access eq 'R' || $self->selfservice_access eq 'P' ) {
$self->selfservice_access('Y');
$self->_resend('Y');
}
=item by_selfservice_email EMAILADDRESS
-Alternate search constructor (class method). Given an email address,
-returns the contact for that address, or the empty string if no contact
-has that email address.
+Alternate search constructor (class method). Given an email address, returns
+the contact for that address. If that contact doesn't have selfservice access,
+or there isn't one, returns the empty string.
=cut
sub by_selfservice_email {
- my($class, $email) = @_;
+ my($class, $email, $case_insensitive) = @_;
+
+ my $email_search = "emailaddress = '".$email."'";
+ $email_search = "LOWER(emailaddress) = LOWER('".$email."')" if $case_insensitive;
+
+ my $contact_email = qsearchs({
+ 'table' => 'contact_email',
+ 'addl_from' => ' LEFT JOIN contact USING ( contactnum ) ',
+ 'extra_sql' => "
+ WHERE $email_search
+ AND ( contact.disabled IS NULL )
+ AND EXISTS ( SELECT 1 FROM cust_contact
+ WHERE contact.contactnum = cust_contact.contactnum
+ AND cust_contact.selfservice_access = 'Y'
+ )
+ ",
+ }) or return '';
+
+ $contact_email->contact;
+
+}
+
+=item by_selfservice_email_custnum EMAILADDRESS, CUSTNUM
+
+Alternate search constructor (class method). Given an email address and custnum, returns
+the contact for that address and custnum. If that contact doesn't have selfservice access,
+or there isn't one, returns the empty string.
+
+=cut
+
+sub by_selfservice_email_custnum {
+ my($class, $email, $custnum) = @_;
my $contact_email = qsearchs({
'table' => 'contact_email',
'addl_from' => ' LEFT JOIN contact USING ( contactnum ) ',
'hashref' => { 'emailaddress' => $email, },
- 'extra_sql' => " AND ( disabled IS NULL OR disabled = '' )",
+ 'extra_sql' => "
+ AND ( contact.disabled IS NULL )
+ AND EXISTS ( SELECT 1 FROM cust_contact
+ WHERE contact.contactnum = cust_contact.contactnum
+ AND cust_contact.selfservice_access = 'Y'
+ AND cust_contact.custnum = $custnum
+ )
+ ",
}) or return '';
$contact_email->contact;
$hash eq $check_hash;
- } else {
+ } else {
return 0 if $self->_password eq '';
'svcnum' => $opt{'svcnum'},
};
- my $timeout = '24 hours'; #?
+
+ my $conf = new FS::Conf;
+ my $timeout =
+ ($conf->config('selfservice-password_reset_hours') || 24 ). ' hours';
my $reset_session_id;
do {
#email it
- my $conf = new FS::Conf;
-
my $cust_main = '';
my @cust_contact = grep $_->selfservice_access, $self->cust_contact;
$cust_main = $cust_contact[0]->cust_main if scalar(@cust_contact) == 1;
my $agentnum = $cust_main ? $cust_main->agentnum : '';
my $msgnum = $conf->config('selfservice-password_reset_msgnum', $agentnum);
#die "selfservice-password_reset_msgnum unset" unless $msgnum;
- return { 'error' => "selfservice-password_reset_msgnum unset" } unless $msgnum;
+ return "selfservice-password_reset_msgnum unset" unless $msgnum;
my $msg_template = qsearchs('msg_template', { msgnum => $msgnum } );
- return { 'error' => "selfservice-password_reset_msgnum cannot be loaded" } unless $msg_template;
+ return "selfservice-password_reset_msgnum cannot be loaded" unless $msg_template;
my %msg_template = (
'to' => join(',', map $_->emailaddress, @contact_email ),
'cust_main' => $cust_main,
my $cust_msg = $msg_template->prepare( %msg_template );
my $error = $cust_msg->insert;
- return { 'error' => $error } if $error;
+ return $error if $error;
my $queue = new FS::queue {
'job' => 'FS::cust_msg::process_send',
'custnum' => $cust_main ? $cust_main->custnum : '',
my @contact_fields = qw(
classnum first last title comment emailaddress selfservice_access
- invoice_dest
+ invoice_dest message_dest password
);
push @contact_fields, 'phonetypenum'. $_->phonetypenum
sub _upgrade_data { #class method
my ($class, %opts) = @_;
+ # before anything else, migrate contact.custnum to cust_contact records
+ unless ( FS::upgrade_journal->is_done('contact_invoice_dest') ) {
+
+ local($skip_fuzzyfiles) = 1;
+
+ foreach my $contact (qsearch('contact', {})) {
+ my $error = $contact->replace;
+ die $error if $error;
+ }
+
+ FS::upgrade_journal->set_done('contact_invoice_dest');
+ }
+
+
# always migrate cust_main_invoice records over
local $FS::cust_main::import = 1; # override require_phone and such
my $search = FS::Cursor->new('cust_main_invoice', {});
+ my %custnum_dest;
while (my $cust_main_invoice = $search->fetch) {
my $custnum = $cust_main_invoice->custnum;
my $dest = $cust_main_invoice->dest;
if !$svc_acct;
$dest = $svc_acct->email;
}
+ push @{ $custnum_dest{$custnum} ||= [] }, $dest;
- my $error = $cust_main->replace( invoicing_list => [ $dest ] );
-
+ my $error = $cust_main_invoice->delete;
if ( $error ) {
- die "custnum $custnum, invoice destination $dest, creating contact: $error\n";
+ die "custnum $custnum, cleaning up cust_main_invoice: $error\n";
}
+ }
- $error = $cust_main_invoice->delete;
- die "custnum $custnum, cleaning up cust_main_invoice: $error\n" if $error;
-
- } # while $search->fetch
-
- unless ( FS::upgrade_journal->is_done('contact_invoice_dest') ) {
-
- foreach my $contact (qsearch('contact', {})) {
- my $error = $contact->replace;
- die $error if $error;
+ foreach my $custnum (keys %custnum_dest) {
+ my $dests = $custnum_dest{$custnum};
+ my $cust_main = FS::cust_main->by_key($custnum);
+ my $error = $cust_main->replace( invoicing_list => $dests );
+ if ( $error ) {
+ die "custnum $custnum, creating contact: $error\n";
}
-
- FS::upgrade_journal->set_done('contact_invoice_dest');
}
}
=cut
1;
-