*** empty log message ***
[freeside.git] / site_perl / cust_main.pm
index a89896d..b5cb271 100644 (file)
@@ -5,12 +5,15 @@ use vars qw($paymentserversecret $paymentserverport $paymentserverhost);
 package FS::cust_main;
 
 use strict;
-use vars qw(@ISA $conf $lpr $processor $xaction $E_NoErr);
+use vars qw( @ISA $conf $lpr $processor $xaction $E_NoErr $invoice_from
+             $smtpmachine );
 use Safe;
 use Carp;
 use Time::Local;
 use Date::Format;
 use Date::Manip;
+use Mail::Internet;
+use Mail::Header;
 use Business::CreditCard;
 use FS::UID qw( getotaker );
 use FS::Record qw( qsearchs qsearch );
@@ -31,6 +34,8 @@ use FS::cust_main_invoice;
 $FS::UID::callback{'FS::cust_main'} = sub { 
   $conf = new FS::Conf;
   $lpr = $conf->config('lpr');
+  $invoice_from = $conf->config('invoice_from');
+  $smtpmachine = $conf->config('smtpmachine');
 
   if ( $conf->exists('cybercash3.2') ) {
     require CCMckLib3_2;
@@ -172,18 +177,61 @@ sub table { 'cust_main'; }
 Adds this customer to the database.  If there is an error, returns the error,
 otherwise returns false.
 
-=item delete
+=item delete NEW_CUSTNUM
 
-Currently unimplemented.  Maybe cancel all of this customer's
-packages (cust_pkg)?
+This deletes the customer.  If there is an error, returns the error, otherwise
+returns false.
 
-I don't remove the customer record in the database because there would then
-be no record the customer ever existed (which is bad, no?)
+This will completely remove all traces of the customer record.  This is not
+what you want when a customer cancels service; for that, cancel all of the
+customer's packages (see L<FS::cust_pkg/cancel>).
+
+If the customer has any packages, you need to pass a new (valid) customer
+number for those packages to be transferred to.
+
+You can't delete a customer with invoices (see L<FS::cust_bill>),
+or credits (see L<FS::cust_credit>).
 
 =cut
 
 sub delete {
-   return "Can't (yet?) delete customers.";
+  my $self = shift;
+
+  if ( qsearch( 'cust_bill', { 'custnum' => $self->custnum } ) ) {
+    return "Can't delete a customer with invoices";
+  }
+  if ( qsearch( 'cust_credit', { 'custnum' => $self->custnum } ) ) {
+    return "Can't delete a customer with credits";
+  }
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my @cust_pkg = qsearch( 'cust_pkg', { 'custnum' => $self->custnum } );
+  if ( @cust_pkg ) {
+    my $new_custnum = shift;
+    return "Invalid new customer number: $new_custnum"
+      unless qsearchs( 'cust_main', { 'custnum' => $new_custnum } );
+    foreach my $cust_pkg ( @cust_pkg ) {
+      my %hash = $cust_pkg->hash;
+      $hash{'custnum'} = $new_custnum;
+      my $new_cust_pkg = new FS::cust_pkg ( \%hash );
+      my $error = $new_cust_pkg->replace($cust_pkg);
+      return $error if $error;
+    }
+  }
+  foreach my $cust_main_invoice (
+    qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } )
+  ) {
+    my $error = $cust_main_invoice->delete;
+    return $error if $error;
+  }
+
+  $self->SUPER::delete;
 }
 
 =item replace OLD_RECORD
@@ -211,7 +259,7 @@ sub check {
     || $self->ut_textn('address2')
     || $self->ut_text('city')
     || $self->ut_textn('county')
-    || $self->ut_text('state')
+    || $self->ut_textn('state')
     || $self->ut_phonen('daytime')
     || $self->ut_phonen('night')
     || $self->ut_phonen('fax')
@@ -224,10 +272,12 @@ sub check {
   return "Unknown referral"
     unless qsearchs( 'part_referral', { 'refnum' => $self->refnum } );
 
-  $self->getfield('last') =~ /^([\w \,\.\-\']+)$/ or return "Illegal last name";
+  $self->getfield('last') =~ /^([\w \,\.\-\']+)$/
+    or return "Illegal last name: ". $self->getfield('last');
   $self->setfield('last',$1);
 
-  $self->first =~ /^([\w \,\.\-\']+)$/ or return "Illegal first name";
+  $self->first =~ /^([\w \,\.\-\']+)$/
+    or return "Illegal first name: ". $self->first;
   $self->first($1);
 
   if ( $self->ss eq '' ) {
@@ -236,18 +286,18 @@ sub check {
     my $ss = $self->ss;
     $ss =~ s/\D//g;
     $ss =~ /^(\d{3})(\d{2})(\d{4})$/
-      or return "Illegal social security number";
+      or return "Illegal social security number: ". $self->ss;
     $self->ss("$1-$2-$3");
   }
 
-  $self->country =~ /^(\w\w)$/ or return "Illegal country";
+  $self->country =~ /^(\w\w)$/ or return "Illegal country: ". $self->country;
   $self->country($1);
   unless ( qsearchs('cust_main_county', {
     'country' => $self->country,
     'state'   => '',
    } ) ) {
-    return "Unknown state/county/country"
-      #" state ". $self->state. " county ". $self->county. " country ". $self->country
+    return "Unknown state/county/country: ".
+      $self->state. "/". $self->county. "/". $self->country
       unless qsearchs('cust_main_county',{
         'state'   => $self->state,
         'county'  => $self->county,
@@ -255,10 +305,12 @@ sub check {
       } );
   }
 
-  $self->zip =~ /^([\w\-]{5,10})$/ or return "Illegal zip";
+  $self->zip =~ /^\s*(\w[\w\-\s]{3,8}\w)\s*$/
+    or return "Illegal zip: ". $self->zip;
   $self->zip($1);
 
-  $self->payby =~ /^(CARD|BILL|COMP)$/ or return "Illegal payby";
+  $self->payby =~ /^(CARD|BILL|COMP)$/
+    or return "Illegal payby: ". $self->payby;
   $self->payby($1);
 
   if ( $self->payby eq 'CARD' ) {
@@ -266,21 +318,22 @@ sub check {
     my $payinfo = $self->payinfo;
     $payinfo =~ s/\D//g;
     $payinfo =~ /^(\d{13,16})$/
-      or return "Illegal credit card number";
+      or return "Illegal credit card number: ". $self->payinfo;
     $payinfo = $1;
     $self->payinfo($payinfo);
-    validate($payinfo) or return "Illegal credit card number";
+    validate($payinfo)
+      or return "Illegal credit card number: ". $self->payinfo;
     return "Unknown card type" if cardtype($self->payinfo) eq "Unknown";
 
   } elsif ( $self->payby eq 'BILL' ) {
 
     $error = $self->ut_textn('payinfo');
-    return "Illegal P.O. number" if $error;
+    return "Illegal P.O. number: ". $self->payinfo if $error;
 
   } elsif ( $self->payby eq 'COMP' ) {
 
     $error = $self->ut_textn('payinfo');
-    return "Illegal comp account issuer" if $error;
+    return "Illegal comp account issuer: ". $self->payinfo if $error;
 
   }
 
@@ -289,7 +342,7 @@ sub check {
     $self->paydate('');
   } else {
     $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/
-      or return "Illegal expiration date";
+      or return "Illegal expiration date: ". $self->paydate;
     if ( length($2) == 4 ) {
       $self->paydate("$2-$1-01");
     } elsif ( $2 > 97 ) { #should pry change to check for "this year"
@@ -303,11 +356,11 @@ sub check {
     $self->payname( $self->first. " ". $self->getfield('last') );
   } else {
     $self->payname =~ /^([\w \,\.\-\']+)$/
-      or return "Illegal billing name";
+      or return "Illegal billing name: ". $self->payname;
     $self->payname($1);
   }
 
-  $self->tax =~ /^(Y?)$/ or return "Illegal tax";
+  $self->tax =~ /^(Y?)$/ or return "Illegal tax: ". $self->tax;
   $self->tax($1);
 
   $self->otaker(getotaker);
@@ -585,11 +638,32 @@ sub collect {
            && ( $cust_bill->printed * 2592000 ) <= $since
       ) {
 
-        open(LPR, "|$lpr") or die "Can't open pipe to $lpr: $!";
-        print LPR $cust_bill->print_text; #( date )
-        close LPR
-          or die $! ? "Error closing $lpr: $!"
-                       : "Exit status $? from $lpr";
+        #my @print_text = $cust_bill->print_text; #( date )
+        my @invoicing_list = $self->invoicing_list;
+        if ( grep { $_ ne 'POST' } @invoicing_list ) { #email invoice
+          $ENV{SMTPHOSTS} = $smtpmachine;
+          $ENV{MAILADDRESS} = $invoice_from;
+          my $header = new Mail::Header ( [
+            "From: $invoice_from",
+            "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
+            "Sender: $invoice_from",
+            "Reply-To: $invoice_from",
+            "Date: ". time2str("%a, %d %b %Y %X %z", time),
+            "Subject: Invoice",
+          ] );
+          my $message = new Mail::Internet (
+            'Header' => $header,
+            'Body' => [ $cust_bill->print_text ], #( date)
+          );
+          $message->smtpsend or die "Can't send invoice email!"; #die?  warn?
+
+        } elsif ( ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list ) {
+          open(LPR, "|$lpr") or die "Can't open pipe to $lpr: $!";
+          print LPR $cust_bill->print_text; #( date )
+          close LPR
+            or die $! ? "Error closing $lpr: $!"
+                         : "Exit status $? from $lpr";
+        }
 
         my %hash = $cust_bill->hash;
         $hash{'printed'}++;
@@ -611,6 +685,7 @@ sub collect {
       my $error = $cust_pay->insert;
       return 'Error COMPing invnum #' . $cust_bill->invnum .
              ':' . $error if $error;
+
     } elsif ( $self->payby eq 'CARD' ) {
 
       if ( $options{'batch_card'} ne 'yes' ) {
@@ -620,8 +695,9 @@ sub collect {
         if ( $processor =~ /^cybercash/ ) {
 
           #fix exp. date for cybercash
-          $self->paydate =~ /^(\d+)\/\d*(\d{2})$/;
-          my $exp = "$1/$2";
+          #$self->paydate =~ /^(\d+)\/\d*(\d{2})$/;
+          $self->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+          my $exp = "$2/$1";
 
           my $paybatch = $cust_bill->invnum. 
                          '-' . time2str("%y%m%d%H%M%S", time);
@@ -712,6 +788,10 @@ sub collect {
       return "Unknown payment type ". $self->payby;
     }
 
+
+
+
+
   }
   '';
 
@@ -781,8 +861,13 @@ This interface may change in the future.
 sub invoicing_list {
   my( $self, $arrayref ) = @_;
   if ( $arrayref ) {
-    my @cust_main_invoice = 
-      qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+    my @cust_main_invoice;
+    if ( $self->custnum ) {
+      @cust_main_invoice = 
+        qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+    } else {
+      @cust_main_invoice = ();
+    }
     foreach my $cust_main_invoice ( @cust_main_invoice ) {
       #warn $cust_main_invoice->destnum;
       unless ( grep { $cust_main_invoice->address eq $_ } @{$arrayref} ) {
@@ -791,8 +876,12 @@ sub invoicing_list {
         warn $error if $error;
       }
     }
-    @cust_main_invoice =
-      qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+    if ( $self->custnum ) {
+      @cust_main_invoice = 
+        qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+    } else {
+      @cust_main_invoice = ();
+    }
     foreach my $address ( @{$arrayref} ) {
       unless ( grep { $address eq $_->address } @cust_main_invoice ) {
         my $cust_main_invoice = new FS::cust_main_invoice ( {
@@ -804,8 +893,12 @@ sub invoicing_list {
       } 
     }
   }
-  map { $_->address }
-    qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+  if ( $self->custnum ) {
+    map { $_->address }
+      qsearch( 'cust_main_invoice', { 'custnum' => $self->custnum } );
+  } else {
+    ();
+  }
 }
 
 =item check_invoicing_list ARRAYREF
@@ -835,12 +928,15 @@ sub check_invoicing_list {
 
 =head1 VERSION
 
-$Id: cust_main.pm,v 1.10 1999-01-25 12:26:09 ivan Exp $
+$Id: cust_main.pm,v 1.22 1999-04-15 16:44:36 ivan Exp $
 
 =head1 BUGS
 
 The delete method.
 
+The delete method should possibly take an FS::cust_main object reference
+instead of a scalar customer number.
+
 Bill and collect options should probably be passed as references instead of a
 list.
 
@@ -891,7 +987,44 @@ enable cybercash, cybercash v3 support, don't need to import
 FS::UID::{datasrc,checkruid} ivan@sisd.com 98-sep-19-21
 
 $Log: cust_main.pm,v $
-Revision 1.10  1999-01-25 12:26:09  ivan
+Revision 1.22  1999-04-15 16:44:36  ivan
+delete customers
+
+Revision 1.21  1999/04/14 07:47:53  ivan
+i18n fixes
+
+Revision 1.20  1999/04/10 08:35:14  ivan
+say what the unknown state/county/country are!
+
+Revision 1.19  1999/04/10 07:38:06  ivan
+_all_ check stuff with illegal data return the bad data too, to help debugging
+
+Revision 1.18  1999/04/10 06:54:11  ivan
+ditto
+
+Revision 1.17  1999/04/10 05:27:38  ivan
+display an illegal payby, to assist importing
+
+Revision 1.16  1999/04/07 14:32:19  ivan
+more &invoicing_list logic to skip searches when there is no custnum
+
+Revision 1.15  1999/04/07 13:41:54  ivan
+in &invoicing_list, don't search if there's no custnum yet
+
+Revision 1.14  1999/03/29 12:06:15  ivan
+buglet in email invoices fixed
+
+Revision 1.13  1999/02/28 20:09:03  ivan
+allow spaces in zip codes, for (at least) canada.  pointed out by
+Clayton Gray <clgray@bcgroup.net>
+
+Revision 1.12  1999/02/27 21:24:22  ivan
+parse paydate correctly for cybercash
+
+Revision 1.11  1999/02/23 08:09:27  ivan
+beginnings of one-screen new customer entry and some other miscellania
+
+Revision 1.10  1999/01/25 12:26:09  ivan
 yet more mod_perl stuff
 
 Revision 1.9  1999/01/18 09:22:41  ivan