add latitude/longitude to prospects, customers and package locations, RT#15539
[freeside.git] / FS / FS / cust_main.pm
index 97b1172..f49eb69 100644 (file)
@@ -14,7 +14,7 @@ use vars qw( $DEBUG $me $conf
              @encrypted_fields
              $import
              $ignore_expired_card $ignore_illegal_zip $ignore_banned_card
-             $skip_fuzzyfiles @fuzzyfields
+             $skip_fuzzyfiles
              @paytypes
            );
 use Carp;
@@ -40,6 +40,7 @@ use FS::payby;
 use FS::cust_pkg;
 use FS::cust_svc;
 use FS::cust_bill;
+use FS::legacy_cust_bill;
 use FS::cust_pay;
 use FS::cust_pay_pending;
 use FS::cust_pay_void;
@@ -68,6 +69,7 @@ use FS::banned_pay;
 use FS::cust_main_note;
 use FS::cust_attachment;
 use FS::contact;
+use FS::Locales;
 
 # 1 is mostly method/subroutine entry and options
 # 2 traces progress of some operations
@@ -81,7 +83,6 @@ $ignore_illegal_zip = 0;
 $ignore_banned_card = 0;
 
 $skip_fuzzyfiles = 0;
-@fuzzyfields = ( 'first', 'last', 'company', 'address1' );
 
 @encrypted_fields = ('payinfo', 'paycvv');
 sub nohistory_fields { ('payinfo', 'paycvv'); }
@@ -210,6 +211,10 @@ phone (optional)
 
 phone (optional)
 
+=item mobile
+
+phone (optional)
+
 =item ship_first
 
 Shipping first name
@@ -256,6 +261,10 @@ phone (optional)
 
 phone (optional)
 
+=item ship_mobile
+
+phone (optional)
+
 =item payby
 
 Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
@@ -328,6 +337,10 @@ Discourage individual CDR printing, empty or `Y'
 
 Allow self-service editing of ticket subjects, empty or 'Y'
 
+=item calling_list_exempt
+
+Do not call, empty or 'Y'
+
 =back
 
 =head1 METHODS
@@ -1468,6 +1481,29 @@ sub replace {
       && length($self->get($pre.'zip')) >= 10;
   }
 
+  for my $pre ( grep $old->get($_.'coord_auto'), ( '', 'ship_' ) ) {
+
+    $self->set($pre.'coord_auto', '') && next
+      if $self->get($pre.'latitude') && $self->get($pre.'longitude')
+      && (    $self->get($pre.'latitude')  != $old->get($pre.'latitude')
+           || $self->get($pre.'longitude') != $old->get($pre.'longitude')
+         );
+
+    $self->set_coord($pre)
+      if $old->get($pre.'address1') ne $self->get($pre.'address1')
+      || $old->get($pre.'city')     ne $self->get($pre.'city')
+      || $old->get($pre.'state')    ne $self->get($pre.'state')
+      || $old->get($pre.'country')  ne $self->get($pre.'country');
+
+  }
+
+  $self->set_coord
+    if ! $self->coord_auto && ! $self->latitude && ! $self->longitude;
+
+  $self->set_coord('ship_')
+    if $self->has_ship_address && ! $self->ship_coord_auto
+    && ! $self->ship_latitude && ! $self->ship_longitude;
+
   local($ignore_expired_card) = 1
     if $old->payby  =~ /^(CARD|DCRD)$/
     && $self->payby =~ /^(CARD|DCRD)$/
@@ -1615,6 +1651,7 @@ Used by insert & replace to update the fuzzy search cache
 
 =cut
 
+use FS::cust_main::Search;
 sub queue_fuzzyfiles_update {
   my $self = shift;
 
@@ -1629,16 +1666,16 @@ sub queue_fuzzyfiles_update {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
-  my $error = $queue->insert( map $self->getfield($_), @fuzzyfields );
+  my $queue = new FS::queue { 'job' => 'FS::cust_main::Search::append_fuzzyfiles' };
+  my $error = $queue->insert( map $self->getfield($_), @FS::cust_main::Search::fuzzyfields );
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return "queueing job (transaction rolled back): $error";
   }
 
   if ( $self->ship_last ) {
-    $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' };
-    $error = $queue->insert( map $self->getfield("ship_$_"), @fuzzyfields );
+    $queue = new FS::queue { 'job' => 'FS::cust_main::Search::append_fuzzyfiles' };
+    $error = $queue->insert( map $self->getfield("ship_$_"), @FS::cust_main::Search::fuzzyfields );
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return "queueing job (transaction rolled back): $error";
@@ -1682,6 +1719,9 @@ sub check {
     || $self->ut_textn('county')
     || $self->ut_textn('state')
     || $self->ut_country('country')
+    || $self->ut_coordn('latitude')
+    || $self->ut_coordn('longitude')
+    || $self->ut_enum('coord_auto', [ '', 'Y' ])
     || $self->ut_anything('comments')
     || $self->ut_numbern('referral_custnum')
     || $self->ut_textn('stateid')
@@ -1692,8 +1732,13 @@ sub check {
     || $self->ut_floatn('credit_limit')
     || $self->ut_numbern('billday')
     || $self->ut_enum('edit_subject', [ '', 'Y' ] )
+    || $self->ut_enum('calling_list_exempt', [ '', 'Y' ] )
+    || $self->ut_enum('locale', [ '', FS::Locales->locales ])
   ;
 
+  $self->set_coord
+    unless $import || ($self->latitude && $self->longitude);
+
   #barf.  need message catalogs.  i18n.  etc.
   $error .= "Please select an advertising source."
     if $error =~ /^Illegal or empty \(numeric\) refnum: /;
@@ -1745,9 +1790,10 @@ sub check {
   }
 
   $error =
-    $self->ut_phonen('daytime', $self->country)
-    || $self->ut_phonen('night', $self->country)
-    || $self->ut_phonen('fax', $self->country)
+       $self->ut_phonen('daytime', $self->country)
+    || $self->ut_phonen('night',   $self->country)
+    || $self->ut_phonen('fax',     $self->country)
+    || $self->ut_phonen('mobile',  $self->country)
   ;
   return $error if $error;
 
@@ -1757,7 +1803,7 @@ sub check {
   }
 
   if ( $conf->exists('cust_main-require_phone')
-       && ! length($self->daytime) && ! length($self->night)
+       && ! length($self->daytime) && ! length($self->night) && ! length($self->mobile)
      ) {
 
     my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/
@@ -1766,8 +1812,12 @@ sub check {
     my $night_label = FS::Msgcat::_gettext('night') =~ /^(night)?$/
                         ? 'Night Phone'
                         : FS::Msgcat::_gettext('night');
-  
-    return "$daytime_label or $night_label is required"
+
+    my $mobile_label = FS::Msgcat::_gettext('mobile') =~ /^(mobile)?$/
+                        ? 'Mobile Phone'
+                        : FS::Msgcat::_gettext('mobile');
+
+    return "$daytime_label, $night_label or $mobile_label is required"
   
   }
 
@@ -1786,9 +1836,15 @@ sub check {
       || $self->ut_textn('ship_county')
       || $self->ut_textn('ship_state')
       || $self->ut_country('ship_country')
+      || $self->ut_coordn('ship_latitude')
+      || $self->ut_coordn('ship_longitude')
+      || $self->ut_enum('ship_coord_auto', [ '', 'Y' ] )
     ;
     return $error if $error;
 
+    $self->set_coord('ship_')
+      unless $import || ($self->ship_latitude && $self->ship_longitude);
+
     #false laziness with above
     unless ( qsearchs('cust_main_county', {
       'country' => $self->ship_country,
@@ -1805,9 +1861,10 @@ sub check {
     #eofalse
 
     $error =
-      $self->ut_phonen('ship_daytime', $self->ship_country)
-      || $self->ut_phonen('ship_night', $self->ship_country)
-      || $self->ut_phonen('ship_fax', $self->ship_country)
+         $self->ut_phonen('ship_daytime', $self->ship_country)
+      || $self->ut_phonen('ship_night',   $self->ship_country)
+      || $self->ut_phonen('ship_fax',     $self->ship_country)
+      || $self->ut_phonen('ship_mobile',  $self->ship_country)
     ;
     return $error if $error;
 
@@ -1933,8 +1990,7 @@ sub check {
     if ( $conf->exists('cust_main-require-bank-branch') ) {
       $payinfo =~ /^(\d+)\@(\d+)\.(\d+)$/ or return 'invalid echeck account@branch.bank';
       $payinfo = "$1\@$2.$3";
-    }
-    elsif ( $conf->exists('echeck-nonus') ) {
+    } elsif ( $conf->exists('echeck-nonus') ) {
       $payinfo =~ /^(\d+)\@(\d+)$/ or return 'invalid echeck account@aba';
       $payinfo = "$1\@$2";
     } else {
@@ -2058,7 +2114,8 @@ Returns a list of fields which have ship_ duplicates.
 sub addr_fields {
   qw( last first company
       address1 address2 city county state zip country
-      daytime night fax
+      latitude longitude
+      daytime night fax mobile
     );
 }
 
@@ -2461,6 +2518,7 @@ sub batch_card {
     'status' => 'O',
     'payby'  => FS::payby->payby2payment($payby),
   );
+  $pay_batch{agentnum} = $self->agentnum if $conf->exists('batch-spoolagent');
 
   my $pay_batch = qsearchs( 'pay_batch', \%pay_batch );
 
@@ -2478,8 +2536,9 @@ sub batch_card {
       'custnum'  => $self->custnum,
   } );
 
-  foreach (qw( address1 address2 city state zip country payby payinfo paydate
-               payname )) {
+  foreach (qw( address1 address2 city state zip country latitude longitude
+               payby payinfo paydate payname ))
+  {
     $options{$_} = '' unless exists($options{$_});
   }
 
@@ -3554,6 +3613,25 @@ sub open_cust_bill {
 
 }
 
+=item legacy_cust_bill [ OPTION => VALUE... | EXTRA_QSEARCH_PARAMS_HASHREF ]
+
+Returns all the legacy invoices (see L<FS::legacy_cust_bill>) for this customer.
+
+=cut
+
+sub legacy_cust_bill {
+  my $self = shift;
+
+  #return $self->num_legacy_cust_bill unless wantarray;
+
+  map { $_ } #behavior of sort undefined in scalar context
+    sort { $a->_date <=> $b->_date }
+      qsearch({ 'table'    => 'legacy_cust_bill',
+                'hashref'  => { 'custnum' => $self->custnum, },
+                'order_by' => 'ORDER BY _date ASC',
+             });
+}
+
 =item cust_statement [ OPTION => VALUE... | EXTRA_QSEARCH_PARAMS_HASHREF ]
 
 Returns all the statements (see L<FS::cust_statement>) for this customer.
@@ -3788,6 +3866,9 @@ sub display_custnum {
   my $self = shift;
   if ( $conf->exists('cust_main-default_agent_custid') && $self->agent_custid ){
     return $self->agent_custid;
+  } elsif ( $conf->config('cust_main-custnum-display_prefix') ) {
+    return $conf->config('cust_main-custnum-display_prefix').
+           sprintf('%08d', $self->custnum)
   } else {
     return $self->custnum;
   }
@@ -4371,42 +4452,6 @@ sub search {
 
 =over 4
 
-=item append_fuzzyfiles FIRSTNAME LASTNAME COMPANY ADDRESS1
-
-=cut
-
-use FS::cust_main::Search;
-sub append_fuzzyfiles {
-  #my( $first, $last, $company ) = @_;
-
-  FS::cust_main::Search::check_and_rebuild_fuzzyfiles();
-
-  use Fcntl qw(:flock);
-
-  my $dir = $FS::UID::conf_dir. "/cache.". $FS::UID::datasrc;
-
-  foreach my $field (@fuzzyfields) {
-    my $value = shift;
-
-    if ( $value ) {
-
-      open(CACHE,">>$dir/cust_main.$field")
-        or die "can't open $dir/cust_main.$field: $!";
-      flock(CACHE,LOCK_EX)
-        or die "can't lock $dir/cust_main.$field: $!";
-
-      print CACHE "$value\n";
-
-      flock(CACHE,LOCK_UN)
-        or die "can't unlock $dir/cust_main.$field: $!";
-      close CACHE;
-    }
-
-  }
-
-  1;
-}
-
 =item batch_charge
 
 =cut