This commit was manufactured by cvs2svn to create tag 'freeside_2_1_1'.
[freeside.git] / FS / FS / cust_main.pm
index 4e42f8d..fc781d2 100644 (file)
@@ -6,7 +6,6 @@ use strict;
 use base qw( FS::cust_main::Packages
              FS::cust_main::Billing FS::cust_main::Billing_Realtime
              FS::otaker_Mixin FS::payinfo_Mixin FS::cust_main_Mixin
-             FS::geocode_Mixin
              FS::Record
            );
 use vars qw( $DEBUG $me $conf
@@ -26,7 +25,7 @@ use Tie::IxHash;
 use Digest::MD5 qw(md5_base64);
 use Date::Format;
 #use Date::Manip;
-use File::Temp; #qw( tempfile );
+use File::Temp qw( tempfile );
 use Business::CreditCard 0.28;
 use Locale::Country;
 use FS::UID qw( getotaker dbh driver_name );
@@ -1656,14 +1655,12 @@ sub check {
     $self->payinfo($payinfo);
     $self->paycvv('');
 
-    unless ( $ignore_banned_card ) {
-      my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
-      if ( $ban ) {
-        return 'Banned ACH account: banned on '.
-               time2str('%a %h %o at %r', $ban->_date).
-               ' by '. $ban->otaker.
-               ' (ban# '. $ban->bannum. ')';
-      }
+    my $ban = qsearchs('banned_pay', $self->_banned_pay_hashref);
+    if ( $ban ) {
+      return 'Banned ACH account: banned on '.
+             time2str('%a %h %o at %r', $ban->_date).
+             ' by '. $ban->otaker.
+             ' (ban# '. $ban->bannum. ')';
     }
 
   } elsif ( $self->payby eq 'LECB' ) {
@@ -1723,7 +1720,6 @@ sub check {
     } else {
       return "Illegal expiration date: ". $self->paydate;
     }
-    $m = sprintf('%02d',$m);
     $self->paydate("$y-$m-01");
     my($nowm,$nowy)=(localtime(time))[4,5]; $nowm++; $nowy+=1900;
     return gettext('expired_card')
@@ -1783,10 +1779,22 @@ sub has_ship_address {
 =item location_hash
 
 Returns a list of key/value pairs, with the following keys: address1, adddress2,
-city, county, state, zip, country, and geocode.  The shipping address is used if present.
+city, county, state, zip, country.  The shipping address is used if present.
 
 =cut
 
+#geocode?  dependent on tax-ship_address config, not available in cust_location
+#mostly.  not yet then.
+
+sub location_hash {
+  my $self = shift;
+  my $prefix = $self->has_ship_address ? 'ship_' : '';
+
+  map { $_ => $self->get($prefix.$_) }
+      qw( address1 address2 city county state zip country geocode );
+      #fields that cust_location has
+}
+
 =item cust_location
 
 Returns all locations (see L<FS::cust_location>) for this customer.
@@ -1798,6 +1806,61 @@ sub cust_location {
   qsearch('cust_location', { 'custnum' => $self->custnum } );
 }
 
+=item location_label [ OPTION => VALUE ... ]
+
+Returns the label of the service location (see analog in L<FS::cust_location>) for this customer.
+
+Options are
+
+=over 4
+
+=item join_string
+
+used to separate the address elements (defaults to ', ')
+
+=item escape_function
+
+a callback used for escaping the text of the address elements
+
+=back
+
+=cut
+
+# false laziness with FS::cust_location::line
+
+sub location_label {
+  my $self = shift;
+  my %opt = @_;
+
+  my $separator = $opt{join_string} || ', ';
+  my $escape = $opt{escape_function} || sub{ shift };
+  my $line = '';
+  my $cydefault = FS::conf->new->config('countrydefault') || 'US';
+  my $prefix = length($self->ship_last) ? 'ship_' : '';
+
+  my $notfirst = 0;
+  foreach (qw ( address1 address2 ) ) {
+    my $method = "$prefix$_";
+    $line .= ($notfirst ? $separator : ''). &$escape($self->$method)
+      if $self->$method;
+    $notfirst++;
+  }
+  $notfirst = 0;
+  foreach (qw ( city county state zip ) ) {
+    my $method = "$prefix$_";
+    if ( $self->$method ) {
+      $line .= ' (' if $method eq 'county';
+      $line .= ($notfirst ? ' ' : $separator). &$escape($self->$method);
+      $line .= ' )' if $method eq 'county';
+      $notfirst++;
+    }
+  }
+  $line .= $separator. &$escape(code2country($self->country))
+    if $self->country ne $cydefault;
+
+  $line;
+}
+
 =item unsuspend
 
 Unsuspends all unflagged suspended packages (see L</unflagged_suspended_pkgs>
@@ -2270,7 +2333,7 @@ sub total_owed_date {
         AND _date <= $time
   ";
 
-  sprintf( "%.2f", $self->scalar_sql($sql) || 0 );
+  sprintf( "%.2f", $self->scalar_sql($sql) );
 
 }
 
@@ -2350,7 +2413,7 @@ sub total_unapplied_credits {
       WHERE custnum = $custnum
   ";
 
-  sprintf( "%.2f", $self->scalar_sql($sql) || 0 );
+  sprintf( "%.2f", $self->scalar_sql($sql) );
 
 }
 
@@ -2388,7 +2451,7 @@ sub total_unapplied_payments {
       WHERE custnum = $custnum
   ";
 
-  sprintf( "%.2f", $self->scalar_sql($sql) || 0 );
+  sprintf( "%.2f", $self->scalar_sql($sql) );
 
 }
 
@@ -2426,7 +2489,7 @@ sub total_unapplied_refunds {
       WHERE custnum = $custnum
   ";
 
-  sprintf( "%.2f", $self->scalar_sql($sql) || 0 );
+  sprintf( "%.2f", $self->scalar_sql($sql) );
 
 }
 
@@ -2484,7 +2547,7 @@ sub balance_date_range {
   my $self = shift;
   my $sql = 'SELECT SUM('. $self->balance_date_sql(@_).
             ') FROM cust_main WHERE custnum='. $self->custnum;
-  sprintf( '%.2f', $self->scalar_sql($sql) || 0 );
+  sprintf( '%.2f', $self->scalar_sql($sql) );
 }
 
 =item balance_pkgnum PKGNUM
@@ -3553,6 +3616,38 @@ Currently this only makes sense for "CCH" as DATA_VENDOR.
 
 =cut
 
+sub geocode {
+  my ($self, $data_vendor) = (shift, shift);  #always cch for now
+
+  my $geocode = $self->get('geocode');  #XXX only one data_vendor for geocode
+  return $geocode if $geocode;
+
+  my $prefix = ( $conf->exists('tax-ship_address') && length($self->ship_last) )
+               ? 'ship_'
+               : '';
+
+  my($zip,$plus4) = split /-/, $self->get("${prefix}zip")
+    if $self->country eq 'US';
+
+  $zip ||= '';
+  $plus4 ||= '';
+  #CCH specific location stuff
+  my $extra_sql = "AND plus4lo <= '$plus4' AND plus4hi >= '$plus4'";
+
+  my @cust_tax_location =
+    qsearch( {
+               'table'     => 'cust_tax_location', 
+               'hashref'   => { 'zip' => $zip, 'data_vendor' => $data_vendor },
+               'extra_sql' => $extra_sql,
+               'order_by'  => 'ORDER BY plus4hi',#overlapping with distinct ends
+             }
+           );
+  $geocode = $cust_tax_location[0]->geocode
+    if scalar(@cust_tax_location);
+
+  $geocode;
+}
+
 =item cust_status
 
 =item status
@@ -3727,24 +3822,6 @@ sub statuses {
   keys %statuscolor;
 }
 
-=item cust_status_sql
-
-Returns an SQL fragment to determine the status of a cust_main record, as a 
-string.
-
-=cut
-
-sub cust_status_sql {
-  my $sql = 'CASE';
-  for my $status ( FS::cust_main->statuses() ) {
-    my $method = $status.'_sql';
-    $sql .= ' WHEN ('.FS::cust_main->$method.") THEN '$status'";
-  }
-  $sql .= ' END';
-  return $sql;
-}
-
-
 =item prospect_sql
 
 Returns an SQL expression identifying prospective cust_main records (customers
@@ -4532,21 +4609,10 @@ sub process_bill_and_collect {
 sub _upgrade_data { #class method
   my ($class, %opts) = @_;
 
-  my @statements = (
+  foreach my $sql (
     'UPDATE h_cust_main SET paycvv = NULL WHERE paycvv IS NOT NULL',
-    'UPDATE cust_main SET signupdate = (SELECT signupdate FROM h_cust_main WHERE signupdate IS NOT NULL AND h_cust_main.custnum = cust_main.custnum ORDER BY historynum DESC LIMIT 1) WHERE signupdate IS NULL',
-  );
-  # fix yyyy-m-dd formatted paydates
-  if ( driver_name =~ /^mysql$/i ) {
-    push @statements,
-    "UPDATE cust_main SET paydate = CONCAT( SUBSTRING(paydate FROM 1 FOR 5), '0', SUBSTRING(paydate FROM 6) ) WHERE SUBSTRING(paydate FROM 7 FOR 1) = '-'";
-  }
-  else { # the SQL standard
-    push @statements, 
-    "UPDATE cust_main SET paydate = SUBSTRING(paydate FROM 1 FOR 5) || '0' || SUBSTRING(paydate FROM 6) WHERE SUBSTRING(paydate FROM 7 FOR 1) = '-'";
-  }
-
-  foreach my $sql ( @statements ) {
+    'UPDATE cust_main SET signupdate = (SELECT signupdate FROM h_cust_main WHERE h_cust_main.custnum = cust_main.custnum ORDER BY historynum ASC LIMIT 1) WHERE signupdate IS NULL',
+  ) {
     my $sth = dbh->prepare($sql) or die dbh->errstr;
     $sth->execute or die $sth->errstr;
   }