[freeside-commits] branch master updated. 916c7d12076875cfc090ffaebde8f5add436430e

Mark Wells mark at 420.am
Wed Apr 13 15:09:54 PDT 2016


The branch, master has been updated
       via  916c7d12076875cfc090ffaebde8f5add436430e (commit)
       via  7a8207e791770dde97db3cf4c75882a18ee679e8 (commit)
       via  f3f54e8ebae3d2f05858ab856f0b727f55c434f7 (commit)
       via  eb8ed18a57d255741003d7d861786eada7970f50 (commit)
      from  31807e3e9acddff34011e919728c113e69ad9a26 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 916c7d12076875cfc090ffaebde8f5add436430e
Author: Mark Wells <mark at freeside.biz>
Date:   Wed Apr 13 15:07:40 2016 -0700

    create prospects from towercoverage.com EUS, #39776

diff --git a/ng_selfservice/coverage_post.php b/ng_selfservice/coverage_post.php
new file mode 100644
index 0000000..657a2ff
--- /dev/null
+++ b/ng_selfservice/coverage_post.php
@@ -0,0 +1,43 @@
+<?
+
+$DEBUG = 1;
+
+require_once('freeside.class.php');
+
+$xml = file_get_contents('php://input');
+//error_log($xml);
+
+$doc = new SimpleXMLElement($xml);
+$cd = $doc->CustomerDetails;
+if ($DEBUG) {
+    error_log(var_dump($cd));
+}
+
+// State and Country are names rather than codes, but we fix that on the other
+// end.
+// It doesn't look like TowerCoverage ever sends a company name.
+$map_fields = Array(
+    'first'           => 'FirstName',
+    'last'            => 'LastName',
+    'address1'        => 'StreetAddress',
+    'city'            => 'City',
+    'state'           => 'State',
+    'country'         => 'Country',
+    'zip'             => 'ZIP',
+    'phone_daytime'   => 'PhoneNumber',
+    'emailaddress'    => 'EmailAddress',
+    'comment'         => 'Comment',
+    'referral_title'  => 'HearAbout',
+);
+
+$prospect = Array();
+// missing from this: any way to set the agent. this should use the API key.
+foreach ($map_fields as $k => $v) {
+    $prospect[$k] = (string)($cd->$v);
+}
+error_log(var_dump($prospect));
+$freeside = new FreesideSelfService();
+$result = $freeside->new_prospect($prospect);
+error_log(var_dump($result));
+
+?>

commit 7a8207e791770dde97db3cf4c75882a18ee679e8
Author: Mark Wells <mark at freeside.biz>
Date:   Wed Apr 13 13:19:07 2016 -0700

    make new_prospect smarter, #39776

diff --git a/FS/FS/ClientAPI/Signup.pm b/FS/FS/ClientAPI/Signup.pm
index 94c9c5e..5010ec9 100644
--- a/FS/FS/ClientAPI/Signup.pm
+++ b/FS/FS/ClientAPI/Signup.pm
@@ -1250,9 +1250,11 @@ sub get_agentnum {
 
 Creates a new L<FS::prospect_main> entry. PACKET must contain:
 
-- either agentnum or session_id (unless signup_server-default_agentnum exists)
+- either agentnum or session_id; if not, signup_server-default_agentnum will
+be used and must not be empty
 
-- refnum (unless signup_server-default_refnum exists)
+- either refnum or referral_title; if not, signup_server-default_refnum will
+be used and must not be empty
 
 - last and first (names), and optionally company and title
 
@@ -1260,8 +1262,15 @@ Creates a new L<FS::prospect_main> entry. PACKET must contain:
 
 - emailaddress
 
+and can also contain:
+
 - one or more of phone_daytime, phone_night, phone_mobile, and phone_fax
 
+- a 'comment' (will be attached to the contact)
+
+State and country will be normalized to Freeside state/country codes if
+necessary.
+
 =cut
 
 sub new_prospect {
@@ -1274,10 +1283,29 @@ sub new_prospect {
   my $dbh = dbh;
   my $conf = FS::Conf->new;
 
+  my $error;
+
   my $agentnum = get_agentnum($packet);
   return $agentnum if ref $agentnum;
-  my $refnum = $packet->{refnum}
-               || $conf->config('signup_server-default_refnum');
+  my $refnum;
+  if ( my $title = $packet->{referral_title} ) {
+    my $part_referral = qsearchs('part_referral', {
+        'agentnum'  => $agentnum,
+        'title'     => $title,
+    });
+    $part_referral ||= qsearchs('part_referral', {
+        'agentnum'  => '',
+        'title'     => $title,
+    });
+    if (!$part_referral) {
+      return { error => "Unknown referral type: '$title'" };
+    }
+    $refnum = $part_referral->refnum;
+  } elsif ( $packet->{refnum} ) {
+    $refnum = $packet->{refnum};
+  }
+  $refnum ||= $conf->config('signup_server-default_refnum');
+  return { error => "Signup referral type is not configured" } if !$refnum;
 
   my $prospect = FS::prospect_main->new({
       'agentnum' => $agentnum,
@@ -1286,12 +1314,42 @@ sub new_prospect {
   });
 
   my $location = FS::cust_location->new;
-  foreach ( qw(address1 address2 city county state zip country ) ) {
+  foreach ( qw(address1 address2 city county zip ) ) {
     $location->set($_, $packet->{$_});
   }
+  # normalize country and state if they're not already ISO codes
+  # easier than doing it on the client side--we already have the tables here
+  my $country = $packet->{country};
+  my $state = $packet->{state};
+  if (length($country) > 2) {
+    # it likes title case
+    $country = join(' ', map ucfirst, split(/\s+/, $country));
+    my $lsc = Locale::SubCountry->new($country);
+    if ($lsc) {
+      $country = uc($lsc->country_code);
+
+      if ($lsc->has_sub_countries) {
+        if ( $lsc->full_name($state) eq 'unknown' ) {
+          # then we were probably given a full name, so resolve it
+          $state = $lsc->code($state);
+          if ( $state eq 'unknown' ) {
+            # doesn't resolve as a full name either, return an error
+            $error = "Unknown state: ".$packet->{state};
+          } else {
+            $state = uc($state);
+          }
+        }
+      } # else state doesn't matter
+    } else {
+      # couldn't find the country in LSC
+      $error = "Unknown country: $country";
+    }
+  }
+  $location->set('country', $country);
+  $location->set('state', $state);
   $prospect->set('cust_location', $location);
-  
-  my $error = $prospect->insert; # also does location
+
+  $error ||= $prospect->insert; # also does location
   return { error => $error } if $error;
 
   my $contact = FS::contact->new({
@@ -1300,7 +1358,7 @@ sub new_prospect {
       invoice_dest  => 'Y',
   });
   # use emailaddress pseudo-field behavior here
-  foreach (qw(last first title emailaddress)) {
+  foreach (qw(last first title emailaddress comment)) {
     $contact->set($_, $packet->{$_});
   }
   $error = $contact->insert;

commit f3f54e8ebae3d2f05858ab856f0b727f55c434f7
Author: Mark Wells <mark at freeside.biz>
Date:   Wed Apr 13 13:18:41 2016 -0700

    add part_referral external ID for API signups, #39776

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 82cf680..d94f963 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -3591,10 +3591,11 @@ sub tables_hashref {
         'refnum',   'serial',     '',        '', '', '', 
         'referral', 'varchar',    '',   $char_d, '', '', 
         'disabled', 'char',   'NULL',         1, '', '', 
-        'agentnum', 'int',    'NULL',        '', '', '', 
+        'agentnum', 'int',    'NULL',        '', '', '',
+        'title',   'varchar', 'NULL',   $char_d, '', '', 
       ],
       'primary_key'  => 'refnum',
-      'unique'       => [],
+      'unique'       => [ ['agentnum', 'title'] ],
       'index'        => [ ['disabled'], ['agentnum'], ],
       'foreign_keys' => [
                           { columns    => [ 'agentnum' ],
diff --git a/FS/FS/part_referral.pm b/FS/FS/part_referral.pm
index e4a5823..2df8a75 100644
--- a/FS/FS/part_referral.pm
+++ b/FS/FS/part_referral.pm
@@ -44,6 +44,9 @@ The following fields are currently supported:
 
 =item agentnum - Optional agentnum (see L<FS::agent>)
 
+=item title - an optional external string that identifies this
+referral source, such as an advertising campaign code.
+
 =back
 
 =head1 NOTE
@@ -101,6 +104,7 @@ sub check {
     || $self->ut_text('referral')
     || $self->ut_enum('disabled', [ '', 'Y' ] )
     #|| $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
+    || $self->ut_textn('title')
     || ( $setup_hack
            ? $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum' )
            : $self->ut_agentnum_acl('agentnum', 'Edit global advertising sources')
diff --git a/httemplate/edit/part_referral.html b/httemplate/edit/part_referral.html
index e9fd794..04287d6 100755
--- a/httemplate/edit/part_referral.html
+++ b/httemplate/edit/part_referral.html
@@ -3,11 +3,13 @@
                 'table'       => 'part_referral',
                 'fields'      => [ 'referral',
                                    { field=>'agentnum', type=>'select-agent', },
+                                   'title',
                                    { field=>'disabled', type=>'checkbox', value=>'Y'  } ,
                                  ],
                 'labels'      => { 'refnum'   => 'Ad Source',
                                    'referral' => 'Advertising source',
                                    'agentnum' => 'Agent',
+                                   'title'    => 'External ID',
                                    'disabled' => 'Disabled',
                                  },
                 'viewall_dir' => 'browse',

commit eb8ed18a57d255741003d7d861786eada7970f50
Author: Mark Wells <mark at freeside.biz>
Date:   Tue Apr 12 12:10:10 2016 -0700

    create prospects through signup API, #39776

diff --git a/FS/FS/ClientAPI/Signup.pm b/FS/FS/ClientAPI/Signup.pm
index df9ee88..94c9c5e 100644
--- a/FS/FS/ClientAPI/Signup.pm
+++ b/FS/FS/ClientAPI/Signup.pm
@@ -7,7 +7,7 @@ use Data::Dumper;
 use Tie::RefHash;
 use Digest::SHA qw(sha512_hex);
 use FS::Conf;
-use FS::Record qw(qsearch qsearchs dbdef);
+use FS::Record qw(qsearch qsearchs dbdef dbh);
 use FS::CGI qw(popurl);
 use FS::Msgcat qw(gettext);
 use FS::Misc qw(card_types);
@@ -31,6 +31,25 @@ use FS::cust_payby;
 $DEBUG = 1;
 $me = '[FS::ClientAPI::Signup]';
 
+=head1 NAME
+
+FS::ClientAPI::Signup - Front-end API for signing up customers
+
+=head1 DESCRIPTION
+
+This module provides the ClientAPI functions for talking to a signup
+server. The signup server is open to the public, i.e. does not require a
+login. The back-end Freeside server creates customers, orders packages and
+services, and processes initial payments.
+
+=head1 METHODS
+
+=over 4
+
+=cut
+
+# document the rest of this as we work on it
+
 sub clear_cache {
   warn "$me clear_cache called\n" if $DEBUG;
   my $cache = new FS::ClientAPI_SessionCache( {
@@ -499,21 +518,8 @@ sub new_customer {
     #possibly some validation will be needed
   }
 
-  my $agentnum;
-  if ( exists $packet->{'session_id'} ) {
-    my $cache = new FS::ClientAPI_SessionCache( {
-      'namespace' => 'FS::ClientAPI::Agent',
-    } );
-    my $session = $cache->get($packet->{'session_id'});
-    if ( $session ) {
-      $agentnum = $session->{'agentnum'};
-    } else {
-      return { 'error' => "Can't resume session" }; #better error message
-    }
-  } else {
-    $agentnum = $packet->{agentnum}
-                || $conf->config('signup_server-default_agentnum');
-  }
+  my $agentnum = get_agentnum($packet);
+  return $agentnum if ref($agentnum);
 
   my ($bill_hash, $ship_hash);
   foreach my $f (FS::cust_main->location_fields) {
@@ -924,21 +930,8 @@ sub new_customer_minimal {
     #possibly some validation will be needed
   }
 
-  my $agentnum;
-  if ( exists $packet->{'session_id'} ) {
-    my $cache = new FS::ClientAPI_SessionCache( {
-      'namespace' => 'FS::ClientAPI::Agent',
-    } );
-    my $session = $cache->get($packet->{'session_id'});
-    if ( $session ) {
-      $agentnum = $session->{'agentnum'};
-    } else {
-      return { 'error' => "Can't resume session" }; #better error message
-    }
-  } else {
-    $agentnum = $packet->{agentnum}
-                || $conf->config('signup_server-default_agentnum');
-  }
+  my $agentnum = get_agentnum($packet);
+  return $agentnum if ref($agentnum);
 
   #shares some stuff with htdocs/edit/process/cust_main.cgi... take any
   # common that are still here and library them.
@@ -1220,4 +1213,128 @@ sub capture_payment {
 
 }
 
+=item get_agentnum PACKET
+
+Given a PACKET from the signup server, looks up the agentnum to use for signing
+up a customer. This will use 'session_id' if the agent is authenticated,
+otherwise 'agentnum', otherwise the 'signup_server-default_agentnum' config. If
+the agent can't be found, returns an error packet.
+
+=cut
+
+sub get_agentnum {
+  my $packet = shift;
+  my $conf = new FS::Conf;
+  my $agentnum;
+  if ( exists $packet->{'session_id'} ) {
+    my $cache = new FS::ClientAPI_SessionCache( {
+      'namespace' => 'FS::ClientAPI::Agent',
+    } );
+    my $session = $cache->get($packet->{'session_id'});
+    if ( $session ) {
+      $agentnum = $session->{'agentnum'};
+    } else {
+      return { 'error' => "Can't resume session" }; #better error message
+    }
+  } else {
+    $agentnum = $packet->{agentnum}
+                || $conf->config('signup_server-default_agentnum');
+  }
+  if ( $agentnum and FS::agent->count('agentnum = ?', $agentnum) ) {
+    return $agentnum;
+  }
+  return { 'error' => 'Signup is not configured' };
+}
+
+=item new_prospect PACKET
+
+Creates a new L<FS::prospect_main> entry. PACKET must contain:
+
+- either agentnum or session_id (unless signup_server-default_agentnum exists)
+
+- refnum (unless signup_server-default_refnum exists)
+
+- last and first (names), and optionally company and title
+
+- address1, city, state, country, zip, and optionally address2
+
+- emailaddress
+
+- one or more of phone_daytime, phone_night, phone_mobile, and phone_fax
+
+=cut
+
+sub new_prospect {
+
+  my $packet = shift;
+  warn "$me new_prospect called\n".Dumper($packet) if $DEBUG;
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+  my $conf = FS::Conf->new;
+
+  my $agentnum = get_agentnum($packet);
+  return $agentnum if ref $agentnum;
+  my $refnum = $packet->{refnum}
+               || $conf->config('signup_server-default_refnum');
+
+  my $prospect = FS::prospect_main->new({
+      'agentnum' => $agentnum,
+      'refnum'   => $refnum,
+      'company'  => $packet->{company},
+  });
+
+  my $location = FS::cust_location->new;
+  foreach ( qw(address1 address2 city county state zip country ) ) {
+    $location->set($_, $packet->{$_});
+  }
+  $prospect->set('cust_location', $location);
+  
+  my $error = $prospect->insert; # also does location
+  return { error => $error } if $error;
+
+  my $contact = FS::contact->new({
+      prospectnum   => $prospect->prospectnum,
+      locationnum   => $location->locationnum,
+      invoice_dest  => 'Y',
+  });
+  # use emailaddress pseudo-field behavior here
+  foreach (qw(last first title emailaddress)) {
+    $contact->set($_, $packet->{$_});
+  }
+  $error = $contact->insert;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return { error => $error };
+  }
+
+  foreach my $phone_type (qsearch('phone_type', {})) {
+    my $key = 'phone_' . lc($phone_type->typename);
+    my $phonenum = $packet->{$key};
+    if ( $phonenum ) {
+      # just to not have to supply country code from the other end
+      my $number = Number::Phone->new($location->country, $phonenum);
+      if (!$number) {
+        $error = 'invalid phone number';
+      } else {
+        my $phone = FS::contact_phone->new({
+            contactnum    => $contact->contactnum,
+            phonenum      => $phonenum,
+            countrycode   => $number->country_code,
+            phonetypenum  => $phone_type->phonetypenum,
+        });
+        $error = $phone->insert;
+      }
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return { error => $phone_type->typename . ' phone: ' . $error };
+      }
+    }
+  } # foreach $phone_type
+  
+  $dbh->commit if $oldAutoCommit;
+  return { prospectnum => $prospect->prospectnum };
+}
+
 1;
diff --git a/FS/FS/ClientAPI_XMLRPC.pm b/FS/FS/ClientAPI_XMLRPC.pm
index 2dea801..622f3df 100644
--- a/FS/FS/ClientAPI_XMLRPC.pm
+++ b/FS/FS/ClientAPI_XMLRPC.pm
@@ -192,6 +192,7 @@ sub ss2clientapi {
   'new_customer_minimal'      => 'Signup/new_customer_minimal',
   'capture_payment'           => 'Signup/capture_payment',
   'clear_signup_cache'        => 'Signup/clear_cache',
+  'new_prospect'              => 'Signup/new_prospect',
   'new_agent'                 => 'Agent/new_agent',
   'agent_login'               => 'Agent/agent_login',
   'agent_logout'              => 'Agent/agent_logout',
diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm
index e902100..49d6f31 100644
--- a/fs_selfservice/FS-SelfService/SelfService.pm
+++ b/fs_selfservice/FS-SelfService/SelfService.pm
@@ -112,6 +112,7 @@ $socket .= '.'.$tag if defined $tag && length($tag);
   'new_customer'              => 'Signup/new_customer',
   'new_customer_minimal'      => 'Signup/new_customer_minimal',
   'capture_payment'           => 'Signup/capture_payment',
+  'new_prospect'              => 'Signup/new_prospect',
   #N/A 'clear_signup_cache'        => 'Signup/clear_cache',
   'new_agent'                 => 'Agent/new_agent',
   'agent_login'               => 'Agent/agent_login',

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/ClientAPI/Signup.pm                    |  237 ++++++++++++++++++++++----
 FS/FS/ClientAPI_XMLRPC.pm                    |    1 +
 FS/FS/Schema.pm                              |    5 +-
 FS/FS/part_referral.pm                       |    4 +
 fs_selfservice/FS-SelfService/SelfService.pm |    1 +
 httemplate/edit/part_referral.html           |    2 +
 ng_selfservice/coverage_post.php             |   43 +++++
 7 files changed, 260 insertions(+), 33 deletions(-)
 create mode 100644 ng_selfservice/coverage_post.php




More information about the freeside-commits mailing list