summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Burger <burgerc@freeside.biz>2017-09-07 08:49:34 -0400
committerChristopher Burger <burgerc@freeside.biz>2017-09-08 10:54:45 -0400
commitf16d9ebb0e56f43e04242ed859d6055f98c74f15 (patch)
treec30fc3e073f47236f0a2e13268710302a71c32d1
parent0505597728b8a0b9b0f3b97c2e3c0607b869f92f (diff)
RT# 77167 - Added the ability to import a list of contacts
-rw-r--r--FS/FS/Mason.pm1
-rw-r--r--FS/FS/contact/Import.pm161
-rw-r--r--FS/FS/contact_import.pm161
-rw-r--r--httemplate/elements/menu.html1
-rw-r--r--httemplate/misc/contact-import.cgi102
-rw-r--r--httemplate/misc/process/contact-import.cgi10
6 files changed, 436 insertions, 0 deletions
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 4aeebbe..8862cf8 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 25a6967..9a3f7a4 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -510,6 +510,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>