Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorIvan Kohler <ivan@freeside.biz>
Mon, 18 Sep 2017 21:57:12 +0000 (14:57 -0700)
committerIvan Kohler <ivan@freeside.biz>
Mon, 18 Sep 2017 21:57:12 +0000 (14:57 -0700)
FS/FS.pm
FS/FS/contact.pm
FS/FS/contact_import.pm [deleted file]
FS/FS/cust_main/Search.pm
FS/MANIFEST
Makefile
httemplate/elements/searchbar-cust_main.html
httemplate/misc/process/contact-import.cgi
httemplate/search/contact.html

index 134a34c..9575c3d 100644 (file)
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -67,6 +67,8 @@ L<FS::cust_main::Search> - Customer searching
 
 L<FS::cust_main::Import> - Batch customer importing
 
+L<FS::contact::Import> - Batch contact importing
+
 =head2 Database record classes
 
 L<FS::Record> - Database record base class
index 568d46f..44c5388 100644 (file)
@@ -10,6 +10,7 @@ use FS::Record qw( qsearch qsearchs dbh );
 use FS::Cursor;
 use FS::contact_phone;
 use FS::contact_email;
+use FS::contact::Import;
 use FS::queue;
 use FS::phone_type; #for cgi_contact_fields
 use FS::cust_contact;
diff --git a/FS/FS/contact_import.pm b/FS/FS/contact_import.pm
deleted file mode 100644 (file)
index d6cd690..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-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
index 27559d7..e8b1bde 100644 (file)
@@ -93,7 +93,7 @@ sub smart_search {
     my $phonenum = "$1$2$3";
     #my $extension = $4;
 
-    #cust_main phone numbers
+    #cust_main phone numbers and contact phone number
     push @cust_main, qsearch( {
       'table'   => 'cust_main',
       'hashref' => { %options },
@@ -102,20 +102,12 @@ sub smart_search {
                          join(' OR ', map "$_ = '$phonen'",
                                           qw( daytime night mobile fax )
                              ).
+                          " OR phonenum = '$phonenum' ".
                      ' ) '.
                      " AND $agentnums_sql", #agent virtualization
+      'addl_from' => ' left join cust_contact using (custnum) left join contact_phone using (contactnum) ',
     } );
 
-    #contact phone numbers
-    push @cust_main,
-      grep $agentnums_href->{$_->agentnum}, #agent virt
-        grep $_, #skip contacts that don't have cust_main records
-          map $_->contact->cust_main,
-            qsearch({
-                      'table'   => 'contact_phone',
-                      'hashref' => { 'phonenum' => $phonenum },
-                   });
-
     unless ( @cust_main || $phonen =~ /x\d+$/ ) { #no exact match
       #try looking for matches with extensions unless one was specified
 
@@ -136,28 +128,20 @@ sub smart_search {
   } 
   
   
-  if ( $search =~ /@/ ) { #email address
-
-      # invoicing email address
-      push @cust_main,
-        grep $agentnums_href->{$_->agentnum}, #agent virt
-         map $_->cust_main,
-             qsearch( {
-                        'table'     => 'cust_main_invoice',
-                        'hashref'   => { 'dest' => $search },
-                      }
-                    );
-
-      # contact email address
-      push @cust_main,
-        grep $agentnums_href->{$_->agentnum}, #agent virt
-          grep $_, #skip contacts that don't have cust_main records
-           map $_->contact->cust_main,
-             qsearch( {
-                        'table'     => 'contact_email',
-                        'hashref'   => { 'emailaddress' => $search },
-                      }
-                    );
+  if ( $search =~ /@/ ) { #email address from cust_main_invoice and contact_email
+
+    push @cust_main, qsearch( {
+      'table'   => 'cust_main',
+      'hashref' => { %options },
+      'extra_sql' => ( scalar(keys %options) ? ' AND ' : ' WHERE ' ).
+                     ' ( '.
+                         join(' OR ', map "$_ = '$search'",
+                                          qw( dest emailaddress )
+                             ).
+                     ' ) '.
+                     " AND $agentnums_sql", #agent virtualization
+      'addl_from' => ' left join cust_main_invoice using (custnum) left join cust_contact using (custnum) left join contact_email using (contactnum) ',
+    } );
 
   # custnum search (also try agent_custid), with some tweaking options if your
   # legacy cust "numbers" have letters
@@ -281,8 +265,8 @@ sub smart_search {
     } elsif ( ! $NameParse->parse($value) ) {
 
       my %name = $NameParse->components;
-      $first = $name{'given_name_1'} || $name{'initials_1'}; #wtf NameParse, Ed?
-      $last  = $name{'surname_1'};
+      $first = lc($name{'given_name_1'}) || $name{'initials_1'}; #wtf NameParse, Ed?
+      $last  = lc($name{'surname_1'});
 
     }
 
@@ -292,28 +276,18 @@ sub smart_search {
 
       #exact
       my $sql = scalar(keys %options) ? ' AND ' : ' WHERE ';
-      $sql .= "( LOWER(cust_main.last) = $q_last AND LOWER(cust_main.first) = $q_first )";
+      $sql .= "( (LOWER(cust_main.last) = $q_last AND LOWER(cust_main.first) = $q_first)
+                 OR (LOWER(contact.last) = $q_last AND LOWER(contact.first) = $q_first) )";
 
-      #cust_main
+      #cust_main and contacts
       push @cust_main, qsearch( {
         'table'     => 'cust_main',
-        'hashref'   => \%options,
+        'select'    => 'cust_main.*, cust_contact.*, contact.contactnum, contact.last as contact_last, contact.first as contact_first, contact.title',
+        'hashref'   => { %options },
         'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization
+        'addl_from' => ' left join cust_contact on cust_main.custnum = cust_contact.custnum left join contact using (contactnum) ',
       } );
 
-      #contacts
-      push @cust_main,
-        grep $agentnums_href->{$_->agentnum}, #agent virt
-          grep $_, #skip contacts that don't have cust_main records
-           map $_->cust_main,
-             qsearch( {
-                        'table'     => 'contact',
-                        'hashref'   => { 'first' => $first,
-                                          'last'  => $last,
-                                        }, 
-                      }
-                    );
-
       # or it just be something that was typed in... (try that in a sec)
 
     }
@@ -326,7 +300,9 @@ sub smart_search {
                 OR LOWER(cust_main.last)          = $q_value
                 OR LOWER(cust_main.company)       = $q_value
                 OR LOWER(cust_main.ship_company)  = $q_value
-            ";
+                OR LOWER(contact.first)           = $q_value
+                OR LOWER(contact.last)            = $q_value
+            )";
 
     #address1 (yes, it's a kludge)
     $sql .= "   OR EXISTS ( 
@@ -336,20 +312,12 @@ sub smart_search {
                           )"
       if $conf->exists('address1-search');
 
-    #contacts (look, another kludge)
-    $sql .= "   OR EXISTS ( SELECT 1 FROM contact
-                              WHERE (    LOWER(contact.first) = $q_value
-                                      OR LOWER(contact.last)  = $q_value
-                                    )
-                                AND contact.custnum IS NOT NULL
-                                AND contact.custnum = cust_main.custnum
-                          )
-              ) ";
-
     push @cust_main, qsearch( {
       'table'     => 'cust_main',
-      'hashref'   => \%options,
+      'select'    => 'cust_main.*, cust_contact.*, contact.contactnum, contact.last as contact_last, contact.first as contact_first, contact.title',
+      'hashref'   => { %options },
       'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization
+      'addl_from' => 'left join cust_contact on cust_main.custnum = cust_contact.custnum left join contact using (contactnum) ',
     } );
 
     #no exact match, trying substring/fuzzy
index 10f0cc2..81087de 100644 (file)
@@ -510,6 +510,7 @@ t/class_Common.t
 FS/category_Common.pm
 t/category_Common.t
 FS/contact.pm
+FS/contact/Import.pm
 t/contact.t
 FS/contact_phone.pm
 t/contact_phone.t
index c6eef91..a1e4566 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -236,7 +236,7 @@ perl-modules:
        " blib/lib/FS/part_export/*.pm;\
        perl -p -i -e "\
          s|%%%FREESIDE_CACHE%%%|${FREESIDE_CACHE}|g;\
-       " blib/lib/FS/cust_main/*.pm blib/lib/FS/cust_pkg/*.pm;\
+       " blib/lib/FS/cust_main/*.pm blib/lib/FS/cust_pkg/*.pm blib/lib/FS/contact/*.pm;\
        perl -p -i -e "\
          s|%%%FREESIDE_LOG%%%|${FREESIDE_LOG}|g;\
        " blib/lib/FS/Daemon/*.pm;\
index 5bfef48..7f8f9d8 100644 (file)
@@ -26,7 +26,7 @@ my $curuser = $FS::CurrentUser::CurrentUser;
 my $menu_position = $curuser->option('menu_position') || 'top';
 
 my $cust_width = 246;
-my $cust_label = '(cust #, name, company';
+my $cust_label = '(cust #, name, company, email';
 if ( $conf->exists('address1-search') ) {
   $cust_label .= ', address';
   $cust_width += 56;
index cbdcad4..108ee93 100644 (file)
@@ -5,6 +5,6 @@ die "access denied"
   unless $FS::CurrentUser::CurrentUser->access_right('Import');
 
 my $server =
-  new FS::UI::Web::JSRPC 'FS::contact_import::process_batch_import', $cgi;
+  new FS::UI::Web::JSRPC 'FS::contact::Import::process_batch_import', $cgi;
 
 </%init>
index 759f095..1615dfa 100644 (file)
@@ -29,6 +29,27 @@ my $email_sub = sub {
   join(', ', map $_->emailaddress, @contact_email);
 };
 
+my $work_phone_sub = sub {
+  my $contact = shift;
+  #can't because contactnum is in the wrong field
+  my @contact_workphone = qsearch('contact_phone', { 'contactnum' => $contact->contact_contactnum, 'phonetypenum' => '1' } );
+  join(', ', map $_->phonenum, @contact_workphone);
+};
+
+my $mobile_phone_sub = sub {
+  my $contact = shift;
+  #can't because contactnum is in the wrong field
+  my @contact_mobilephone = qsearch('contact_phone', { 'contactnum' => $contact->contact_contactnum, 'phonetypenum' => '3' } );
+  join(', ', map $_->phonenum, @contact_mobilephone);
+};
+
+my $home_phone_sub = sub {
+  my $contact = shift;
+  #can't because contactnum is in the wrong field
+  my @contact_homephone = qsearch('contact_phone', { 'contactnum' => $contact->contact_contactnum, 'phonetypenum' => '2' } );
+  join(', ', map $_->phonenum, @contact_homephone);
+};
+
 my $link; #for closure in this sub, we'll define it later
 my $contact_classname_sub = sub {
   my $contact = shift;
@@ -44,9 +65,9 @@ my $contact_classname_sub = sub {
   $X_contact->contact_classname;
 };
 
-my @header = ( 'First', 'Last', 'Title', 'Email', 'Type' );
-my @fields = ( 'first', 'last', 'title', $email_sub, $contact_classname_sub );
-my @links = ( '', '', '', '', '', );
+my @header = ( 'First', 'Last', 'Title', 'Email', 'Work Phone', 'Mobile Phone', 'Home Phone', 'Type' );
+my @fields = ( 'first', 'last', 'title', $email_sub, $work_phone_sub, $mobile_phone_sub, $home_phone_sub, $contact_classname_sub );
+my @links = ( '', '', '', '', '', '', '', '', );
 
 my $company_link = '';