[freeside-commits] branch FREESIDE_3_BRANCH updated. 645a1c856120171b58bfb3eb5f326c06a9c49825

Jonathan Prykop jonathan at 420.am
Fri Apr 22 14:04:51 PDT 2016


The branch, FREESIDE_3_BRANCH has been updated
       via  645a1c856120171b58bfb3eb5f326c06a9c49825 (commit)
       via  70ff8d2c5f32a674d4ae75c8f018e630033ed645 (commit)
       via  94cdf97cf0a14a433c81c44bbeb76bf953db2dcc (commit)
       via  a8d1f57eba87ab0f00ce5cd3ba8ef0cef956f68f (commit)
       via  a76eba49dd827c1afefa62d96fa796ff39bb904d (commit)
      from  932d55b3fbcc45aa697ff9996f0f166aa11caf8a (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 645a1c856120171b58bfb3eb5f326c06a9c49825
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Wed Apr 20 19:48:53 2016 -0500

    RT#29296: API stuff: Add new locations [v3 reconcile]

diff --git a/FS/FS/API.pm b/FS/FS/API.pm
index 5db29b2..4e6cb6c 100644
--- a/FS/FS/API.pm
+++ b/FS/FS/API.pm
@@ -755,6 +755,8 @@ secret
 
 locationnum - pass this, or the following keys (don't pass both)
 
+locationname
+
 address1
 
 address2
@@ -767,8 +769,22 @@ state
 
 zip
 
+addr_clean
+
 country
 
+censustract
+
+censusyear
+
+location_type
+
+location_number
+
+location_kind
+
+incorporated
+
 On error, returns a hashref with an 'error' key.
 On success, returns a hashref with 'pkgnum' and 'locationnum' keys,
 containing the new values.
@@ -785,7 +801,24 @@ sub change_package_location {
 
   my %changeopt;
 
-  foreach my $field ('locationnum',FS::cust_location::API::API_editable_fields()) {
+  foreach my $field ( qw(
+    locationnum
+    locationname
+    address1
+    address2
+    city
+    county
+    state
+    zip
+    addr_clean
+    country
+    censustract
+    censusyear
+    location_type
+    location_number
+    location_kind
+    incorporated
+  )) {
     $changeopt{$field} = $opt{$field} if $opt{$field};
   }
 
diff --git a/FS/FS/cust_location/API.pm b/FS/FS/cust_location/API.pm
deleted file mode 100644
index 7d9140f..0000000
--- a/FS/FS/cust_location/API.pm
+++ /dev/null
@@ -1,34 +0,0 @@
-package FS::cust_location::API;
-
-use strict;
-
-# this gets used by FS::cust_main::API and FS::cust_pkg::API,
-# so don't use FS::cust_main or FS::cust_pkg here
-
-# some of these could probably be included, but in general,
-# probably a bad idea to expose everything in 
-# cust_main::Location::location_fields by default
-#
-#locationname
-#district
-#latitude
-#longitude
-#censustract
-#censusyear
-#geocode
-#coord_auto
-#addr_clean
-
-sub API_editable_fields {
-  return qw(
-    address1
-    address2
-    city
-    county
-    state
-    zip
-    country
-  );
-}
-
-1;
diff --git a/FS/FS/cust_pkg/API.pm b/FS/FS/cust_pkg/API.pm
index 7ef3f72..837cf40 100644
--- a/FS/FS/cust_pkg/API.pm
+++ b/FS/FS/cust_pkg/API.pm
@@ -2,8 +2,6 @@ package FS::cust_pkg::API;
 
 use strict;
 
-use FS::cust_location::API;
-
 sub API_getinfo {
   my $self = shift;
 
@@ -25,7 +23,23 @@ sub API_change {
 
   # update location--accepts raw fields OR location
   my %location_hash;
-  foreach my $field (FS::cust_location::API::API_editable_fields()) {
+  foreach my $field ( qw(
+    locationname
+    address1
+    address2
+    city
+    county
+    state
+    zip
+    addr_clean
+    country
+    censustract
+    censusyear
+    location_type
+    location_number
+    location_kind
+    incorporated
+  ) ) {
     $location_hash{$field} = $opt{$field} if $opt{$field};
   }
   return { 'error' => 'Cannot pass both locationnum and location fields' }
diff --git a/FS/FS/part_export/cust_http.pm b/FS/FS/part_export/cust_http.pm
index f72d006..c13e18d 100644
--- a/FS/FS/part_export/cust_http.pm
+++ b/FS/FS/part_export/cust_http.pm
@@ -55,7 +55,7 @@ tie %options, 'Tie::IxHash',
 ;
 
 %info = (
-  'svc'     => [qw( cust_main cust_location )],
+  'svc'     => [qw( cust_main )],
   'desc'    => 'Send an HTTP or HTTPS GET or POST request, for customers.',
   'options' => \%options,
   'no_machine' => 1,
diff --git a/FS/FS/part_export/cust_location_http.pm b/FS/FS/part_export/cust_location_http.pm
index e2830db..fe77223 100644
--- a/FS/FS/part_export/cust_location_http.pm
+++ b/FS/FS/part_export/cust_location_http.pm
@@ -4,9 +4,28 @@ use strict;
 use base qw( FS::part_export::http );
 use vars qw( %options %info );
 
-use FS::cust_main::Location;
-
-my @location_fields = ( qw( custnum prospectnum ), FS::cust_main::Location->location_fields );
+my @location_fields = qw(
+  custnum
+  prospectnum
+  locationname
+  address1
+  address2
+  city
+  county
+  state
+  zip
+  country
+  latitude
+  longitude
+  censustract
+  censusyear
+  district
+  geocode
+  location_type
+  location_number
+  location_kind
+  incorporated
+);
 
 tie %options, 'Tie::IxHash',
   'method' => { label   =>'Method',
@@ -23,6 +42,10 @@ tie %options, 'Tie::IxHash',
                         'type'  => 'select',
                         'multiple' => 1,
                         'options' => [ @location_fields ] },
+  'location_data'  => { 'label'   => 'Location data',
+                        'type'    => 'textarea' },
+  'package_data'   => { 'label'   => 'Package data',
+                        'type'    => 'textarea' },
   'success_regexp' => {
     label  => 'Success Regexp',
     default => '',
@@ -37,10 +60,16 @@ tie %options, 'Tie::IxHash',
   'notes'   => <<'END',
 Send an HTTP or HTTPS GET or POST to the specified URLs on customer location
 creation/update (action 'location') and package location assignment/change (action 'package').
-Always sends locationnum, action and any fields specified in the 'Include fields' 
-export option.  Action 'package' also sends pkgnum and old_pkgnum (because location
-changes usually instigate a pkgnum change.)  Action 'location' only sends on replace 
-if one of the specified fields changed.  Leave a URL blank to skip that action.
+Leave a URL blank to skip that action.
+Always sends locationnum, action, and fields specified in the export options.
+Action 'package' also sends pkgnum and change_pkgnum (the previous pkgnum,
+because location changes usually instigate a pkgnum change.)
+Simple field values can be selected in 'Include fields', and more complex
+values can be specified in the data field options as perl code using vars
+$cust_location, $cust_main and (where relevant) $cust_pkg.
+Action 'location' only sends on update if a specified field changed.
+Note that scheduled future package changes are currently sent when the change is scheduled
+(this may not be the case in future versions of this export.)
 For HTTPS support, <a href="http://search.cpan.org/dist/Crypt-SSLeay">Crypt::SSLeay</a>
 or <a href="http://search.cpan.org/dist/IO-Socket-SSL">IO::Socket::SSL</a> is required.
 END
@@ -51,11 +80,11 @@ END
 # we don't send blank custnum/prospectnum because we do a lot of inserting/replacing 
 #   with blank values and then immediately overwriting, but that unfortunately
 #   makes it difficult to indicate if this is the first time we've sent the location
-#   to the customer--hence we don't distinguish creation from update in the cgi vars
+#   to the customer--hence we don't distinguish insert from update in the cgi vars
 
 # gets invoked by FS::part_export::http _export_insert
 sub _export_command {
-  my( $self, $action, $cust_location ) = ( shift, shift, shift );
+  my( $self, $action, $cust_location ) = @_;
 
   # redundant--cust_location exports don't get invoked by cust_location->delete,
   # or by any status trigger, but just to be clear, since http export has other actions...
@@ -63,32 +92,55 @@ sub _export_command {
 
   $self->_http_queue_standard(
     'action' => 'location',
-    map { $_ => $cust_location->get($_) } ('locationnum', $self->_include_fields)
+    (map { $_ => $cust_location->get($_) } ('locationnum', $self->_include_fields)),
+    $self->_eval_replace('location_data',$cust_location,$cust_location->cust_main),
   );
 
 }
 
 sub _export_replace {
-  my( $self, $new, $old ) = ( shift, shift, shift );
+  my( $self, $new, $old ) = @_;
 
   my $changed = 0;
+
+  # even if they don't want custnum/prospectnum exported,
+  # inserts that lack custnum/prospectnum don't trigger exports,
+  # so we might not have previously reported these
+  $changed = 1 if $new->custnum && !$old->custnum;
+  $changed = 1 if $new->prospectnum && !$old->prospectnum;
+
   foreach my $field ($self->_include_fields) {
+    last if $changed;
     next if $new->get($field) eq $old->get($field);
     next if ($field =~ /latitude|longitude/) and $new->get($field) == $old->get($field);
     $changed = 1;
-    last;
   }
+
+  my %old_eval;
+  unless ($changed) {
+    %old_eval = $self->_eval_replace('location_data', $old, $old->cust_main),
+  }
+
+  my %eval = $self->_eval_replace('location_data', $new, $new->cust_main);
+
+  foreach my $key (keys %eval) {
+    last if $changed;
+    next if $eval{$key} eq $old_eval{$key};
+    $changed = 1;
+  }
+
   return '' unless $changed;
 
   $self->_http_queue_standard(
     'action' => 'location',
-    map { $_ => $new->get($_) } ('locationnum', $self->_include_fields)
+    (map { $_ => $new->get($_) } ('locationnum', $self->_include_fields)),
+    %eval,
   );
 }
 
 # not to be confused with export_pkg_change, which is for svcs
 sub export_pkg_location {
-  my( $self, $cust_pkg ) = ( shift, shift, shift );
+  my ($self, $cust_pkg) = @_;
 
   return '' unless $cust_pkg->locationnum;
 
@@ -98,6 +150,7 @@ sub export_pkg_location {
     'action' => 'package',
     (map { $_ => $cust_pkg->get($_) } ('pkgnum', 'change_pkgnum', 'locationnum')),
     (map { $_ => $cust_location->get($_) } $self->_include_fields),
+    $self->_eval_replace('package_data',$cust_location,$cust_pkg->cust_main,$cust_pkg),
   );
 }
 
@@ -128,4 +181,16 @@ sub _include_fields {
   split( /\s+/, $self->option('include_fields') );
 }
 
+sub _eval_replace {
+  my ($self,$option,$cust_location,$cust_main,$cust_pkg) = @_;
+  return
+    map {
+      /^\s*(\S+)\s+(.*)$/ or /()()/;
+      my( $field, $value_expression ) = ( $1, $2 );
+      my $value = eval $value_expression;
+      die $@ if $@;
+      ( $field, $value );
+    } split(/\n/, $self->option($option) );
+}
+
 1;

commit 70ff8d2c5f32a674d4ae75c8f018e630033ed645
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Tue Apr 19 12:16:15 2016 -0500

    RT#29296: API stuff: Add new locations [API bug fixes]

diff --git a/FS/FS/API.pm b/FS/FS/API.pm
index f9f28c1..5db29b2 100644
--- a/FS/FS/API.pm
+++ b/FS/FS/API.pm
@@ -776,7 +776,7 @@ containing the new values.
 =cut
 
 sub change_package_location {
-  my $self = shift;
+  my $class = shift;
   my %opt  = @_;
   return _shared_secret_error() unless _check_shared_secret($opt{'secret'});
 
@@ -784,13 +784,10 @@ sub change_package_location {
     or return { 'error' => 'Unknown pkgnum' };
 
   my %changeopt;
-  $changeopt{'pkgnum'} = $pkgnum;
 
-  my $cust_location = FS::cust_location->new({
-    'custnum' => $cust_pkg->custnum,
-    %location_hash,
-  });
-  $changeopt{'cust_location'} = $cust_location;
+  foreach my $field ('locationnum',FS::cust_location::API::API_editable_fields()) {
+    $changeopt{$field} = $opt{$field} if $opt{$field};
+  }
 
   $cust_pkg->API_change(%changeopt);
 }
diff --git a/FS/FS/cust_pkg/API.pm b/FS/FS/cust_pkg/API.pm
index 6e03b8e..7ef3f72 100644
--- a/FS/FS/cust_pkg/API.pm
+++ b/FS/FS/cust_pkg/API.pm
@@ -19,7 +19,7 @@ sub API_change {
   my %opt = @_;
 
   return { 'error' => 'Cannot change canceled package' }
-    if $self->cancel;
+    if $self->get('cancel');
 
   my %changeopt;
 

commit 94cdf97cf0a14a433c81c44bbeb76bf953db2dcc
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Mon Apr 18 21:47:51 2016 -0500

    RT#29296: API stuff: Add new locations [v3 reconcile, includes backport of FS::cust_pkg::API]

diff --git a/FS/FS/API.pm b/FS/FS/API.pm
index c595dae..f9f28c1 100644
--- a/FS/FS/API.pm
+++ b/FS/FS/API.pm
@@ -7,6 +7,7 @@ use FS::cust_location;
 use FS::cust_pay;
 use FS::cust_credit;
 use FS::cust_refund;
+use FS::cust_pkg;
 
 =head1 NAME
 
@@ -743,6 +744,57 @@ sub location_info {
   return \%return;
 }
 
+=item change_package_location
+
+Updates package location. Takes a list of keys and values 
+as paramters with the following keys: 
+
+pkgnum
+
+secret
+
+locationnum - pass this, or the following keys (don't pass both)
+
+address1
+
+address2
+
+city
+
+county
+
+state
+
+zip
+
+country
+
+On error, returns a hashref with an 'error' key.
+On success, returns a hashref with 'pkgnum' and 'locationnum' keys,
+containing the new values.
+
+=cut
+
+sub change_package_location {
+  my $self = shift;
+  my %opt  = @_;
+  return _shared_secret_error() unless _check_shared_secret($opt{'secret'});
+
+  my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $opt{'pkgnum'} })
+    or return { 'error' => 'Unknown pkgnum' };
+
+  my %changeopt;
+  $changeopt{'pkgnum'} = $pkgnum;
+
+  my $cust_location = FS::cust_location->new({
+    'custnum' => $cust_pkg->custnum,
+    %location_hash,
+  });
+  $changeopt{'cust_location'} = $cust_location;
+
+  $cust_pkg->API_change(%changeopt);
+}
+
 =item bill_now OPTION => VALUE, ...
 
 Bills a single customer now, in the same fashion as the "Bill now" link in the
diff --git a/FS/FS/cust_location.pm b/FS/FS/cust_location.pm
index 2736b50..818838e 100644
--- a/FS/FS/cust_location.pm
+++ b/FS/FS/cust_location.pm
@@ -722,7 +722,7 @@ sub label_prefix {
   $prefix;
 }
 
-=item county_state_county
+=item county_state_country
 
 Returns a string consisting of just the county, state and country.
 
diff --git a/FS/FS/cust_location/API.pm b/FS/FS/cust_location/API.pm
new file mode 100644
index 0000000..7d9140f
--- /dev/null
+++ b/FS/FS/cust_location/API.pm
@@ -0,0 +1,34 @@
+package FS::cust_location::API;
+
+use strict;
+
+# this gets used by FS::cust_main::API and FS::cust_pkg::API,
+# so don't use FS::cust_main or FS::cust_pkg here
+
+# some of these could probably be included, but in general,
+# probably a bad idea to expose everything in 
+# cust_main::Location::location_fields by default
+#
+#locationname
+#district
+#latitude
+#longitude
+#censustract
+#censusyear
+#geocode
+#coord_auto
+#addr_clean
+
+sub API_editable_fields {
+  return qw(
+    address1
+    address2
+    city
+    county
+    state
+    zip
+    country
+  );
+}
+
+1;
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 876a625..4221e34 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -1,5 +1,5 @@
 package FS::cust_pkg;
-use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::Sales_Mixin
+use base qw( FS::cust_pkg::API FS::otaker_Mixin FS::cust_main_Mixin FS::Sales_Mixin
              FS::contact_Mixin FS::location_Mixin
              FS::m2m_Common FS::option_Common );
 
diff --git a/FS/FS/cust_pkg/API.pm b/FS/FS/cust_pkg/API.pm
new file mode 100644
index 0000000..6e03b8e
--- /dev/null
+++ b/FS/FS/cust_pkg/API.pm
@@ -0,0 +1,61 @@
+package FS::cust_pkg::API;
+
+use strict;
+
+use FS::cust_location::API;
+
+sub API_getinfo {
+  my $self = shift;
+
+  +{ ( map { $_=>$self->$_ } $self->fields ),
+   };
+
+}
+
+# currently only handles location change...
+# eventually have it handle all sorts of package changes
+sub API_change {
+  my $self = shift;
+  my %opt = @_;
+
+  return { 'error' => 'Cannot change canceled package' }
+    if $self->cancel;
+
+  my %changeopt;
+
+  # update location--accepts raw fields OR location
+  my %location_hash;
+  foreach my $field (FS::cust_location::API::API_editable_fields()) {
+    $location_hash{$field} = $opt{$field} if $opt{$field};
+  }
+  return { 'error' => 'Cannot pass both locationnum and location fields' }
+    if $opt{'locationnum'} && %location_hash;
+
+  if (%location_hash) {
+    my $cust_location = FS::cust_location->new({
+      'custnum' => $self->custnum,
+      %location_hash,
+    });
+    $changeopt{'cust_location'} = $cust_location;
+  } elsif ($opt{'locationnum'}) {
+    $changeopt{'locationnum'} = $opt{'locationnum'};
+  }
+
+  # not quite "nothing changed" because passed changes might be identical to current record,
+  #   we don't currently check for that, don't want to imply that we do...but maybe we should?
+  return { 'error' => 'No changes passed to method' }
+    unless $changeopt{'cust_location'} || $changeopt{'locationnum'};
+
+  $changeopt{'keep_dates'} = 1;
+
+  my $pkg_or_error = $self->change( \%changeopt );
+  my $error = ref($pkg_or_error) ? '' : $pkg_or_error;
+
+  return { 'error' => $error } if $error;
+
+  # return all fields?  we don't yet expose them through FS::API
+  return { map { $_ => $pkg_or_error->get($_) } qw( pkgnum locationnum ) };
+
+}
+
+1;

commit a8d1f57eba87ab0f00ce5cd3ba8ef0cef956f68f
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Mon Apr 18 14:33:02 2016 -0500

    RT#29296: API stuff: Add new locations [v3 reconcile]

diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 6910977..6042cae 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -5584,7 +5584,7 @@ and customer address. Include units.',
       my @part_export =
         map { qsearch( 'part_export', {exporttype => $_ } ) }
           keys %{FS::part_export::export_info('cust_main')};
-      map { $_->exportnum => $_->exporttype.' to '.$_->machine } @part_export;
+      map { $_->exportnum => $_->exportname } @part_export;
     },
     'option_sub'  => sub {
       require FS::Record;
@@ -5593,7 +5593,7 @@ and customer address. Include units.',
         'part_export', { 'exportnum' => shift }
       );
       $part_export
-        ? $part_export->exporttype.' to '.$part_export->machine
+        ? $part_export->exportname
         : '';
     },
   },
@@ -5602,7 +5602,7 @@ and customer address. Include units.',
   {
     'key'         => 'cust_location-exports',
     'section'     => '',
-    'description' => 'Export(s) to call on cust_location insert, modification and deletion.',
+    'description' => 'Export(s) to call on cust_location insert or modification',
     'type'        => 'select-sub',
     'multiple'    => 1,
     'options_sub' => sub {
diff --git a/FS/FS/cust_location.pm b/FS/FS/cust_location.pm
index aad25a4..2736b50 100644
--- a/FS/FS/cust_location.pm
+++ b/FS/FS/cust_location.pm
@@ -244,20 +244,22 @@ sub insert {
   # cust_location exports
   #my $export_args = $options{'export_args'} || [];
 
-  my @part_export =
-    map qsearch( 'part_export', {exportnum=>$_} ),
-      $conf->config('cust_location-exports'); #, $agentnum
-
-  foreach my $part_export ( @part_export ) {
-    my $error = $part_export->export_insert($self); #, @$export_args);
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return "exporting to ". $part_export->exporttype.
-             " (transaction rolled back): $error";
+  # don't export custnum_pending cases, let follow-up replace handle that
+  if ($self->custnum || $self->prospectnum) {
+    my @part_export =
+      map qsearch( 'part_export', {exportnum=>$_} ),
+        $conf->config('cust_location-exports'); #, $agentnum
+
+    foreach my $part_export ( @part_export ) {
+      my $error = $part_export->export_insert($self); #, @$export_args);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
     }
   }
 
-
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
 }
@@ -306,20 +308,22 @@ sub replace {
   # cust_location exports
   #my $export_args = $options{'export_args'} || [];
 
-  my @part_export =
-    map qsearch( 'part_export', {exportnum=>$_} ),
-      $conf->config('cust_location-exports'); #, $agentnum
-
-  foreach my $part_export ( @part_export ) {
-    my $error = $part_export->export_replace($self, $old); #, @$export_args);
-    if ( $error ) {
-      $dbh->rollback if $oldAutoCommit;
-      return "exporting to ". $part_export->exporttype.
-             " (transaction rolled back): $error";
+  # don't export custnum_pending cases, let follow-up replace handle that
+  if ($self->custnum || $self->prospectnum) {
+    my @part_export =
+      map qsearch( 'part_export', {exportnum=>$_} ),
+        $conf->config('cust_location-exports'); #, $agentnum
+
+    foreach my $part_export ( @part_export ) {
+      my $error = $part_export->export_replace($self, $old); #, @$export_args);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
     }
   }
 
-
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
 }
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 140f961..876a625 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -397,6 +397,21 @@ sub insert {
 
   my $conf = new FS::Conf;
 
+  if ($self->locationnum) {
+    my @part_export =
+      map qsearch( 'part_export', {exportnum=>$_} ),
+        $conf->config('cust_location-exports'); #, $agentnum
+
+    foreach my $part_export ( @part_export ) {
+      my $error = $part_export->export_pkg_location($self); #, @$export_args);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+  }
+
   if ( $conf->config('ticket_system') && $options{ticket_subject} ) {
 
     #this init stuff is still inefficient, but at least its limited to 
@@ -642,6 +657,24 @@ sub replace {
     }
   }
 
+  # also run exports if removing locationnum?
+  #   doesn't seem to happen, and we don't export blank locationnum on insert...
+  if ($new->locationnum and ($new->locationnum != $old->locationnum)) {
+    my $conf = new FS::Conf;
+    my @part_export =
+      map qsearch( 'part_export', {exportnum=>$_} ),
+        $conf->config('cust_location-exports'); #, $agentnum
+
+    foreach my $part_export ( @part_export ) {
+      my $error = $part_export->export_pkg_location($new); #, @$export_args);
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "exporting to ". $part_export->exporttype.
+               " (transaction rolled back): $error";
+      }
+    }
+  }
+
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
 
diff --git a/FS/FS/part_export/cust_location_http.pm b/FS/FS/part_export/cust_location_http.pm
index 460080c..e2830db 100644
--- a/FS/FS/part_export/cust_location_http.pm
+++ b/FS/FS/part_export/cust_location_http.pm
@@ -4,27 +4,9 @@ use strict;
 use base qw( FS::part_export::http );
 use vars qw( %options %info );
 
-my @location_fields = qw(
-  custnum
-  address1
-  address2
-  city
-  state
-  zip
-  country
-  locationname
-  county
-  latitude
-  longitude
-  prospectnum
-  location_type
-  location_number
-  location_kind
-  geocode
-  district
-  censusyear
-  incorporated
-);
+use FS::cust_main::Location;
+
+my @location_fields = ( qw( custnum prospectnum ), FS::cust_main::Location->location_fields );
 
 tie %options, 'Tie::IxHash',
   'method' => { label   =>'Method',
@@ -32,10 +14,11 @@ tie %options, 'Tie::IxHash',
                 #options =>[qw(POST GET)],
                 options =>[qw(POST)],
                 default =>'POST' },
-  'url'    => { label   => 'URL', default => 'http://', },
-  'ssl_no_verify' => { label => 'Skip SSL certificate validation',
-                       type  => 'checkbox',
-                     },
+  'location_url'   => { label   => 'Location URL' },
+  'package_url'    => { label   => 'Package URL' },
+  'ssl_no_verify'  => { label => 'Skip SSL certificate validation',
+                        type  => 'checkbox',
+                      },
   'include_fields' => { 'label' => 'Include fields',
                         'type'  => 'select',
                         'multiple' => 1,
@@ -52,45 +35,39 @@ tie %options, 'Tie::IxHash',
   'options' => \%options,
   'no_machine' => 1,
   'notes'   => <<'END',
-Send an HTTP or HTTPS GET or POST to the specified URL on customer location insert
-or replace.  Always sends cgi fields action ('insert' or 'replace') and locationnum,
-as well as any fields specified below.  Only sends on replace if one of the
-specified fields changed.
+Send an HTTP or HTTPS GET or POST to the specified URLs on customer location
+creation/update (action 'location') and package location assignment/change (action 'package').
+Always sends locationnum, action and any fields specified in the 'Include fields' 
+export option.  Action 'package' also sends pkgnum and old_pkgnum (because location
+changes usually instigate a pkgnum change.)  Action 'location' only sends on replace 
+if one of the specified fields changed.  Leave a URL blank to skip that action.
 For HTTPS support, <a href="http://search.cpan.org/dist/Crypt-SSLeay">Crypt::SSLeay</a>
 or <a href="http://search.cpan.org/dist/IO-Socket-SSL">IO::Socket::SSL</a> is required.
 END
 );
 
-sub http_queue_standard {
-  my $self = shift;
-  $self->http_queue( '',
-    ( $self->option('ssl_no_verify') ? 'ssl_no_verify' : '' ),
-    $self->option('method'),
-    $self->option('url'),
-    $self->option('success_regexp'),
-    @_
-  );
-}
-
-sub _include_fields {
-  my $self = shift;
-  split( /\s+/, $self->option('include_fields') );
-}
+# we don't do anything on deletion because we generally don't delete locations
+#
+# we don't send blank custnum/prospectnum because we do a lot of inserting/replacing 
+#   with blank values and then immediately overwriting, but that unfortunately
+#   makes it difficult to indicate if this is the first time we've sent the location
+#   to the customer--hence we don't distinguish creation from update in the cgi vars
 
+# gets invoked by FS::part_export::http _export_insert
 sub _export_command {
   my( $self, $action, $cust_location ) = ( shift, shift, shift );
 
+  # redundant--cust_location exports don't get invoked by cust_location->delete,
+  # or by any status trigger, but just to be clear, since http export has other actions...
   return '' unless $action eq 'insert';
 
-  $self->http_queue_standard(
-    'action' => $action,
+  $self->_http_queue_standard(
+    'action' => 'location',
     map { $_ => $cust_location->get($_) } ('locationnum', $self->_include_fields)
   );
 
 }
 
-# currently, only custnum can change (when converting prospect to customer)
-# but using more generic logic for ease of adding other changeable fields
 sub _export_replace {
   my( $self, $new, $old ) = ( shift, shift, shift );
 
@@ -99,13 +76,56 @@ sub _export_replace {
     next if $new->get($field) eq $old->get($field);
     next if ($field =~ /latitude|longitude/) and $new->get($field) == $old->get($field);
     $changed = 1;
+    last;
   }
   return '' unless $changed;
 
-  $self->http_queue_standard(
-    'action' => 'replace',
+  $self->_http_queue_standard(
+    'action' => 'location',
     map { $_ => $new->get($_) } ('locationnum', $self->_include_fields)
   );
 }
 
+# not to be confused with export_pkg_change, which is for svcs
+sub export_pkg_location {
+  my( $self, $cust_pkg ) = ( shift, shift, shift );
+
+  return '' unless $cust_pkg->locationnum;
+
+  my $cust_location = $cust_pkg->cust_location;
+
+  $self->_http_queue_standard(
+    'action' => 'package',
+    (map { $_ => $cust_pkg->get($_) } ('pkgnum', 'change_pkgnum', 'locationnum')),
+    (map { $_ => $cust_location->get($_) } $self->_include_fields),
+  );
+}
+
+sub _http_queue_standard {
+  my $self = shift;
+  my %opts = @_;
+  my $url;
+  if ($opts{'action'} eq 'location') {
+    $url = $self->option('location_url');
+    return '' unless $url;
+  } elsif ($opts{'action'} eq 'package') {
+    $url = $self->option('package_url');
+    return '' unless $url;
+  } else {
+    return "Bad action ".$opts{'action'};
+  }
+  $self->http_queue( '',
+    ( $self->option('ssl_no_verify') ? 'ssl_no_verify' : '' ),
+    $self->option('method'),
+    $url,
+    $self->option('success_regexp'),
+    %opts
+  );
+}
+
+sub _include_fields {
+  my $self = shift;
+  split( /\s+/, $self->option('include_fields') );
+}
+
 1;

commit a76eba49dd827c1afefa62d96fa796ff39bb904d
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Fri Apr 15 11:43:59 2016 -0500

    RT#29296: API stuff: Add new locations

diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 8a353ce..6910977 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -5611,7 +5611,7 @@ and customer address. Include units.',
       my @part_export =
         map { qsearch( 'part_export', {exporttype => $_ } ) }
           keys %{FS::part_export::export_info('cust_location')};
-      map { $_->exportnum => $_->exporttype.' to '.$_->machine } @part_export;
+      map { $_->exportnum => $_->exportname } @part_export;
     },
     'option_sub'  => sub {
       require FS::Record;
@@ -5620,7 +5620,7 @@ and customer address. Include units.',
         'part_export', { 'exportnum' => shift }
       );
       $part_export
-        ? $part_export->exporttype.' to '.$part_export->machine
+        ? $part_export->exportname
         : '';
     },
   },
diff --git a/FS/FS/part_export/cust_location_http.pm b/FS/FS/part_export/cust_location_http.pm
new file mode 100644
index 0000000..460080c
--- /dev/null
+++ b/FS/FS/part_export/cust_location_http.pm
@@ -0,0 +1,111 @@
+package FS::part_export::cust_location_http;
+
+use strict;
+use base qw( FS::part_export::http );
+use vars qw( %options %info );
+
+my @location_fields = qw(
+  custnum
+  address1
+  address2
+  city
+  state
+  zip
+  country
+  locationname
+  county
+  latitude
+  longitude
+  prospectnum
+  location_type
+  location_number
+  location_kind
+  geocode
+  district
+  censusyear
+  incorporated
+);
+
+tie %options, 'Tie::IxHash',
+  'method' => { label   =>'Method',
+                type    =>'select',
+                #options =>[qw(POST GET)],
+                options =>[qw(POST)],
+                default =>'POST' },
+  'url'    => { label   => 'URL', default => 'http://', },
+  'ssl_no_verify' => { label => 'Skip SSL certificate validation',
+                       type  => 'checkbox',
+                     },
+  'include_fields' => { 'label' => 'Include fields',
+                        'type'  => 'select',
+                        'multiple' => 1,
+                        'options' => [ @location_fields ] },
+  'success_regexp' => {
+    label  => 'Success Regexp',
+    default => '',
+  },
+;
+
+%info = (
+  'svc'     => [qw( cust_location )],
+  'desc'    => 'Send an HTTP or HTTPS GET or POST request, for customer locations',
+  'options' => \%options,
+  'no_machine' => 1,
+  'notes'   => <<'END',
+Send an HTTP or HTTPS GET or POST to the specified URL on customer location insert
+or replace.  Always sends cgi fields action ('insert' or 'replace') and locationnum,
+as well as any fields specified below.  Only sends on replace if one of the
+specified fields changed.
+For HTTPS support, <a href="http://search.cpan.org/dist/Crypt-SSLeay">Crypt::SSLeay</a>
+or <a href="http://search.cpan.org/dist/IO-Socket-SSL">IO::Socket::SSL</a> is required.
+END
+);
+
+sub http_queue_standard {
+  my $self = shift;
+  $self->http_queue( '',
+    ( $self->option('ssl_no_verify') ? 'ssl_no_verify' : '' ),
+    $self->option('method'),
+    $self->option('url'),
+    $self->option('success_regexp'),
+    @_
+  );
+}
+
+sub _include_fields {
+  my $self = shift;
+  split( /\s+/, $self->option('include_fields') );
+}
+
+sub _export_command {
+  my( $self, $action, $cust_location ) = ( shift, shift, shift );
+
+  return '' unless $action eq 'insert';
+
+  $self->http_queue_standard(
+    'action' => $action,
+    map { $_ => $cust_location->get($_) } ('locationnum', $self->_include_fields)
+  );
+
+}
+
+# currently, only custnum can change (when converting prospect to customer)
+# but using more generic logic for ease of adding other changeable fields
+sub _export_replace {
+  my( $self, $new, $old ) = ( shift, shift, shift );
+
+  my $changed = 0;
+  foreach my $field ($self->_include_fields) {
+    next if $new->get($field) eq $old->get($field);
+    next if ($field =~ /latitude|longitude/) and $new->get($field) == $old->get($field);
+    $changed = 1;
+  }
+  return '' unless $changed;
+
+  $self->http_queue_standard(
+    'action' => 'replace',
+    map { $_ => $new->get($_) } ('locationnum', $self->_include_fields)
+  );
+}
+
+1;

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

Summary of changes:
 FS/FS/API.pm                            |   82 +++++++++++++
 FS/FS/Conf.pm                           |   10 +-
 FS/FS/cust_location.pm                  |   50 ++++----
 FS/FS/cust_pkg.pm                       |   35 +++++-
 FS/FS/cust_pkg/API.pm                   |   75 ++++++++++++
 FS/FS/part_export/cust_http.pm          |    2 +-
 FS/FS/part_export/cust_location_http.pm |  196 +++++++++++++++++++++++++++++++
 7 files changed, 420 insertions(+), 30 deletions(-)
 create mode 100644 FS/FS/cust_pkg/API.pm
 create mode 100644 FS/FS/part_export/cust_location_http.pm




More information about the freeside-commits mailing list