documentation updates from jason
[freeside.git] / FS / FS / cust_main.pm
index 25b6b9f..7b75bea 100644 (file)
@@ -6,16 +6,16 @@ package FS::cust_main;
 
 use strict;
 use vars qw( @ISA $conf $lpr $processor $xaction $E_NoErr $invoice_from
-             $smtpmachine );
+             $smtpmachine $Debug );
 use Safe;
 use Carp;
 use Time::Local;
 use Date::Format;
-use Date::Manip;
+#use Date::Manip;
 use Mail::Internet;
 use Mail::Header;
 use Business::CreditCard;
-use FS::UID qw( getotaker );
+use FS::UID qw( getotaker dbh );
 use FS::Record qw( qsearchs qsearch );
 use FS::cust_pkg;
 use FS::cust_bill;
@@ -27,9 +27,13 @@ use FS::part_referral;
 use FS::cust_main_county;
 use FS::agent;
 use FS::cust_main_invoice;
+use FS::prepay_credit;
 
 @ISA = qw( FS::Record );
 
+$Debug = 0;
+#$Debug = 1;
+
 #ask FS::UID to run this stuff for us later
 $FS::UID::callback{'FS::cust_main'} = sub { 
   $conf = new FS::Conf;
@@ -145,9 +149,9 @@ FS::Record.  The following fields are currently supported:
 
 =item fax - phone (optional)
 
-=item payby - `CARD' (credit cards), `BILL' (billing), or `COMP' (free)
+=item payby - `CARD' (credit cards), `BILL' (billing), `COMP' (free), or `PREPAY' (special billing type: applies a credit - see L<FS::prepay_credit> and sets billing type to BILL)
 
-=item payinfo - card number, P.O.#, or comp issuer (4-8 lowercase alphanumerics; think username)
+=item payinfo - card number, P.O., comp issuer (4-8 lowercase alphanumerics; think username) or prepayment identifier (see L<FS::prepay_credit>)
 
 =item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
 
@@ -179,6 +183,107 @@ sub table { 'cust_main'; }
 Adds this customer to the database.  If there is an error, returns the error,
 otherwise returns false.
 
+There is a special insert mode in which you pass a data structure to the insert
+method containing FS::cust_pkg and FS::svc_I<tablename> objects.  When
+running under a transactional database, all records are inserted atomicly, or
+the transaction is rolled back.  There should be a better explanation of this,
+but until then, here's an example:
+
+  use Tie::RefHash;
+  tie %hash, 'Tie::RefHash'; #this part is important
+  %hash = {
+    $cust_pkg => [ $svc_acct ],
+  };
+  $cust_main->insert( \%hash );
+
+=cut
+
+sub insert {
+  my $self = shift;
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $amount = 0;
+  my $seconds = 0;
+  if ( $self->payby eq 'PREPAY' ) {
+    $self->payby('BILL');
+    my $prepay_credit = qsearchs(
+      'prepay_credit',
+      { 'identifier' => $self->payinfo },
+      '',
+      'FOR UPDATE'
+    );
+    warn "WARNING: can't find pre-found prepay_credit: ". $self->payinfo
+      unless $prepay_credit;
+    $amount = $prepay_credit->amount;
+    $seconds = $prepay_credit->seconds;
+    my $error = $prepay_credit->delete;
+    if ( $error ) {
+      $dbh->rollback;
+      return $error;
+    }
+  }
+
+  my $error = $self->SUPER::insert;
+  if ( $error ) {
+    $dbh->rollback;
+    return $error;
+  }
+
+  if ( @_ ) {
+    my $cust_pkgs = shift;
+    foreach my $cust_pkg ( keys %$cust_pkgs ) {
+      $cust_pkg->custnum( $self->custnum );
+      $error = $cust_pkg->insert;
+      if ( $error ) {
+        $dbh->rollback;
+        return $error;
+      }
+      foreach my $svc_something ( @{$cust_pkgs->{$cust_pkg}} ) {
+        $svc_something->pkgnum( $cust_pkg->pkgnum );
+        if ( $seconds && $svc_something->isa('FS::svc_acct') ) {
+          $svc_something->seconds( $svc_something->seconds + $seconds );
+          $seconds = 0;
+        }
+        $error = $svc_something->insert;
+        if ( $error ) {
+          $dbh->rollback;
+          return $error;
+        }
+      }
+    }
+  }
+
+  if ( $seconds ) {
+    $dbh->rollback;
+    return "No svc_acct record to apply pre-paid time";
+  }
+
+  if ( $amount ) {
+    my $cust_credit = new FS::cust_credit {
+      'custnum' => $self->custnum,
+      'amount'  => $amount,
+    };
+    $error = $cust_credit->insert;
+    if ( $error ) {
+      $dbh->rollback;
+      return $error;
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr;
+  '';
+
+}
+
 =item delete NEW_CUSTNUM
 
 This deletes the customer.  If there is an error, returns the error, otherwise
@@ -262,9 +367,6 @@ sub check {
     || $self->ut_text('city')
     || $self->ut_textn('county')
     || $self->ut_textn('state')
-    || $self->ut_phonen('daytime')
-    || $self->ut_phonen('night')
-    || $self->ut_phonen('fax')
   ;
   return $error if $error;
 
@@ -307,11 +409,18 @@ sub check {
       } );
   }
 
-  $self->zip =~ /^\s*(\w[\w\-\s]{3,8}\w)\s*$/
+  $error =
+    $self->ut_phonen('daytime', $self->country)
+    || $self->ut_phonen('night', $self->country)
+    || $self->ut_phonen('fax', $self->country)
+  ;
+  return $error if $error;
+
+  $self->zip =~ /^\s*(\w[\w\-\s]{2,8}\w)\s*$/
     or return "Illegal zip: ". $self->zip;
   $self->zip($1);
 
-  $self->payby =~ /^(CARD|BILL|COMP)$/
+  $self->payby =~ /^(CARD|BILL|COMP|PREPAY)$/
     or return "Illegal payby: ". $self->payby;
   $self->payby($1);
 
@@ -337,10 +446,21 @@ sub check {
     $error = $self->ut_textn('payinfo');
     return "Illegal comp account issuer: ". $self->payinfo if $error;
 
+  } elsif ( $self->payby eq 'PREPAY' ) {
+
+    my $payinfo = $self->payinfo;
+    $payinfo =~ s/\W//g; #anything else would just confuse things
+    $self->payinfo($payinfo);
+    $error = $self->ut_alpha('payinfo');
+    return "Illegal prepayment identifier: ". $self->payinfo if $error;
+    return "Unknown prepayment identifier"
+      unless qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
+
   }
 
-  if ( $self->paydate eq '' ) {
-    return "Expriation date required" unless $self->payby eq 'BILL';
+  if ( $self->paydate eq '' || $self->paydate eq '-' ) {
+    return "Expriation date required"
+      unless $self->payby eq 'BILL' || $self->payby eq 'PREPAY';
     $self->paydate('');
   } else {
     $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/
@@ -389,10 +509,16 @@ Returns all non-cancelled packages (see L<FS::cust_pkg>) for this customer.
 
 sub ncancelled_pkgs {
   my $self = shift;
-  qsearch( 'cust_pkg', {
-    'custnum' => $self->custnum,
-    'cancel'  => '',
-  });
+  @{ [ # force list context
+    qsearch( 'cust_pkg', {
+      'custnum' => $self->custnum,
+      'cancel'  => '',
+    }),
+    qsearch( 'cust_pkg', {
+      'custnum' => $self->custnum,
+      'cancel'  => 0,
+    }),
+  ] };
 }
 
 =item bill OPTIONS
@@ -479,7 +605,8 @@ sub bill {
         warn "Error reval-ing part_pkg->recur pkgpart ",
              $part_pkg->pkgpart, ": $@";
       } else {
-        #change this bit to use Date::Manip?
+        #change this bit to use Date::Manip? CAREFUL with timezones (see
+        # mailing list archive)
         #$sdate=$cust_pkg->bill || time;
         #$sdate=$cust_pkg->bill || $time;
         $sdate = $cust_pkg->bill || $cust_pkg->setup || $time;
@@ -493,9 +620,9 @@ sub bill {
       }
     }
 
-    warn "setup is undefinded" unless defined($setup);
-    warn "recur is undefinded" unless defined($recur);
-    warn "cust_pkg bill is undefinded" unless defined($cust_pkg->bill);
+    warn "setup is undefined" unless defined($setup);
+    warn "recur is undefined" unless defined($recur);
+    warn "cust_pkg bill is undefined" unless defined($cust_pkg->bill);
 
     if ( $cust_pkg_mod_flag ) {
       $error=$cust_pkg->replace($old_cust_pkg);
@@ -602,6 +729,7 @@ sub collect {
   my $invoice_time = $options{'invoice_time'} || time;
 
   my $total_owed = $self->balance;
+  warn "collect: total owed $total_owed " if $Debug;
   return '' unless $total_owed > 0; #redundant?????
 
   #put below somehow?
@@ -627,7 +755,7 @@ sub collect {
 
     next if qsearchs( 'cust_pay_batch', { 'invnum' => $cust_bill->invnum } );
 
-    #warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ", amount $amount, total_owed $total_owed)";
+    warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ", amount $amount, total_owed $total_owed)" if $Debug;
 
     next unless $amount > 0;
 
@@ -733,7 +861,7 @@ sub collect {
           } elsif ( $processor eq 'cybercash3.2' ) {
             %result = &CCMckDirectLib3_2::SendCC2_1Server(@full_xaction);
           } else {
-            return "Unkonwn real-time processor $processor\n";
+            return "Unknown real-time processor $processor\n";
           }
          
           #if ( $result{'MActionCode'} == 7 ) { #cybercash smps v.1.1.3
@@ -759,7 +887,7 @@ sub collect {
           }
 
         } else {
-          return "Unkonwn real-time processor $processor\n";
+          return "Unknown real-time processor $processor\n";
         }
 
       } else { #batch card
@@ -790,10 +918,6 @@ sub collect {
       return "Unknown payment type ". $self->payby;
     }
 
-
-
-
-
   }
   '';
 
@@ -930,7 +1054,7 @@ sub check_invoicing_list {
 
 =head1 VERSION
 
-$Id: cust_main.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
+$Id: cust_main.pm,v 1.10 2001-02-03 14:03:50 ivan Exp $
 
 =head1 BUGS