[freeside-commits] branch master updated. 444ecdc59e1da312e559ffa8b8f34130cf2317b5

Christopher Burger burgerc at 420.am
Thu Sep 7 05:52:44 PDT 2017


The branch, master has been updated
       via  444ecdc59e1da312e559ffa8b8f34130cf2317b5 (commit)
       via  0c8c33892661b2774fd29423e3791d08a257d0fe (commit)
      from  0bca43616911f2b48d9a0617e70a3bf0f30b92db (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 444ecdc59e1da312e559ffa8b8f34130cf2317b5
Merge: 0c8c338 0bca436
Author: Christopher Burger <burgerc at freeside.biz>
Date:   Thu Sep 7 08:51:58 2017 -0400

    Merge branch 'master' of ssh://git.freeside.biz/home/git/freeside


commit 0c8c33892661b2774fd29423e3791d08a257d0fe
Author: Christopher Burger <burgerc at freeside.biz>
Date:   Thu Sep 7 08:49:34 2017 -0400

    RT# 77167 - Added the ability to import a list of contacts

diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 956ea62..7bdb605 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -262,6 +262,7 @@ if ( -e $addl_handler_use_file ) {
   use FS::cust_category;
   use FS::prospect_main;
   use FS::contact;
+  use FS::contact::Import;
   use FS::phone_type;
   use FS::svc_pbx;
   use FS::discount;
diff --git a/FS/FS/contact/Import.pm b/FS/FS/contact/Import.pm
new file mode 100644
index 0000000..26bdcfa
--- /dev/null
+++ b/FS/FS/contact/Import.pm
@@ -0,0 +1,161 @@
+package FS::contact::Import;
+
+use strict;
+use vars qw( $DEBUG ); #$conf );
+use Data::Dumper;
+use FS::Misc::DateTime qw( parse_datetime );
+use FS::Record qw( qsearchs );
+use FS::contact;
+use FS::cust_main;
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::contact::Import - Batch contact importing
+
+=head1 SYNOPSIS
+
+  use FS::contact::Import;
+
+  #import
+  FS::contact::Import::batch_import( {
+    file      => $file,      #filename
+    type      => $type,      #csv or xls
+    format    => $format,    #default
+    agentnum  => $agentnum,
+    job       => $job,       #optional job queue job, for progressbar updates
+    pkgbatch  => $pkgbatch,  #optional batch unique identifier
+  } );
+  die $error if $error;
+
+  #ajax helper
+  use FS::UI::Web::JSRPC;
+  my $server =
+    new FS::UI::Web::JSRPC 'FS::contact::Import::process_batch_import', $cgi;
+  print $server->process;
+
+=head1 DESCRIPTION
+
+Batch contact importing.
+
+=head1 SUBROUTINES
+
+=item process_batch_import
+
+Load a batch import as a queued JSRPC job
+
+=cut
+
+sub process_batch_import {
+  my $job = shift;
+  my $param = shift;
+  warn Dumper($param) if $DEBUG;
+  
+  my $files = $param->{'uploaded_files'}
+    or die "No files provided.\n";
+
+  my (%files) = map { /^(\w+):([\.\w]+)$/ ? ($1,$2):() } split /,/, $files;
+
+  my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc. '/';
+  #my $dir = '/usr/local/etc/freeside/cache.'. $FS::UID::datasrc. '/';
+  my $file = $dir. $files{'file'};
+
+  my $type;
+  if ( $file =~ /\.(\w+)$/i ) {
+    $type = lc($1);
+  } else {
+    #or error out???
+    warn "can't parse file type from filename $file; defaulting to CSV";
+    $type = 'csv';
+  }
+
+  my $error =
+    FS::contact::Import::batch_import( {
+      job      => $job,
+      file     => $file,
+      type     => $type,
+      agentnum => $param->{'agentnum'},
+      'format' => $param->{'format'},
+    } );
+
+  unlink $file;
+
+  die "$error\n" if $error;
+
+}
+
+=item batch_import
+
+=cut
+
+my %formatfields = (
+  'default'      => [ qw( custnum last first title comment selfservice_access emailaddress phonetypenum1 phonetypenum3 phonetypenum2 ) ],
+);
+
+sub _formatfields {
+  \%formatfields;
+}
+
+## not tested but maybe allow 2nd format to attach location in the future
+my %import_options = (
+  'table'         => 'contact',
+
+  'preinsert_callback'  => sub {
+    my($record, $param) = @_;
+    my @location_params = grep /^location\./, keys %$param;
+    if (@location_params) {
+      my $cust_location = FS::cust_location->new({
+          'custnum' => $record->custnum,
+      });
+      foreach my $p (@location_params) {
+        $p =~ /^location.(\w+)$/;
+        $cust_location->set($1, $param->{$p});
+      }
+
+      my $error = $cust_location->find_or_insert; # this avoids duplicates
+      return "error creating location: $error" if $error;
+      $record->set('locationnum', $cust_location->locationnum);
+    }
+    '';
+  },
+
+);
+
+sub _import_options {
+  \%import_options;
+}
+
+sub batch_import {
+  my $opt = shift;
+
+  my $iopt = _import_options;
+  $opt->{$_} = $iopt->{$_} foreach keys %$iopt;
+
+  my $format = delete $opt->{'format'};
+
+  my $formatfields = _formatfields();
+    die "unknown format $format" unless $formatfields->{$format};
+
+  my @fields;
+  foreach my $field ( @{ $formatfields->{$format} } ) {
+    push @fields, $field;
+  }
+
+  $opt->{'fields'} = \@fields;
+
+  FS::Record::batch_import( $opt );
+
+}
+
+=head1 BUGS
+
+Not enough documentation.
+
+=head1 SEE ALSO
+
+L<FS::contact>
+
+=cut
+
+1;
\ No newline at end of file
diff --git a/FS/FS/contact_import.pm b/FS/FS/contact_import.pm
new file mode 100644
index 0000000..d6cd690
--- /dev/null
+++ b/FS/FS/contact_import.pm
@@ -0,0 +1,161 @@
+package FS::contact_import;
+
+use strict;
+use vars qw( $DEBUG ); #$conf );
+use Data::Dumper;
+use FS::Misc::DateTime qw( parse_datetime );
+use FS::Record qw( qsearchs );
+use FS::contact;
+use FS::cust_main;
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::contact_import - Batch contact importing
+
+=head1 SYNOPSIS
+
+  use FS::contact_import;
+
+  #import
+  FS::contact_import::batch_import( {
+    file      => $file,      #filename
+    type      => $type,      #csv or xls
+    format    => $format,    #default
+    agentnum  => $agentnum,
+    job       => $job,       #optional job queue job, for progressbar updates
+    pkgbatch  => $pkgbatch,  #optional batch unique identifier
+  } );
+  die $error if $error;
+
+  #ajax helper
+  use FS::UI::Web::JSRPC;
+  my $server =
+    new FS::UI::Web::JSRPC 'FS::contact_import::process_batch_import', $cgi;
+  print $server->process;
+
+=head1 DESCRIPTION
+
+Batch contact importing.
+
+=head1 SUBROUTINES
+
+=item process_batch_import
+
+Load a batch import as a queued JSRPC job
+
+=cut
+
+sub process_batch_import {
+  my $job = shift;
+  my $param = shift;
+  warn Dumper($param) if $DEBUG;
+  
+  my $files = $param->{'uploaded_files'}
+    or die "No files provided.\n";
+
+  my (%files) = map { /^(\w+):([\.\w]+)$/ ? ($1,$2):() } split /,/, $files;
+
+  my $dir = '%%%FREESIDE_CACHE%%%/cache.'. $FS::UID::datasrc. '/';
+
+  my $file = $dir. $files{'file'};
+
+  my $type;
+  if ( $file =~ /\.(\w+)$/i ) {
+    $type = lc($1);
+  } else {
+    #or error out???
+    warn "can't parse file type from filename $file; defaulting to CSV";
+    $type = 'csv';
+  }
+
+  my $error =
+    FS::contact_import::batch_import( {
+      job      => $job,
+      file     => $file,
+      type     => $type,
+      agentnum => $param->{'agentnum'},
+      'format' => $param->{'format'},
+    } );
+
+  unlink $file;
+
+  die "$error\n" if $error;
+
+}
+
+=item batch_import
+
+=cut
+
+my %formatfields = (
+  'default'      => [ qw( custnum last first title comment selfservice_access emailaddress phonetypenum1 phonetypenum3 phonetypenum2 ) ],
+);
+
+sub _formatfields {
+  \%formatfields;
+}
+
+## not tested but maybe allow 2nd format to attach location in the future
+my %import_options = (
+  'table'         => 'contact',
+
+  'preinsert_callback'  => sub {
+    my($record, $param) = @_;
+    my @location_params = grep /^location\./, keys %$param;
+    if (@location_params) {
+      my $cust_location = FS::cust_location->new({
+          'custnum' => $record->custnum,
+      });
+      foreach my $p (@location_params) {
+        $p =~ /^location.(\w+)$/;
+        $cust_location->set($1, $param->{$p});
+      }
+
+      my $error = $cust_location->find_or_insert; # this avoids duplicates
+      return "error creating location: $error" if $error;
+      $record->set('locationnum', $cust_location->locationnum);
+    }
+    '';
+  },
+
+);
+
+sub _import_options {
+  \%import_options;
+}
+
+sub batch_import {
+  my $opt = shift;
+
+  my $iopt = _import_options;
+  $opt->{$_} = $iopt->{$_} foreach keys %$iopt;
+
+  my $format = delete $opt->{'format'};
+
+  my $formatfields = _formatfields();
+    die "unknown format $format" unless $formatfields->{$format};
+
+  my @fields;
+  foreach my $field ( @{ $formatfields->{$format} } ) {
+    push @fields, $field;
+  }
+
+  $opt->{'fields'} = \@fields;
+
+  FS::Record::batch_import( $opt );
+
+}
+
+=head1 BUGS
+
+Not enough documentation.
+
+=head1 SEE ALSO
+
+L<FS::contact>
+
+=cut
+
+1;
\ No newline at end of file
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index d963094..8541456 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -516,6 +516,7 @@ tie my %tools_importing, 'Tie::IxHash',
   'Package definitions'  => [ $fsurl.'misc/part_pkg-import.html', '' ],
   'Customer packages'    => [ $fsurl.'misc/cust_pkg-import.html', '' ],
   'Customer notes'       => [ $fsurl.'misc/cust_main_note-import.html', '' ],
+  'Customer Contacts'    => [ $fsurl.'misc/contact-import.cgi', '' ],
   'One-time charges'     => [ $fsurl.'misc/cust_main-import_charges.cgi', '' ],
   'Payments'             => [ $fsurl.'misc/cust_pay-import.cgi', '' ],
   'Credits'              => [ $fsurl.'misc/cust_credit-import.html', '' ],
diff --git a/httemplate/misc/contact-import.cgi b/httemplate/misc/contact-import.cgi
new file mode 100644
index 0000000..ae2e349
--- /dev/null
+++ b/httemplate/misc/contact-import.cgi
@@ -0,0 +1,102 @@
+<% include("/elements/header.html",'Batch Contacts Import') %>
+
+Import a file containing customer contact records.
+<BR><BR>
+
+<& /elements/form-file_upload.html,
+     'name'      => 'ContactImportForm',
+     'action'    => 'process/contact-import.cgi',
+     'num_files' => 1,
+     'fields'    => [ 'custbatch', 'format' ],
+     'message'   => 'Customer contacts import successful',
+     'onsubmit'  => "document.ContactImportForm.submitButton.disabled=true;",
+&>
+
+<% &ntable("#cccccc", 2) %>
+
+  <INPUT TYPE="hidden" NAME="custbatch" VALUE="<% $custbatch %>"%>
+
+  <TR>
+    <TH ALIGN="right">Format</TH>
+    <TD>
+      <SELECT NAME="format">
+        <OPTION VALUE="default" SELECTED>Default
+      </SELECT>
+    </TD>
+  </TR>
+
+  <% include( '/elements/file-upload.html',
+                'field' => 'file',
+                'label' => 'Filename',
+            )
+  %>
+
+  <TR>
+    <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+      <INPUT TYPE    = "submit"
+             NAME    = "submitButton"
+             ID      = "submitButton"
+             VALUE   = "Import file"
+      >
+    </TD>
+  </TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+
+Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets.  The file should have a .CSV or .XLS extension.
+<BR><BR>
+
+Default Format has the following field order:
+<BR>
+<i>custnum<%$req%>, last<%$req%>, first<%$req%>, title<%$req%>, comment, selfservice_access, emailaddress, workphone, mobilephone, homephone</i>
+<BR><BR>
+
+Field information:
+<BR>
+You must include a customer number and either a last name, first name or title.
+
+<ul>
+
+  <li><i>custnum</i>: This is the customer number of the customer the contact is attached to.</li>
+
+  <li><i>last</i>: Last name for contact.</li>
+
+  <li><i>first</i>: First name for contact.</li>
+
+  <li><i>title</i>: Optional title for contact.</li>
+
+  <li><i>comment</i>: Optional comment for contact.</li>
+
+  <li><i>selfservice_access</i>: Empty for no self service access or Y if granting self service access.</li>
+
+  <li><i>emailaddress</i>: Email address for contact.</li>
+
+  <li><i>workphone</i>: Work phone number for contact. Format xxxxxxxxxx</li>
+
+  <li><i>mobilephone</i>: Mobile phone number for contact. Format xxxxxxxxxx</li>
+
+  <li><i>homephone</i>: Home phone number for contact. Format xxxxxxxxxx</li>
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+my $req = qq!<font color="#ff0000">*</font>!;
+
+</%once>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $custbatch = time2str('webimport-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+</%init>
\ No newline at end of file
diff --git a/httemplate/misc/process/contact-import.cgi b/httemplate/misc/process/contact-import.cgi
new file mode 100644
index 0000000..cbdcad4
--- /dev/null
+++ b/httemplate/misc/process/contact-import.cgi
@@ -0,0 +1,10 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $server =
+  new FS::UI::Web::JSRPC 'FS::contact_import::process_batch_import', $cgi;
+
+</%init>

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

Summary of changes:
 FS/FS/Mason.pm                                     |    1 +
 FS/FS/contact/Import.pm                            |  161 ++++++++++++++++++++
 FS/FS/contact_import.pm                            |  161 ++++++++++++++++++++
 httemplate/elements/menu.html                      |    1 +
 httemplate/misc/contact-import.cgi                 |  102 +++++++++++++
 .../{part_pkg-import.html => contact-import.cgi}   |    2 +-
 6 files changed, 427 insertions(+), 1 deletion(-)
 create mode 100644 FS/FS/contact/Import.pm
 create mode 100644 FS/FS/contact_import.pm
 create mode 100644 httemplate/misc/contact-import.cgi
 copy httemplate/misc/process/{part_pkg-import.html => contact-import.cgi} (64%)




More information about the freeside-commits mailing list