summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/ClientAPI/MyAccount.pm70
-rw-r--r--FS/FS/ClientAPI_XMLRPC.pm1
-rw-r--r--FS/FS/Conf.pm2
-rw-r--r--FS/FS/Daemon/Preforking.pm1
-rw-r--r--FS/FS/Mason.pm3
-rw-r--r--FS/FS/Record.pm1
-rw-r--r--FS/FS/Schema.pm59
-rw-r--r--FS/FS/Upgrade.pm3
-rw-r--r--FS/FS/contact.pm299
-rw-r--r--FS/FS/cust_contact.pm146
-rw-r--r--FS/FS/cust_main.pm67
-rw-r--r--FS/FS/msg_template.pm40
-rw-r--r--FS/FS/o2m_Common.pm7
-rw-r--r--FS/FS/part_event/Condition/cust_bill_has_service.pm4
-rw-r--r--FS/FS/part_event/Condition/has_cust_tag.pm1
-rw-r--r--FS/FS/part_export/amazon_ec2.pm35
-rw-r--r--FS/FS/part_export/cardfortress.pm2
-rw-r--r--FS/FS/phone_avail.pm4
-rw-r--r--FS/FS/prospect_contact.pm125
-rw-r--r--FS/FS/prospect_main.pm4
-rw-r--r--FS/MANIFEST4
-rw-r--r--FS/bin/freeside-cdrd8
-rw-r--r--FS/t/cust_contact.t5
-rw-r--r--FS/t/prospect_contact.t5
-rwxr-xr-xbin/cust_bill-credit_ship255
-rw-r--r--eg/table_template.pm2
-rwxr-xr-xfs_selfservice/DEPLOY3
-rw-r--r--fs_selfservice/FS-SelfService/SelfService.pm1
-rw-r--r--fs_selfservice/FS-SelfService/cgi/select_cust.html38
-rwxr-xr-xfs_selfservice/FS-SelfService/cgi/selfservice.cgi16
-rw-r--r--httemplate/browse/discount.html3
-rw-r--r--httemplate/docs/about.html2
-rw-r--r--httemplate/edit/cust_main-contacts.html1
-rwxr-xr-xhttemplate/edit/cust_main.cgi4
-rw-r--r--httemplate/edit/elements/edit.html4
-rw-r--r--httemplate/elements/contact.html19
-rw-r--r--httemplate/elements/popup_link.html7
-rw-r--r--httemplate/elements/tr-fixed.html6
-rw-r--r--httemplate/elements/tr-select-contact.html10
-rw-r--r--httemplate/elements/tr-select-cust_location.html2
-rw-r--r--httemplate/misc/email-quotation.html8
-rw-r--r--httemplate/search/contact.html59
-rw-r--r--httemplate/search/cust_msg.html4
-rw-r--r--httemplate/search/prospect_main.html4
-rw-r--r--httemplate/view/cust_main/contacts_new.html33
-rw-r--r--httemplate/view/prospect_main.html5
46 files changed, 982 insertions, 200 deletions
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm
index 8276d7e4b..86c7ac324 100644
--- a/FS/FS/ClientAPI/MyAccount.pm
+++ b/FS/FS/ClientAPI/MyAccount.pm
@@ -46,6 +46,7 @@ use FS::payby;
use FS::acct_rt_transaction;
use FS::msg_template;
use FS::contact;
+use FS::cust_contact;
$DEBUG = 1;
$me = '[FS::ClientAPI::MyAccount]';
@@ -82,7 +83,7 @@ sub skin_info {
#return { 'error' => $session } if $context eq 'error';
my $agentnum = '';
- if ( $context eq 'customer' ) {
+ if ( $context eq 'customer' && $custnum ) {
my $sth = dbh->prepare('SELECT agentnum FROM cust_main WHERE custnum = ?')
or die dbh->errstr;
@@ -237,7 +238,16 @@ sub login {
return { error => 'Incorrect contact password.' }
unless $contact->authenticate_password($p->{'password'});
- $session->{'custnum'} = $contact->custnum;
+ my @cust_contact = grep $_->selfservice_access, $contact->cust_contact;
+ if ( scalar(@cust_contact) == 1 ) {
+ $session->{'custnum'} = $cust_contact[0]->custnum;
+ } elsif ( scalar(@cust_contact) ) {
+ $session->{'customers'} = { map { $_->custnum => $_->cust_main->name }
+ @cust_contact
+ };
+ } else {
+ return { error => 'No customer self-service access for contact' }; #??
+ }
} else {
@@ -303,6 +313,7 @@ sub login {
return { 'error' => '',
'session_id' => $session_id,
+ %$session,
};
}
@@ -336,6 +347,23 @@ sub switch_acct {
}
+sub switch_cust {
+ my $p = shift;
+ my($context, $session, $custnum) = _custoragent_session_custnum($p);
+ return { 'error' => $session } if $context eq 'error';
+
+ $session->{'custnum'} = $p->{'custnum'}
+ if exists $session->{'customers'}{ $p->{'custnum'} };
+
+ my $conf = new FS::Conf;
+ my $timeout = $conf->config('selfservice-session_timeout') || '1 hour';
+ _cache->set( $p->{'session_id'}, $session, $timeout );
+
+ return { 'error' => '',
+ %{ customer_info( { session_id=>$p->{'session_id'} } ) },
+ };
+}
+
sub payment_gateway {
# internal use only
# takes a cust_main and a cust_payby entry, returns the payment_gateway
@@ -380,22 +408,23 @@ sub access_info {
my($context, $session, $custnum) = _custoragent_session_custnum($p);
return { 'error' => $session } if $context eq 'error';
- my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
- or return { 'error' => "unknown custnum $custnum" };
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
$info->{'hide_payment_fields'} = [
map {
- my $pg = payment_gateway($cust_main, $_);
+ my $pg = $cust_main && payment_gateway($cust_main, $_);
$pg && $pg->gateway_namespace eq 'Business::OnlineThirdPartyPayment';
} @{ $info->{cust_paybys} }
];
$info->{'self_suspend_reason'} =
- $conf->config('selfservice-self_suspend_reason', $cust_main->agentnum);
+ $conf->config('selfservice-self_suspend_reason',
+ $cust_main ? $cust_main->agentnum : ''
+ );
$info->{'edit_ticket_subject'} =
$conf->exists('ticket_system-selfservice_edit_subject') &&
- $cust_main->edit_subject;
+ $cust_main && $cust_main->edit_subject;
$info->{'timeout'} = $conf->config('selfservice-timeout') || 3600;
@@ -432,7 +461,7 @@ sub customer_info {
my $search = { 'custnum' => $custnum };
$search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
my $cust_main = qsearchs('cust_main', $search )
- or return { 'error' => "unknown custnum $custnum" };
+ or return { 'error' => "customer_info: unknown custnum $custnum" };
my $list_tickets = list_tickets($p);
$return{'tickets'} = $list_tickets->{'tickets'};
@@ -536,7 +565,7 @@ sub customer_info_short {
my $search = { 'custnum' => $custnum };
$search->{'agentnum'} = $session->{'agentnum'} if $context eq 'agent';
my $cust_main = qsearchs('cust_main', $search )
- or return { 'error' => "unknown custnum $custnum" };
+ or return { 'error' => "customer_info_short: unknown custnum $custnum" };
$return{display_custnum} = $cust_main->display_custnum;
@@ -2916,7 +2945,12 @@ sub myaccount_passwd {
#need to support the "ISP provides email that's used as a contact email" case
#as well as we can.
my $contact = FS::contact->by_selfservice_email($svc_acct->email);
- if ( $contact && $contact->custnum == $custnum ) {
+ if ( $contact && qsearchs('cust_contact', { contactnum=> $contact->contactnum,
+ custnum => $custnum,
+ selfservice_access => 'Y',
+ }
+ )
+ ) {
#svc_acct was successful but this one returns an error? "shouldn't happen"
$error ||= $contact->change_password($p->{'new_password'});
}
@@ -2993,7 +3027,10 @@ sub reset_passwd {
$contact = FS::contact->by_selfservice_email($p->{'email'});
- $cust_main = $contact->cust_main if $contact;
+ if ( $contact ) {
+ my @cust_contact = grep $_->selfservice_access, $contact->cust_contact;
+ $cust_main = $cust_contact[0]->cust_main if scalar(@cust_contact) == 1;
+ }
#also look for an svc_acct, otherwise it would be super confusing
@@ -3035,6 +3072,9 @@ sub reset_passwd {
}
+ return { %$info, 'error' => 'Multi-customer contacts incompatible with customer-based verification' }
+ if ! $cust_main && $verification ne 'email';
+
my %verify = (
'email' => sub { 1; },
'paymask' => sub {
@@ -3157,7 +3197,9 @@ sub check_reset_passwd {
my @contact_email = $contact->contact_email;
return { 'error' => 'No contact email' } unless @contact_email;
- $p->{'agentnum'} = $contact->cust_main->agentnum;
+ my @cust_contact = grep $_->selfservice_access, $contact->cust_contact;
+ $p->{'agentnum'} = $cust_contact[0]->cust_main->agentnum
+ if scalar(@cust_contact) == 1;
my $info = skin_info($p);
return { %$info,
@@ -3207,7 +3249,9 @@ sub process_reset_passwd {
$contact = qsearchs('contact', { 'contactnum' => $contactnum } )
or return { 'error' => "Contact not found" };
- $p->{'agentnum'} ||= $contact->cust_main->agentnum;
+ my @cust_contact = grep $_->selfservice_access, $contact->cust_contact;
+ $p->{'agentnum'} = $cust_contact[0]->cust_main->agentnum
+ if scalar(@cust_contact) == 1;
$info ||= skin_info($p);
}
diff --git a/FS/FS/ClientAPI_XMLRPC.pm b/FS/FS/ClientAPI_XMLRPC.pm
index 62f61d6e5..952b19940 100644
--- a/FS/FS/ClientAPI_XMLRPC.pm
+++ b/FS/FS/ClientAPI_XMLRPC.pm
@@ -102,6 +102,7 @@ sub ss2clientapi {
'login' => 'MyAccount/login',
'logout' => 'MyAccount/logout',
'switch_acct' => 'MyAccount/switch_acct',
+ 'switch_cust' => 'MyAccount/switch_cust',
'customer_info' => 'MyAccount/customer_info',
'customer_info_short' => 'MyAccount/customer_info_short',
'billing_history' => 'MyAccount/billing_history',
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 029f1a1bf..2b959e661 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -2996,7 +2996,7 @@ and customer address. Include units.',
'type' => 'select',
'select_hash' => [ '' => 'Password reset disabled',
'email' => 'Click on a link in email',
- 'paymask,amount,zip' => 'Click on a link in email, and also verify with credit card (or bank account) last 4 digits, payment amount and zip code',
+ 'paymask,amount,zip' => 'Click on a link in email, and also verify with credit card (or bank account) last 4 digits, payment amount and zip code. Note: Do not use if you have multi-customer contacts, as they will be unable to reset their passwords.',
],
},
diff --git a/FS/FS/Daemon/Preforking.pm b/FS/FS/Daemon/Preforking.pm
index 98b4fa68c..f3a39a6ed 100644
--- a/FS/FS/Daemon/Preforking.pm
+++ b/FS/FS/Daemon/Preforking.pm
@@ -96,6 +96,7 @@ sub daemon_run {
#parent doesn't need to hold a DB connection open
dbh->disconnect;
undef $FS::UID::dbh;
+ undef $RT::Handle;
server_spawn(MAX_PROCESSES);
POE::Kernel->run();
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index d3e45dfee..37e3ad243 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -396,6 +396,9 @@ if ( -e $addl_handler_use_file ) {
use FS::circuit_provider;
use FS::circuit_termination;
use FS::svc_circuit;
+ use FS::cust_credit_source_bill_pkg;
+ use FS::prospect_contact;
+ use FS::cust_contact;
# Sammath Naur
if ( $FS::Mason::addl_handler_use ) {
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index f8282c031..92fb89665 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -876,6 +876,7 @@ sub qsearchs { # $result_record = &FS::Record:qsearchs('table',\%hash);
my $table = $_[0];
my(@result) = qsearch(@_);
cluck "warning: Multiple records in scalar search ($table)"
+ #.join(' / ', map "$_=>".$_[1]->{$_}, keys %{ $_[1] } )
if scalar(@result) > 1;
#should warn more vehemently if the search was on a primary key?
scalar(@result) ? ($result[0]) : ();
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index d5ed1b718..133b6d81a 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1740,20 +1740,69 @@ sub tables_hashref {
'index' => [ ['disabled'] ],
},
+ 'cust_contact' => {
+ 'columns' => [
+ 'custcontactnum', 'serial', '', '', '', '',
+ 'custnum', 'int', '', '', '', '',
+ 'contactnum', 'int', '', '', '', '',
+ 'classnum', 'int', 'NULL', '', '', '',
+ 'comment', 'varchar', 'NULL', 255, '', '',
+ 'selfservice_access', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'custcontactnum',
+ 'unique' => [ [ 'custnum', 'contactnum' ], ],
+ 'index' => [ [ 'custnum' ], [ 'contactnum' ], ],
+ 'foreign_keys' => [
+ { columns => [ 'custnum' ],
+ table => 'cust_main',
+ },
+ { columns => [ 'contactnum' ],
+ table => 'contact',
+ },
+ { columns => [ 'classnum' ],
+ table => 'contact_class',
+ },
+ ],
+ },
+
+ 'prospect_contact' => {
+ 'columns' => [
+ 'prospectcontactnum', 'serial', '', '', '', '',
+ 'prospectnum', 'int', '', '', '', '',
+ 'contactnum', 'int', '', '', '', '',
+ 'classnum', 'int', 'NULL', '', '', '',
+ 'comment', 'varchar', 'NULL', 255, '', '',
+ ],
+ 'primary_key' => 'prospectcontactnum',
+ 'unique' => [ [ 'prospectnum', 'contactnum' ], ],
+ 'index' => [ [ 'prospectnum' ], [ 'contactnum' ], ],
+ 'foreign_keys' => [
+ { columns => [ 'prospectnum' ],
+ table => 'prospect_main',
+ },
+ { columns => [ 'contactnum' ],
+ table => 'contact',
+ },
+ { columns => [ 'classnum' ],
+ table => 'contact_class',
+ },
+ ],
+ },
+
'contact' => {
'columns' => [
'contactnum', 'serial', '', '', '', '',
- 'prospectnum', 'int', 'NULL', '', '', '',
- 'custnum', 'int', 'NULL', '', '', '',
+ 'prospectnum', 'int', 'NULL', '', '', '', #deprecated, now prospect_contact table
+ 'custnum', 'int', 'NULL', '', '', '', #deprecated, now cust_contact table
'locationnum', 'int', 'NULL', '', '', '', #not yet
- 'classnum', 'int', 'NULL', '', '', '',
+ 'classnum', 'int', 'NULL', '', '', '', #deprecated, now prospect_contact or cust_contact
# 'titlenum', 'int', 'NULL', '', '', '', #eg Mr. Mrs. Dr. Rev.
'last', 'varchar', '', $char_d, '', '',
# 'middle', 'varchar', 'NULL', $char_d, '', '',
'first', 'varchar', '', $char_d, '', '',
'title', 'varchar', 'NULL', $char_d, '', '', #eg Head Bottle Washer
- 'comment', 'varchar', 'NULL', 255, '', '',
- 'selfservice_access', 'char', 'NULL', 1, '', '',
+ 'comment', 'varchar', 'NULL', 255, '', '', #depredated, now prospect_contact or cust_contact
+ 'selfservice_access', 'char', 'NULL', 1, '', '', #deprecated, now cust_contact
'_password', 'varchar', 'NULL', $char_d, '', '',
'_password_encoding', 'varchar', 'NULL', $char_d, '', '',
'disabled', 'char', 'NULL', 1, '', '',
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index 4719caa22..d05b309c7 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -312,6 +312,9 @@ sub upgrade_data {
#cust_main (remove paycvv from history)
'cust_main' => [],
+ #contact -> cust_contact / prospect_contact
+ 'contact' => [],
+
#msgcat
'msgcat' => [],
diff --git a/FS/FS/contact.pm b/FS/FS/contact.pm
index 437fd1694..589fc7c19 100644
--- a/FS/FS/contact.pm
+++ b/FS/FS/contact.pm
@@ -3,12 +3,15 @@ use base qw( FS::Record );
use strict;
use vars qw( $skip_fuzzyfiles );
+use Carp;
use Scalar::Util qw( blessed );
use FS::Record qw( qsearch qsearchs dbh );
use FS::contact_phone;
use FS::contact_email;
use FS::queue;
use FS::phone_type; #for cgi_contact_fields
+use FS::cust_contact;
+use FS::prospect_contact;
$skip_fuzzyfiles = 0;
@@ -123,10 +126,88 @@ sub insert {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- my $error = $self->SUPER::insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+ #save off and blank values that move to cust_contact / prospect_contact now
+ my $prospectnum = $self->prospectnum;
+ $self->prospectnum('');
+ my $custnum = $self->custnum;
+ $self->custnum('');
+
+ my %link_hash = ();
+ for (qw( classnum comment selfservice_access )) {
+ $link_hash{$_} = $self->get($_);
+ $self->$_('');
+ }
+
+ #look for an existing contact with this email address
+ my $existing_contact = '';
+ 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 = $contact_email->contact;
+ $existing_contact{ $contact->contactnum } = $contact;
+
+ }
+
+ 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;
+ }
+
+ }
+
+ if ( $existing_contact ) {
+
+ $self->$_($existing_contact->$_())
+ for qw( contactnum _password _password_encoding );
+ $self->SUPER::replace($existing_contact);
+
+ } else {
+
+ my $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ }
+
+ my $cust_contact = '';
+ 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
+ : $cust_contact->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ if ( $prospectnum ) {
+ my %hash = ( 'contactnum' => $self->contactnum,
+ 'prospectnum' => $prospectnum,
+ );
+ my $prospect_contact = qsearchs('prospect_contact', \%hash )
+ || new FS::prospect_contact { %hash, %link_hash };
+ my $error =
+ $prospect_contact->prospectcontactnum ? $prospect_contact->replace
+ : $prospect_contact->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
foreach my $pf ( grep { /^phonetypenum(\d+)$/ && $self->get($_) =~ /\S/ }
@@ -134,12 +215,14 @@ sub insert {
$pf =~ /^phonetypenum(\d+)$/ or die "wtf (daily, the)";
my $phonetypenum = $1;
- my $contact_phone = new FS::contact_phone {
- 'contactnum' => $self->contactnum,
- 'phonetypenum' => $phonetypenum,
- _parse_phonestring( $self->get($pf) ),
- };
- $error = $contact_phone->insert;
+ my %hash = ( 'contactnum' => $self->contactnum,
+ 'phonetypenum' => $phonetypenum,
+ );
+ my $contact_phone =
+ qsearchs('contact_phone', \%hash)
+ || new FS::contact_phone { %hash, _parse_phonestring($self->get($pf)) };
+ my $error = $contact_phone->contactphonenum ? $contact_phone->replace
+ : $contact_phone->insert;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
@@ -149,17 +232,18 @@ sub insert {
if ( $self->get('emailaddress') =~ /\S/ ) {
foreach my $email ( split(/\s*,\s*/, $self->get('emailaddress') ) ) {
-
- my $contact_email = new FS::contact_email {
+ my %hash = (
'contactnum' => $self->contactnum,
'emailaddress' => $email,
- };
- $error = $contact_email->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+ );
+ unless ( qsearchs('contact_email', \%hash) ) {
+ my $contact_email = new FS::contact_email \%hash;
+ my $error = $contact_email->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
}
-
}
}
@@ -167,14 +251,17 @@ sub insert {
unless ( $skip_fuzzyfiles ) { #unless ( $import || $skip_fuzzyfiles ) {
#warn " queueing fuzzyfiles update\n"
# if $DEBUG > 1;
- $error = $self->queue_fuzzyfiles_update;
+ my $error = $self->queue_fuzzyfiles_update;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "updating fuzzy search cache: $error";
}
}
- if ( $self->selfservice_access ) {
+ if ( $link_hash{'selfservice_access'} eq 'R'
+ or ( $link_hash{'selfservice_access'} && $cust_contact )
+ )
+ {
my $error = $self->send_reset_email( queue=>1 );
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
@@ -208,6 +295,44 @@ sub delete {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ #got a prospetnum or custnum? delete the prospect_contact or cust_contact link
+
+ if ( $self->prospectnum ) {
+ my $prospect_contact = qsearchs('prospect_contact', {
+ 'contactnum' => $self->contactnum,
+ 'prospectnum' => $self->prospectnum,
+ });
+ my $error = $prospect_contact->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ if ( $self->custnum ) {
+ my $cust_contact = qsearchs('cust_contact', {
+ 'contactnum' => $self->contactnum,
+ 'custnum' => $self->custnum,
+ });
+ my $error = $cust_contact->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ # then, proceed with deletion only if the contact isn't attached to any other
+ # prospects or customers
+
+ #inefficient, but how many prospects/customers can a single contact be
+ # attached too? (and is removing them from one a common operation?)
+ if ( $self->prospect_contact || $self->cust_contact ) {
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return '';
+ }
+
+ #proceed with deletion
+
foreach my $cust_pkg ( $self->cust_pkg ) {
$cust_pkg->contactnum('');
my $error = $cust_pkg->replace;
@@ -262,12 +387,61 @@ sub replace {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ #save off and blank values that move to cust_contact / prospect_contact now
+ my $prospectnum = $self->prospectnum;
+ $self->prospectnum('');
+ my $custnum = $self->custnum;
+ $self->custnum('');
+
+ my %link_hash = ();
+ for (qw( classnum comment selfservice_access )) {
+ $link_hash{$_} = $self->get($_);
+ $self->$_('');
+ }
+
my $error = $self->SUPER::replace($old);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
}
+ my $cust_contact = '';
+ 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;
+ }
+ }
+
+ if ( $prospectnum ) {
+ my %hash = ( 'contactnum' => $self->contactnum,
+ 'prospectnum' => $prospectnum,
+ );
+ my $error;
+ if ( my $prospect_contact = qsearchs('prospect_contact', \%hash ) ) {
+ $prospect_contact->$_($link_hash{$_}) for keys %link_hash;
+ $error = $prospect_contact->replace;
+ } else {
+ my $prospect_contact = new FS::prospect_contact { %hash, %link_hash };
+ $error = $prospect_contact->insert;
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
foreach my $pf ( grep { /^phonetypenum(\d+)$/ }
keys %{ $self->hashref } ) {
$pf =~ /^phonetypenum(\d+)$/ or die "wtf (daily, the)";
@@ -278,7 +452,7 @@ sub replace {
);
my $contact_phone = qsearchs('contact_phone', \%cp);
- # if new value is empty, delete old entry
+ #if new value is empty, delete old entry
if (!$self->get($pf)) {
if ($contact_phone) {
$error = $contact_phone->delete;
@@ -342,11 +516,14 @@ sub replace {
}
}
- if ( ( $old->selfservice_access eq '' && $self->selfservice_access
- && ! $self->_password
- )
- || $self->_resend()
- )
+ if ( $cust_contact and (
+ ( $cust_contact->selfservice_access eq ''
+ && $link_hash{selfservice_access}
+ && ! length($self->_password)
+ )
+ || $cust_contact->_resend()
+ )
+ )
{
my $error = $self->send_reset_email( queue=>1 );
if ( $error ) {
@@ -463,7 +640,6 @@ sub check {
;
return $error if $error;
- return "No prospect or customer!" unless $self->prospectnum || $self->custnum;
return "Prospect and customer!" if $self->prospectnum && $self->custnum;
return "One of first name, last name, or title must have a value"
@@ -500,17 +676,35 @@ sub firstlast {
$self->first . ' ' . $self->last;
}
-=item contact_classname
-
-Returns the name of this contact's class (see L<FS::contact_class>).
-
-=cut
-
-sub contact_classname {
- my $self = shift;
- my $contact_class = $self->contact_class or return '';
- $contact_class->classname;
-}
+#=item contact_classname PROSPECT_OBJ | CUST_MAIN_OBJ
+#
+#Returns the name of this contact's class for the specified prospect or
+#customer (see L<FS::prospect_contact>, L<FS::cust_contact> and
+#L<FS::contact_class>).
+#
+#=cut
+#
+#sub contact_classname {
+# my( $self, $prospect_or_cust ) = @_;
+#
+# my $link = '';
+# if ( ref($prospect_or_cust) eq 'FS::prospect_main' ) {
+# $link = qsearchs('prospect_contact', {
+# 'contactnum' => $self->contactnum,
+# 'prospectnum' => $prospect_or_cust->prospectnum,
+# });
+# } elsif ( ref($prospect_or_cust) eq 'FS::cust_main' ) {
+# $link = qsearchs('cust_contact', {
+# 'contactnum' => $self->contactnum,
+# 'custnum' => $prospect_or_cust->custnum,
+# });
+# } else {
+# croak "$prospect_or_cust is not an FS::prospect_main or FS::cust_main object";
+# }
+#
+# my $contact_class = $link->contact_class or return '';
+# $contact_class->classname;
+#}
=item by_selfservice_email EMAILADDRESS
@@ -527,8 +721,7 @@ sub by_selfservice_email {
'table' => 'contact_email',
'addl_from' => ' LEFT JOIN contact USING ( contactnum ) ',
'hashref' => { 'emailaddress' => $email, },
- 'extra_sql' => " AND selfservice_access = 'Y' ".
- " AND ( disabled IS NULL OR disabled = '' )",
+ 'extra_sql' => " AND ( disabled IS NULL OR disabled = '' )",
}) or return '';
$contact_email->contact;
@@ -629,10 +822,12 @@ sub send_reset_email {
my $conf = new FS::Conf;
- my $cust_main = $self->cust_main
- or die "no customer"; #reset a password for a prospect contact? someday
+ 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 $msgnum = $conf->config('selfservice-password_reset_msgnum', $cust_main->agentnum);
+ 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;
my $msg_template = qsearchs('msg_template', { msgnum => $msgnum } );
@@ -647,7 +842,7 @@ sub send_reset_email {
my $queue = new FS::queue {
'job' => 'FS::Misc::process_send_email',
- 'custnum' => $cust_main->custnum,
+ 'custnum' => $cust_main ? $cust_main->custnum : '',
};
$queue->insert( $msg_template->prepare( %msg_template ) );
@@ -690,7 +885,21 @@ sub cgi_contact_fields {
}
-use FS::phone_type;
+use FS::upgrade_journal;
+sub _upgrade_data { #class method
+ my ($class, %opts) = @_;
+
+ unless ( FS::upgrade_journal->is_done('contact__DUPEMAIL') ) {
+
+ foreach my $contact (qsearch('contact', {})) {
+ my $error = $contact->replace;
+ die $error if $error;
+ }
+
+ FS::upgrade_journal->set_done('contact__DUPEMAIL');
+ }
+
+}
=back
diff --git a/FS/FS/cust_contact.pm b/FS/FS/cust_contact.pm
new file mode 100644
index 000000000..6f899d83f
--- /dev/null
+++ b/FS/FS/cust_contact.pm
@@ -0,0 +1,146 @@
+package FS::cust_contact;
+use base qw( FS::Record );
+
+use strict;
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::cust_contact - Object methods for cust_contact records
+
+=head1 SYNOPSIS
+
+ use FS::cust_contact;
+
+ $record = new FS::cust_contact \%hash;
+ $record = new FS::cust_contact { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_contact object represents a contact's attachment to a specific
+customer. FS::cust_contact inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item custcontactnum
+
+primary key
+
+=item custnum
+
+custnum
+
+=item contactnum
+
+contactnum
+
+=item classnum
+
+classnum
+
+=item comment
+
+comment
+
+=item selfservice_access
+
+empty or Y
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_contact'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ if ( $self->selfservice_access eq 'R' ) {
+ $self->selfservice_access('Y');
+ $self->_resend('Y');
+ }
+
+ my $error =
+ $self->ut_numbern('custcontactnum')
+ || $self->ut_number('custnum')
+ || $self->ut_number('contactnum')
+ || $self->ut_numbern('classnum')
+ || $self->ut_textn('comment')
+ || $self->ut_enum('selfservice_access', [ '', 'Y' ])
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=item contact_classname
+
+Returns the name of this contact's class (see L<FS::contact_class>).
+
+=cut
+
+sub contact_classname {
+ my $self = shift;
+ my $contact_class = $self->contact_class or return '';
+ $contact_class->classname;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::contact>, L<FS::cust_main>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index d6f1a3176..cd675f9d4 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -71,7 +71,7 @@ use FS::agent_payment_gateway;
use FS::banned_pay;
use FS::cust_main_note;
use FS::cust_attachment;
-use FS::contact;
+use FS::cust_contact;
use FS::Locales;
use FS::upgrade_journal;
use FS::sales;
@@ -529,11 +529,23 @@ sub insert {
return $error;
}
- my @contact = $prospect_main->contact;
+ foreach my $prospect_contact ( $prospect_main->prospect_contact ) {
+ my $cust_contact = new FS::cust_contact {
+ 'custnum' => $self->custnum,
+ map { $_ => $prospect_contact->$_() } qw( contactnum classnum comment )
+ };
+ my $error = $cust_contact->insert
+ || $prospect_contact->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
my @cust_location = $prospect_main->cust_location;
my @qual = $prospect_main->qual;
- foreach my $r ( @contact, @cust_location, @qual ) {
+ foreach my $r ( @cust_location, @qual ) {
$r->prospectnum('');
$r->custnum($self->custnum);
my $error = $r->replace;
@@ -1915,14 +1927,13 @@ sub cust_location {
=item cust_contact
-Returns all contacts (see L<FS::contact>) for this customer.
+Returns all contact associations (see L<FS::cust_contact>) for this customer.
=cut
-#already used :/ sub contact {
sub cust_contact {
my $self = shift;
- qsearch('contact', { 'custnum' => $self->custnum } );
+ qsearch('cust_contact', { 'custnum' => $self->custnum } );
}
=item cust_payby
@@ -3656,9 +3667,11 @@ sub service_contact {
my $classnum = $self->scalar_sql(
'SELECT classnum FROM contact_class WHERE classname = \'Service\''
) || 0; #if it's zero, qsearchs will return nothing
- $self->{service_contact} = qsearchs('contact', {
- 'classnum' => $classnum, 'custnum' => $self->custnum
- }) || undef;
+ my $cust_contact = qsearchs('cust_contact', {
+ 'classnum' => $classnum,
+ 'custnum' => $self->custnum,
+ });
+ $self->{service_contact} = $cust_contact->contact if $cust_contact;
}
$self->{service_contact};
}
@@ -4614,6 +4627,42 @@ sub _agent_plandata {
}
+sub process_o2m_qsearch {
+ my $self = shift;
+ my $table = shift;
+ return qsearch($table, @_) unless $table eq 'contact';
+
+ my $hashref = shift;
+ my %hash = %$hashref;
+ ( my $custnum = delete $hash{'custnum'} ) =~ /^(\d+)$/
+ or die 'guru meditation #4343';
+
+ qsearch({ 'table' => 'contact',
+ 'addl_from' => 'LEFT JOIN cust_contact USING ( contactnum )',
+ 'hashref' => \%hash,
+ 'extra_sql' => ( keys %hash ? ' AND ' : ' WHERE ' ).
+ " cust_contact.custnum = $custnum "
+ });
+}
+
+sub process_o2m_qsearchs {
+ my $self = shift;
+ my $table = shift;
+ return qsearchs($table, @_) unless $table eq 'contact';
+
+ my $hashref = shift;
+ my %hash = %$hashref;
+ ( my $custnum = delete $hash{'custnum'} ) =~ /^(\d+)$/
+ or die 'guru meditation #2121';
+
+ qsearchs({ 'table' => 'contact',
+ 'addl_from' => 'LEFT JOIN cust_contact USING ( contactnum )',
+ 'hashref' => \%hash,
+ 'extra_sql' => ( keys %hash ? ' AND ' : ' WHERE ' ).
+ " cust_contact.custnum = $custnum "
+ });
+}
+
=item queued_bill 'custnum' => CUSTNUM [ , OPTION => VALUE ... ]
Subroutine (not a method), designed to be called from the queue.
diff --git a/FS/FS/msg_template.pm b/FS/FS/msg_template.pm
index f45fb2aef..94d478f6f 100644
--- a/FS/FS/msg_template.pm
+++ b/FS/FS/msg_template.pm
@@ -278,16 +278,17 @@ A hash reference of additional substitutions
sub prepare {
my( $self, %opt ) = @_;
- my $cust_main = $opt{'cust_main'} or die 'cust_main required';
+ my $cust_main = $opt{'cust_main'}; # or die 'cust_main required';
my $object = $opt{'object'} or die 'object required';
# localization
- my $locale = $cust_main->locale || '';
+ my $locale = $cust_main && $cust_main->locale || '';
warn "no locale for cust#".$cust_main->custnum."; using default content\n"
- if $DEBUG and !$locale;
- my $content = $self->content($cust_main->locale);
- warn "preparing template '".$self->msgname."' to cust#".$cust_main->custnum."\n"
- if($DEBUG);
+ if $DEBUG and $cust_main && !$locale;
+ my $content = $self->content($locale);
+
+ warn "preparing template '".$self->msgname."\n"
+ if $DEBUG;
my $subs = $self->substitutions;
@@ -295,7 +296,8 @@ sub prepare {
# create substitution table
###
my %hash;
- my @objects = ($cust_main);
+ my @objects = ();
+ push @objects, $cust_main if $cust_main;
my @prefixes = ('');
my $svc;
if( ref $object ) {
@@ -385,20 +387,22 @@ sub prepare {
my @to;
if ( exists($opt{'to'}) ) {
@to = split(/\s*,\s*/, $opt{'to'});
- }
- else {
+ } elsif ( $cust_main ) {
@to = $cust_main->invoicing_list_emailonly;
+ } else {
+ die 'no To: address or cust_main object specified';
}
- # no warning when preparing with no destination
my $from_addr = $self->from_addr;
if ( !$from_addr ) {
+
+ my $agentnum = $cust_main ? $cust_main->agentnum : '';
+
if ( $opt{'from_config'} ) {
- $from_addr = scalar( $conf->config($opt{'from_config'},
- $cust_main->agentnum) );
+ $from_addr = $conf->config($opt{'from_config'}, $agentnum);
}
- $from_addr ||= $conf->invoice_from_full($cust_main->agentnum);
+ $from_addr ||= $conf->invoice_from_full($agentnum);
}
# my @cust_msg = ();
# if ( $conf->exists('log_sent_mail') and !$opt{'preview'} ) {
@@ -416,11 +420,11 @@ sub prepare {
->format( HTML::TreeBuilder->new_from_content($body) )
);
(
- 'custnum' => $cust_main->custnum,
- 'msgnum' => $self->msgnum,
- 'from' => $from_addr,
- 'to' => \@to,
- 'bcc' => $self->bcc_addr || undef,
+ 'custnum' => ( $cust_main ? $cust_main->custnum : ''),
+ 'msgnum' => $self->msgnum,
+ 'from' => $from_addr,
+ 'to' => \@to,
+ 'bcc' => $self->bcc_addr || undef,
'subject' => $subject,
'html_body' => $body,
'text_body' => $text_body
diff --git a/FS/FS/o2m_Common.pm b/FS/FS/o2m_Common.pm
index 0e03b52ee..4848649d3 100644
--- a/FS/FS/o2m_Common.pm
+++ b/FS/FS/o2m_Common.pm
@@ -87,7 +87,7 @@ sub process_o2m {
foreach my $del_obj (
grep { ! $edits{$_->$table_pkey()} }
- qsearch( $table, $hashref )
+ $self->process_o2m_qsearch( $table, $hashref )
) {
my $error = $del_obj->delete;
if ( $error ) {
@@ -97,7 +97,7 @@ sub process_o2m {
}
foreach my $pkey_value ( keys %edits ) {
- my $old_obj = qsearchs( $table, { %$hashref, $table_pkey => $pkey_value } ),
+ my $old_obj = $self->process_o2m_qsearchs( $table, { %$hashref, $table_pkey => $pkey_value } );
my $add_param = $edits{$pkey_value};
my %hash = ( $table_pkey => $pkey_value,
map { $_ => $opt{'params'}->{$add_param."_$_"} }
@@ -131,6 +131,9 @@ sub process_o2m {
'';
}
+sub process_o2m_qsearch { shift->qsearch( @_ ); }
+sub process_o2m_qsearchs { shift->qsearchs( @_ ); }
+
sub _load_table {
my( $self, $table ) = @_;
eval "use FS::$table";
diff --git a/FS/FS/part_event/Condition/cust_bill_has_service.pm b/FS/FS/part_event/Condition/cust_bill_has_service.pm
index 6e981ee03..898b08d10 100644
--- a/FS/FS/part_event/Condition/cust_bill_has_service.pm
+++ b/FS/FS/part_event/Condition/cust_bill_has_service.pm
@@ -44,13 +44,13 @@ sub condition_sql {
my $servicenums =
$class->condition_sql_option_option_integer('has_service');
- my $sql = qq| 0 < ( SELECT COUNT(cs.svcpart)
+ my $sql = " 0 < ( SELECT COUNT(cs.svcpart)
FROM cust_bill_pkg cbp, cust_svc cs
WHERE cbp.invnum = cust_bill.invnum
AND cs.pkgnum = cbp.pkgnum
AND cs.svcpart IN $servicenums
)
- |;
+ ";
return $sql;
}
diff --git a/FS/FS/part_event/Condition/has_cust_tag.pm b/FS/FS/part_event/Condition/has_cust_tag.pm
index cde933881..79bf2d303 100644
--- a/FS/FS/part_event/Condition/has_cust_tag.pm
+++ b/FS/FS/part_event/Condition/has_cust_tag.pm
@@ -16,7 +16,6 @@ sub eventtable_hashref {
};
}
-#something like this
sub option_fields {
(
'tagnum' => { 'label' => 'Customer tag',
diff --git a/FS/FS/part_export/amazon_ec2.pm b/FS/FS/part_export/amazon_ec2.pm
index 06e2c238e..c1082a8aa 100644
--- a/FS/FS/part_export/amazon_ec2.pm
+++ b/FS/FS/part_export/amazon_ec2.pm
@@ -8,10 +8,12 @@ use FS::Record qw( qsearchs );
use FS::svc_external;
tie my %options, 'Tie::IxHash',
- 'access_key' => { label => 'AWS access key', },
- 'secret_key' => { label => 'AWS secret key', },
- 'ami' => { label => 'AMI', 'default' => 'ami-ff46a796', },
- 'keyname' => { label => 'Keypair name', },
+ 'access_key' => { label => 'AWS access key', },
+ 'secret_key' => { label => 'AWS secret key', },
+ 'ami' => { label => 'AMI', 'default' => 'ami-ff46a796', },
+ 'keyname' => { label => 'Keypair name', },
+ 'region' => { label => 'Region', },
+ 'InstanceType' => { label => 'Instance Type', },
#option to turn off (or on) ip address allocation
;
@@ -38,6 +40,7 @@ sub _export_insert {
$svc_external->svcnum,
$self->option('ami'),
$self->option('keyname'),
+ $self->option('InstanceType'),
);
ref($err_or_queue) ? '' : $err_or_queue;
}
@@ -96,31 +99,35 @@ sub amazon_ec2_queue {
};
$queue->insert( $self->option('access_key'),
$self->option('secret_key'),
+ $self->option('region'),
@_
)
or $queue;
}
sub amazon_ec2_new {
- my( $access_key, $secret_key, @rest ) = @_;
+ my( $access_key, $secret_key, $region, @rest ) = @_;
eval 'use Net::Amazon::EC2;';
die $@ if $@;
my $ec2 = new Net::Amazon::EC2 'AWSAccessKeyId' => $access_key,
- 'SecretAccessKey' => $secret_key;
-
+ 'SecretAccessKey' => $secret_key,
+ 'region' => $region || 'us-east-1',
+ ;
( $ec2, @rest );
}
sub amazon_ec2_insert { #subroutine, not method
- my( $ec2, $svcnum, $ami, $keyname ) = amazon_ec2_new(@_);
-
- my $reservation_info = $ec2->run_instances( 'ImageId' => $ami,
- 'KeyName' => $keyname,
- 'MinCount' => 1,
- 'MaxCount' => 1,
- );
+ my( $ec2, $svcnum, $ami, $keyname, $InstanceType ) = amazon_ec2_new(@_);
+
+ my $reservation_info = $ec2->run_instances(
+ 'ImageId' => $ami,
+ 'KeyName' => $keyname,
+ 'InstanceType' => $InstanceType || 'm1.small',
+ 'MinCount' => 1,
+ 'MaxCount' => 1,
+ );
my $instance_id = $reservation_info->instances_set->[0]->instance_id;
diff --git a/FS/FS/part_export/cardfortress.pm b/FS/FS/part_export/cardfortress.pm
index 7ff728081..154f979b0 100644
--- a/FS/FS/part_export/cardfortress.pm
+++ b/FS/FS/part_export/cardfortress.pm
@@ -28,6 +28,7 @@ sub _export_insert {
my $ssh = Net::OpenSSH->new( $self->machine,
default_stdin_fh => $def_in );
+ #capture2 and return STDERR, its probably useful if there's a problem
my $private_key = $ssh->capture(
{ 'stdin_data' => $svc_acct->_password. "\n" },
'/usr/local/bin/merchant_create', map $svc_acct->$_, qw( username finger )
@@ -67,6 +68,7 @@ sub _export_delete {
my $ssh = Net::OpenSSH->new( $self->machine,
default_stdin_fh => $def_in );
+ #capture2 and return STDERR, its probably useful if there's a problem
my $unused_output = $ssh->capture(
'/usr/local/bin/merchant_disable', map $svc_acct->$_, qw( username )
);
diff --git a/FS/FS/phone_avail.pm b/FS/FS/phone_avail.pm
index 52bbdeb10..ae8526c3d 100644
--- a/FS/FS/phone_avail.pm
+++ b/FS/FS/phone_avail.pm
@@ -283,8 +283,8 @@ sub _upgrade_data {
my $sth = dbh->prepare(
'UPDATE phone_avail SET svcnum = NULL
WHERE svcnum IS NOT NULL
- AND 0 = ( SELECT COUNT(*) FROM svc_phone
- WHERE phone_avail.svcnum = svc_phone.svcnum )'
+ AND NOT EXISTS ( SELECT 1 FROM svc_phone
+ WHERE phone_avail.svcnum = svc_phone.svcnum )'
) or die dbh->errstr;
$sth->execute or die $sth->errstr;
diff --git a/FS/FS/prospect_contact.pm b/FS/FS/prospect_contact.pm
new file mode 100644
index 000000000..6626132dd
--- /dev/null
+++ b/FS/FS/prospect_contact.pm
@@ -0,0 +1,125 @@
+package FS::prospect_contact;
+use base qw( FS::Record );
+
+use strict;
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::prospect_contact - Object methods for prospect_contact records
+
+=head1 SYNOPSIS
+
+ use FS::prospect_contact;
+
+ $record = new FS::prospect_contact \%hash;
+ $record = new FS::prospect_contact { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::prospect_contact object represents a contact's attachment to a specific
+prospect. FS::prospect_contact inherits from FS::Record. The following fields
+are currently supported:
+
+=over 4
+
+=item prospectcontactnum
+
+primary key
+
+=item prospectnum
+
+prospectnum
+
+=item contactnum
+
+contactnum
+
+=item classnum
+
+classnum
+
+=item comment
+
+comment
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'prospect_contact'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('prospectcontactnum')
+ || $self->ut_number('prospectnum')
+ || $self->ut_number('contactnum')
+ || $self->ut_numbern('classnum')
+ || $self->ut_textn('comment')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::contact>, L<FS::prospect_main>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/prospect_main.pm b/FS/FS/prospect_main.pm
index b160343de..81f71a996 100644
--- a/FS/FS/prospect_main.pm
+++ b/FS/FS/prospect_main.pm
@@ -269,7 +269,7 @@ sub name {
my $self = shift;
return $self->company if $self->company;
- my $contact = ($self->contact)[0]; #first contact? good enough for now
+ my $contact = ($self->prospect_contact)[0]->contact; #first contact? good enough for now
return $contact->line if $contact;
'Prospect #'. $self->prospectnum;
@@ -314,7 +314,7 @@ sub convert_cust_main {
my @cust_location = $self->cust_location;
#the interface only allows one, so we're just gonna go with that for now
- my @contact = $self->contact;
+ my @contact = map $_->contact, $self->prospect_contact;
#XXX define one contact type as "billing", then we could pick just that one
my @invoicing_list = map $_->emailaddress, map $_->contact_email, @contact;
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 6e36c3344..e5e29b4c9 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -834,3 +834,7 @@ FS/svc_circuit.pm
t/svc_circuit.t
FS/cust_credit_source_bill_pkg.pm
t/cust_credit_source_bill_pkg.t
+FS/prospect_contact.pm
+t/prospect_contact.t
+FS/cust_contact.pm
+t/cust_contact.t
diff --git a/FS/bin/freeside-cdrd b/FS/bin/freeside-cdrd
index 45d58789d..a3c67f919 100644
--- a/FS/bin/freeside-cdrd
+++ b/FS/bin/freeside-cdrd
@@ -120,10 +120,10 @@ while (1) {
sub _shouldrun {
my $extra_sql =
- ' AND 0 < ( SELECT COUNT(*) FROM cust_pkg
- WHERE cust_pkg.pkgpart = part_pkg.pkgpart
- AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
- )
+ ' AND EXISTS ( SELECT 1 FROM cust_pkg
+ WHERE cust_pkg.pkgpart = part_pkg.pkgpart
+ AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ )
';
my @part_pkg =
diff --git a/FS/t/cust_contact.t b/FS/t/cust_contact.t
new file mode 100644
index 000000000..0e9ea7100
--- /dev/null
+++ b/FS/t/cust_contact.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_contact;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/prospect_contact.t b/FS/t/prospect_contact.t
new file mode 100644
index 000000000..dbb12e510
--- /dev/null
+++ b/FS/t/prospect_contact.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::prospect_contact;
+$loaded=1;
+print "ok 1\n";
diff --git a/bin/cust_bill-credit_ship2 b/bin/cust_bill-credit_ship2
index c4d5169c1..a9a899c38 100755
--- a/bin/cust_bill-credit_ship2
+++ b/bin/cust_bill-credit_ship2
@@ -193,16 +193,17 @@ foreach my $cust_bill ( @cust_bill ) {
my $cur_cr = 0;
$cur_cr += $_->amount foreach $cust_bill->cust_credited;
$cur_cr = '' if $cur_cr == 0;
+
+ next if $cur_cr > 0 && $opt_k;
+
if ( $opt_p ) {
#print $cust_bill->invnum. ','. $cust_bill->custnum. ",$tax,$credit,$cr_percent%\n";
+# print $cust_bill->invnum. ','. $cust_bill->custnum. ',"'.
+# $cust_bill->cust_main->name. '",'. "$tax,$credit,$cur_cr\n";
print $cust_bill->invnum. ','. $cust_bill->custnum. ',"'.
- $cust_bill->cust_main->name. '",'. "$tax,$credit,$cur_cr\n";
+ $cust_bill->cust_main->name. '",'. "$tax,$credit\n";
}
- next if $cur_cr > 0 && $opt_k;
-
-#COMMENTING OUT ALL DANGEROUS STUFF
-#
# if ( $opt_m && ! $opt_r ) {
#
# my $msg_template = qsearchs('msg_template', { 'msgnum' => $opt_m } )
@@ -216,28 +217,28 @@ foreach my $cust_bill ( @cust_bill ) {
# " custnum ". $cust_bill->custnum. ": $error\n";
# }
# }
-#
-# if ( $opt_c ) {
-# my $cust_credit = new FS::cust_credit {
-# 'custnum' => $cust_main->custnum,
-# 'amount' => $credit,
-# 'reasonnum' => $opt_c,
-# };
-# my $error = $cust_credit->insert;
-# if ( $error ) {
-# warn "error inserting credit: $error\n";
-# }
-# my $cust_credit_bill = new FS::cust_credit_bill {
-# 'crednum' => $cust_credit->crednum,
-# 'invnum' => $cust_bill->invnum,
-# 'amount' => $credit,
-# };
-# my $aerror = $cust_credit_bill->insert;
-# if ( $aerror ) {
-# warn "error applying credit to invnum ". $cust_bill->invnum. ": $aerror\n";
-# }
-# }
-#
+
+ if ( $opt_c ) {
+ my $cust_credit = new FS::cust_credit {
+ 'custnum' => $cust_main->custnum,
+ 'amount' => $credit,
+ 'reasonnum' => $opt_c,
+ };
+ my $error = $cust_credit->insert;
+ if ( $error ) {
+ warn "error inserting credit: $error\n";
+ }
+ my $cust_credit_bill = new FS::cust_credit_bill {
+ 'crednum' => $cust_credit->crednum,
+ 'invnum' => $cust_bill->invnum,
+ 'amount' => $credit,
+ };
+ my $aerror = $cust_credit_bill->insert;
+ if ( $aerror ) {
+ warn "error applying credit to invnum ". $cust_bill->invnum. ": $aerror\n";
+ }
+ }
+
# if ( $opt_e && ! $opt_r ) {
# eval { $cust_bill->email };
# if ( $@ ) {
diff --git a/eg/table_template.pm b/eg/table_template.pm
index 0a6f85156..686bef6a8 100644
--- a/eg/table_template.pm
+++ b/eg/table_template.pm
@@ -1,7 +1,7 @@
package FS::table_name;
+use base qw( FS::Record );
use strict;
-use base qw( FS::Record );
use FS::Record qw( qsearch qsearchs );
=head1 NAME
diff --git a/fs_selfservice/DEPLOY b/fs_selfservice/DEPLOY
index bedb5eca9..4e0f495f9 100755
--- a/fs_selfservice/DEPLOY
+++ b/fs_selfservice/DEPLOY
@@ -11,7 +11,8 @@ perl Makefile.PL && make && make install
cd ..
#( cd ..; make deploy; cd fs_selfservice )
-( cd ..; make clean; make configure-rt; make install-perl-modules; /etc/init.d/freeside restart; cd fs_selfservice )
+#( cd ..; make clean; make configure-rt; make install-perl-modules; /etc/init.d/freeside restart; cd fs_selfservice )
+( cd ..; make clean; make configure-rt; make install-perl-modules; make deploy; cd fs_selfservice )
#cp /home/ivan/freeside/fs_selfservice/FS-SelfService/cgi/* /var/www/MyAccount
#chown freeside /var/www/MyAccount/*.cgi
diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm
index f54a1571e..3aa60a0c0 100644
--- a/fs_selfservice/FS-SelfService/SelfService.pm
+++ b/fs_selfservice/FS-SelfService/SelfService.pm
@@ -30,6 +30,7 @@ $socket .= '.'.$tag if defined $tag && length($tag);
'login' => 'MyAccount/login',
'logout' => 'MyAccount/logout',
'switch_acct' => 'MyAccount/switch_acct',
+ 'switch_cust' => 'MyAccount/switch_cust',
'customer_info' => 'MyAccount/customer_info',
'customer_info_short' => 'MyAccount/customer_info_short',
'billing_history' => 'MyAccount/billing_history',
diff --git a/fs_selfservice/FS-SelfService/cgi/select_cust.html b/fs_selfservice/FS-SelfService/cgi/select_cust.html
new file mode 100644
index 000000000..7ab55db45
--- /dev/null
+++ b/fs_selfservice/FS-SelfService/cgi/select_cust.html
@@ -0,0 +1,38 @@
+<HTML>
+ <HEAD>
+ <TITLE>Select customer</TITLE>
+ <%= $head %>
+ </HEAD>
+ <BODY BGCOLOR="<%= $body_bgcolor || '#eeeeee' %>">
+ <%= $body_header %>
+
+<FONT SIZE=5>Select customer</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+
+<%= $selfurl =~ s/\?.*//; ''; %>
+<FORM ACTION="<%= $selfurl %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="action" VALUE="switch_cust">
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="<%= $agentnum %>">
+
+<TABLE BGCOLOR="<%= $box_bgcolor || '#c0c0c0' %>" BORDER=0 CELLSPACING=2 CELLPADDING=0>
+
+ <TR>
+ <TH ALIGN="right">Customer </TH>
+ <TD>
+ <SELECT NAME="custnum">
+ <OPTION VALUE="">Select a customer
+<%= $OUT .= qq(<OPTION VALUE="$_">). encode_entities( $customers{$_} )
+ foreach keys %customers;
+%>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center"><INPUT TYPE="submit" VALUE="Select customer"></TD>
+ </TR>
+
+</TABLE>
+</FORM>
+
+<%= $body_footer %>
diff --git a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi
index 9443a7de7..2337fb51e 100755
--- a/fs_selfservice/FS-SelfService/cgi/selfservice.cgi
+++ b/fs_selfservice/FS-SelfService/cgi/selfservice.cgi
@@ -81,6 +81,7 @@ my @actions = ( qw(
process_change_password
customer_suspend_pkg
process_suspend_pkg
+ switch_cust
));
my @nologin_actions = (qw(
@@ -204,6 +205,12 @@ unless ( $nologin_actions{$action} ) {
# at this point $session_id is a real session
+ if ( ! $login_rv->{'custnum'} && ! $login_rv->{'svcnum'} && $login_rv->{'customers'} ) {
+ #select a customer if we're a multi-contact customer
+ do_template('select_cust', { %$login_rv } );
+ exit;
+ }
+
}
warn "calling $action sub\n"
@@ -212,6 +219,7 @@ $FS::SelfService::DEBUG = $DEBUG;
my $result = eval "&$action();";
die $@ if $@;
+use Data::Dumper;
warn Dumper($result) if $DEBUG;
if ( $result->{error} && ( $result->{error} eq "Can't resume session"
@@ -237,7 +245,13 @@ do_template($action, {
#--
-use Data::Dumper;
+sub switch_cust {
+ $action = 'myaccount';
+ FS::SelfService::switch_cust( 'session_id' => $session_id,
+ 'custnum' => scalar($cgi->param('custnum')),
+ );
+}
+
sub myaccount {
customer_info( 'session_id' => $session_id );
}
diff --git a/httemplate/browse/discount.html b/httemplate/browse/discount.html
index d3cf873d0..9b2298ae4 100644
--- a/httemplate/browse/discount.html
+++ b/httemplate/browse/discount.html
@@ -8,8 +8,9 @@
'count_query' => 'SELECT COUNT(*) FROM discount',
'disableable' => 1,
'disabled_statuspos' => 1,
- 'header' => [ 'Name', 'Class', 'Discount', ],
+ 'header' => [ 'Name', 'Comment', 'Class', 'Discount', ],
'fields' => [ 'name',
+ 'comment',
'classname',
'description',
],
diff --git a/httemplate/docs/about.html b/httemplate/docs/about.html
index 80d9488b6..0f173f228 100644
--- a/httemplate/docs/about.html
+++ b/httemplate/docs/about.html
@@ -56,7 +56,7 @@ GNU <b>Affero</b> General Public License.<BR>
% unless ( $agentnum ) {
<CENTER>
- <FONT SIZE="-3">"" - R. Hunter</FONT>
+ <FONT SIZE="-3">"Half the world's a desert / Cannibals eat human brains for dessert" - D. Zero</FONT>
</CENTER>
% }
diff --git a/httemplate/edit/cust_main-contacts.html b/httemplate/edit/cust_main-contacts.html
index 9f0654608..3b7eb07d3 100644
--- a/httemplate/edit/cust_main-contacts.html
+++ b/httemplate/edit/cust_main-contacts.html
@@ -11,6 +11,7 @@
{ 'field' => 'contactnum',
'type' => 'contact',
'colspan' => 6,
+ 'custnum' => $custnum,
'm2m_method' => 'cust_contact',
'm2m_dstcol' => 'contactnum',
'm2_label' => ' ', #'Contact',
diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi
index 353ae1799..da87bfca7 100755
--- a/httemplate/edit/cust_main.cgi
+++ b/httemplate/edit/cust_main.cgi
@@ -325,8 +325,8 @@ if ( $cgi->param('error') ) {
$cust_main->company( $prospect_main->company );
#first contact? -> name
- my @contacts = $prospect_main->contact;
- my $contact = $contacts[0];
+ my @prospect_contacts = $prospect_main->prospect_contact;
+ my $contact = $prospect_contacts[0]->contact;
$cust_main->first( $contact->first );
$cust_main->set( 'last', $contact->get('last') );
#contact phone numbers?
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index 9e506a731..4d5beee71 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -334,6 +334,10 @@ Example:
% #any?
% 'colspan' => $f->{'colspan'},
% 'required' => $f->{'required'},
+%
+% #contact
+% 'custnum' => $f->{'custnum'},
+% 'prospectnum' => $f->{'prospectnum'},
% );
%
% $include_common{$_} = $f->{$_} foreach grep exists($f->{$_}),
diff --git a/httemplate/elements/contact.html b/httemplate/elements/contact.html
index 979c26b49..ef74481c0 100644
--- a/httemplate/elements/contact.html
+++ b/httemplate/elements/contact.html
@@ -9,7 +9,7 @@
<SELECT NAME="<%$name%>_classnum" <% $onchange %>>
<OPTION VALUE="">
% my $classnum = scalar($cgi->param($name.'_classnum'))
-% || $contact->classnum;
+% || $X_contact->classnum;
% foreach my $contact_class (@contact_class) {
<OPTION VALUE="<% $contact_class->classnum %>"
<% ($contact_class->classnum == $classnum) ? 'SELECTED' : '' %>
@@ -40,6 +40,8 @@
% }
% } elsif ( $field eq 'emailaddress' ) {
% $value = join(', ', map $_->emailaddress, $contact->contact_email);
+% } elsif ( $field eq 'selfservice_access' || $field eq 'comment' ) {
+% $value = $X_contact->get($field);
% } else {
% $value = $contact->get($field);
% }
@@ -100,10 +102,25 @@ if ( $opt{'onchange'} ) {
my @contact_class = qsearch('contact_class', { 'disabled' => '' });
my $contact;
+my $X_contact;
if ( $curr_value ) {
$contact = qsearchs('contact', { 'contactnum' => $curr_value } );
+ if ( $opt{'custnum'} ) {
+ $X_contact = qsearchs('cust_contact', {
+ 'contactnum' => $curr_value,
+ 'custnum' => $opt{'custnum'},
+ });
+ } elsif ( $opt{'prospectnum'} ) {
+ $X_contact = qsearchs('prospect_contact', {
+ 'contactnum' => $curr_value,
+ 'prospectnum' => $opt{'prospectnum'},
+ });
+ } else {
+ die 'neither custnum nor prospectnum specified';
+ }
} else {
$contact = new FS::contact {};
+ $X_contact = new FS::cust_contact; #arbitrary, it could be prospect_contact
}
my %size = ( 'title' => 12 );
diff --git a/httemplate/elements/popup_link.html b/httemplate/elements/popup_link.html
index e5f8c61ca..2b6b187e9 100644
--- a/httemplate/elements/popup_link.html
+++ b/httemplate/elements/popup_link.html
@@ -2,9 +2,9 @@
Example:
- include('/elements/init_overlib.html')
+ <& /elements/init_overlib.html &>
- include( '/elements/popup_link.html', { #hashref or a list, either way is fine
+ <& /elements/popup_link.html', { #hashref or a list, either way is fine
#required
'action' => 'content.html', # uri for content of popup
@@ -23,7 +23,8 @@ Example:
'aname' => "target", # link NAME= value, useful for #targets
'target' => '_parent',
'style' => 'css-attribute:value',
- } )
+ }
+ &>
</%doc>
% if ($params->{'action'} && $label) {
diff --git a/httemplate/elements/tr-fixed.html b/httemplate/elements/tr-fixed.html
index 6904e3b30..373c0ab3a 100644
--- a/httemplate/elements/tr-fixed.html
+++ b/httemplate/elements/tr-fixed.html
@@ -1,6 +1,6 @@
<% include('tr-td-label.html', @_ ) %>
- <TD BGCOLOR="#dddddd" <% $style %>><% $value %></TD>
+ <TD BGCOLOR="#dddddd" <% $style %> <% $colspan %>><% $value %></TD>
</TR>
@@ -10,7 +10,9 @@
my %opt = @_;
-my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+my $style = $opt{'cell_style'} ? ' STYLE="'. $opt{'cell_style'}. '" ' : '';
+
+my $colspan = $opt{'colspan'} ? ' COLSPAN="'. $opt{'colspan'}. '" ' : '';
my $value = $opt{'formatted_value'} || $opt{'curr_value'} || $opt{'value'};
$value = $opt{'prefix'} . $value if defined($opt{'prefix'});
diff --git a/httemplate/elements/tr-select-contact.html b/httemplate/elements/tr-select-contact.html
index e37d26d1b..0bfa8934c 100644
--- a/httemplate/elements/tr-select-contact.html
+++ b/httemplate/elements/tr-select-contact.html
@@ -138,8 +138,8 @@ if ( $cgi->param('error') ) {
if ( length($opt{'curr_value'}) ) {
$contactnum = $opt{'curr_value'};
} elsif ($prospect_main) {
- my @cust_contact = $prospect_main->contact;
- $contactnum = $cust_contact[0]->contactnum if scalar(@cust_contact)==1;
+ my @prospect_contact = $prospect_main->prospect_contact;
+ $contactnum = $prospect_contact[0]->contactnum if scalar(@cust_contact)==1;
} else { #$cust_main
$cgi->param('contactnum') =~ /^(\-?\d*)$/ or die "illegal contactnum";
$contactnum = $1;
@@ -176,8 +176,10 @@ my $contact_sort = sub {
};
my @contact;
-push @contact, $cust_main->cust_contact if $cust_main;
-push @contact, $prospect_main->contact if $prospect_main;
+push @contact, map $_->contact, $cust_main->cust_contact
+ if $cust_main;
+push @contact, map $_->contact, $prospect_main->prospect_contact
+ if $prospect_main;
push @contact, $contact
if !$cust_main && $contact && $contact->contactnum > 0
&& ! grep { $_->contactnum == $contact->contactnum } @contact;
diff --git a/httemplate/elements/tr-select-cust_location.html b/httemplate/elements/tr-select-cust_location.html
index abaaa5b42..7a5b43bb8 100644
--- a/httemplate/elements/tr-select-cust_location.html
+++ b/httemplate/elements/tr-select-cust_location.html
@@ -287,6 +287,8 @@ if ( $locationnum && $locationnum > 0 ) {
$cust_location->coord_auto('Y');
my $location_sort = sub {
+ #enabled w/label_prefix _location # $a->locationname cmp $b->locationname
+ # or
$a->country cmp $b->country
or lc($a->city) cmp lc($b->city)
or lc($a->address1) cmp lc($b->address1)
diff --git a/httemplate/misc/email-quotation.html b/httemplate/misc/email-quotation.html
index b93b80bb7..64e3691b5 100644
--- a/httemplate/misc/email-quotation.html
+++ b/httemplate/misc/email-quotation.html
@@ -16,9 +16,11 @@
% }
% }
-% my @contact = $quotation->custnum ? $quotation->cust_main->cust_contact
-% : $quotation->prospect_main->contact;
-% foreach my $contact ( @contact ) {
+% my @X_contact = $quotation->custnum
+% ? $quotation->cust_main->cust_contact
+% : $quotation->prospect_main->prospect_contact;
+% foreach my $X_contact ( @X_contact ) {
+% my $contact = $X_contact->contact;
% foreach my $contact_email ( $contact->contact_email ) {
% $emails++;
<& .emailrow, $contact_email->emailaddress, $contact->firstlast &>
diff --git a/httemplate/search/contact.html b/httemplate/search/contact.html
index 193349369..c3667df98 100644
--- a/httemplate/search/contact.html
+++ b/httemplate/search/contact.html
@@ -1,13 +1,13 @@
<& elements/search.html,
title => 'Contacts',
name_singular => 'contact',
- query => { select => $select,
+ query => { select => join(', ', @select),
table => 'contact',
addl_from => $addl_from,
hashref => \%hash,
extra_sql => $extra_sql,
},
- count_query => "SELECT COUNT(*) FROM contact $extra_sql", #XXX
+ count_query => "SELECT COUNT(*) FROM contact $addl_from $extra_sql", #XXX
header => \@header,
fields => \@fields,
links => \@links,
@@ -17,13 +17,29 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List contacts');
-my $select = 'contact.*';
+my @select = 'contact.contactnum AS contact_contactnum'; #if we select it as bare contactnum, the multi-customer listings go away
+push @select, map "contact.$_", qw( first last title );
my %hash = ();
my $addl_from = '';
-my @header = ( 'First', 'Last', 'Title', );
-my @fields = ( 'first', 'last', 'title', );
-my @links = ( '', '', '' );
+my $link; #for closure in this sub, we'll define it later
+my $contact_classname_sub = sub {
+ my $contact = shift;
+ my %hash = ( 'contactnum' => $contact->contact_contactnum );
+ my $X_contact;
+ if ( $link eq 'cust_main' ) {
+ $X_contact = qsearchs('cust_contact', { %hash, 'custnum' => $contact->custnum } );
+ } elsif ( $link eq 'prospect_main' ) {
+ $X_contact = qsearchs('prospect_contact', { %hash, 'prospectnum' => $contact->prospectnum } );
+ } else {
+ die 'guru meditation #5555';
+ }
+ $X_contact->contact_classname;
+};
+
+my @header = ( 'First', 'Last', 'Title', 'Type' );
+my @fields = ( 'first', 'last', 'title', $contact_classname_sub );
+my @links = ( '', '', '', '', );
my $company_link = '';
@@ -32,22 +48,30 @@ if ( $cgi->param('selfservice_access') eq 'Y' ) {
}
my $extra_sql = '';
-if ( $cgi->param('link') ) {
+$link = $cgi->param('link');
+if ( $link ) {
- my $coalesce = ', COALESCE( cust_main.company,';
my $as = ') AS prospect_or_customer';
- if ( $cgi->param('link') eq 'cust_main' ) {
+ if ( $link eq 'cust_main' ) {
push @header, 'Customer';
- $select .= "$coalesce cust_main.first||' '||cust_main.last $as";
- $addl_from = ' LEFT JOIN cust_main USING ( custnum )';
- $extra_sql = ' custnum IS NOT NULL ';
+ push @select,
+ "COALESCE( cust_main.company, cust_main.first||' '||cust_main.last $as",
+ map "cust_contact.$_", qw( custnum classnum comment selfservice_access );
+ $addl_from =
+ ' LEFT JOIN cust_contact USING ( contactnum ) '.
+ ' LEFT JOIN cust_main ON ( cust_contact.custnum = cust_main.custnum )';
+ $extra_sql = ' cust_contact.custnum IS NOT NULL ';
$company_link = [ $p.'view/cust_main.cgi?', 'custnum' ];
- } elsif ( $cgi->param('link') eq 'prospect_main' ) {
+ } elsif ( $link eq 'prospect_main' ) {
push @header, 'Prospect';
- $select .= "$coalesce contact.first||' '||contact.last $as";
- $addl_from = ' LEFT JOIN prospect_main USING ( prospectnum )';
- $extra_sql = ' prospectnum IS NOT NULL ';
+ push @select,
+ "COALESCE( prospect_main.company, contact.first||' '||contact.last $as",
+ map "prospect_contact.$_", qw( prospectnum classnum comment );
+ $addl_from =
+ ' LEFT JOIN prospect_contact USING ( contactnum ) '.
+ ' LEFT JOIN prospect_main ON ( prospect_contact.prospectnum = prospect_main.prospectnum )';
+ $extra_sql = ' prospect_contact.prospectnum IS NOT NULL ';
$company_link = [ $p.'view/prospect_main.html?', 'prospectnum' ];
} else {
die "don't know how to report on contacts linked to specified table";
@@ -62,6 +86,9 @@ if ( $cgi->param('link') ) {
push @header, 'Self-service';
push @fields, 'selfservice_access';
+push @header, 'Comment';
+push @fields, 'comment';
+
$extra_sql = (keys(%hash) ? ' AND ' : ' WHERE '). $extra_sql
if $extra_sql;
diff --git a/httemplate/search/cust_msg.html b/httemplate/search/cust_msg.html
index 486c7b09c..d5b865c3b 100644
--- a/httemplate/search/cust_msg.html
+++ b/httemplate/search/cust_msg.html
@@ -47,7 +47,7 @@
],
'html_init' => $html_init,
'really_disable_download' => 1,
- @_
+ @_ #why?
&>
<%init>
#hmm...
@@ -71,7 +71,7 @@ if ( $cgi->param('msgtype') =~ /^(\w+)$/ ) {
push @where, "msgtype = '$1'";
}
if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
- push @where, "custnum = $1";
+ push @where, "cust_msg.custnum = $1";
}
my ($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, '');
push @where, "(_date >= $beginning AND _date <= $ending)";
diff --git a/httemplate/search/prospect_main.html b/httemplate/search/prospect_main.html
index 4798f58f2..241918b98 100644
--- a/httemplate/search/prospect_main.html
+++ b/httemplate/search/prospect_main.html
@@ -12,9 +12,9 @@
sub {
my $pm = shift;
[ map {
- [ { 'data' => $_->line, }, ];
+ [ { 'data'=>$_->contact->line, }, ];
}
- $pm->contact
+ $pm->prospect_contact
];
},
],
diff --git a/httemplate/view/cust_main/contacts_new.html b/httemplate/view/cust_main/contacts_new.html
index f73483ae1..f0bc0b848 100644
--- a/httemplate/view/cust_main/contacts_new.html
+++ b/httemplate/view/cust_main/contacts_new.html
@@ -6,26 +6,31 @@
% my $bgcolor1 = '#eeeeee';
% my $bgcolor2 = '#ffffff';
% my $bgcolor = $bgcolor2;
+% my $th = '<TH CLASS="grid" ALIGN="left" BGCOLOR="#cccccc">';
<TR>
- <TH CLASS="grid" ALIGN="left" BGCOLOR="#cccccc">Type</TH>
- <TH CLASS="grid" ALIGN="left" BGCOLOR="#cccccc">Contact</TH>
- <TH CLASS="grid" ALIGN="left" BGCOLOR="#cccccc">Email</TH>
- <TH CLASS="grid" ALIGN="left" BGCOLOR="#cccccc">Self-service</TH>
+ <%$th%>Type</TH>
+ <%$th%>Contact</TH>
+ <%$th%>Email</TH>
+ <%$th%>Self-service</TH>
% foreach my $phone_type (@phone_type) {
- <TH CLASS="grid" ALIGN="left" BGCOLOR="#cccccc"><% $phone_type->typename |h %> phone</TD>
+ <%$th%><% $phone_type->typename |h %></TH>
% }
+ <%$th%>Comment</TH>
</TR>
-% foreach my $contact ( @contacts ) {
+% foreach my $cust_contact ( @cust_contacts ) {
+% my $contact = $cust_contact->contact;
+% my $td = qq(<TD CLASS="grid" BGCOLOR="$bgcolor">);
+
<TR>
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $contact->contact_classname |h %></TD>
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $contact->line |h %></TD>
+ <%$td%><% $cust_contact->contact_classname |h %></TD>
+ <%$td%><% $contact->line |h %></TD>
% my @contact_email = $contact->contact_email;
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% join(', ', map $_->emailaddress, @contact_email) %></TD>
+ <%$td%><% join(', ', map $_->emailaddress, @contact_email) %></TD>
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
-% if ( $contact->selfservice_access ) {
+ <%$td%>
+% if ( $cust_contact->selfservice_access ) {
Enabled
%# <FONT SIZE="-1"><A HREF="XXX">disable</A>
%# <A HREF="XXX">re-email</A></FONT>
@@ -41,9 +46,11 @@
% 'contactnum' => $contact->contactnum,
% 'phonetypenum' => $phone_type->phonetypenum,
% });
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $contact_phone ? $contact_phone->phonenum_pretty : '' |h %></TD>
+ <%$td%><% $contact_phone ? $contact_phone->phonenum_pretty : '' |h %></TD>
% }
+ <%$td%><% $cust_contact->comment |h %></TD>
+
</TR>
% if ( $bgcolor eq $bgcolor1 ) {
@@ -63,6 +70,6 @@ my @phone_type = qsearch({table=>'phone_type', order_by=>'weight'});
my( $cust_main ) = @_;
#my $conf = new FS::Conf;
-my @contacts = $cust_main->cust_contact;
+my @cust_contacts = $cust_main->cust_contact;
</%init>
diff --git a/httemplate/view/prospect_main.html b/httemplate/view/prospect_main.html
index 66abffcdd..a1f14a374 100644
--- a/httemplate/view/prospect_main.html
+++ b/httemplate/view/prospect_main.html
@@ -39,9 +39,10 @@
</TR>
% }
-% foreach my $contact ( $prospect_main->contact ) {
+% foreach my $prospect_contact ( $prospect_main->prospect_contact ) {
+% my $contact = $prospect_contact->contact;
<TR>
- <TD ALIGN="right"><% $contact->contact_classname %> Contact</TD>
+ <TD ALIGN="right"><% $prospect_contact->contact_classname %> Contact</TD>
<TD BGCOLOR="#FFFFFF"><% $contact->line %></TD>
</TR>
%}