start adding package locations, RT#4499
[freeside.git] / FS / FS / cust_main.pm
index c7bcf67..2b94dca 100644 (file)
@@ -9,7 +9,7 @@ use Safe;
 use Carp;
 use Exporter;
 use Scalar::Util qw( blessed );
 use Carp;
 use Exporter;
 use Scalar::Util qw( blessed );
-use Time::Local qw(timelocal_nocheck);
+use Time::Local qw(timelocal timelocal_nocheck);
 use Data::Dumper;
 use Tie::IxHash;
 use Digest::MD5 qw(md5_base64);
 use Data::Dumper;
 use Tie::IxHash;
 use Digest::MD5 qw(md5_base64);
@@ -135,101 +135,181 @@ FS::Record.  The following fields are currently supported:
 
 =over 4
 
 
 =over 4
 
-=item custnum - primary key (assigned automatically for new customers)
+=item custnum
 
 
-=item agentnum - agent (see L<FS::agent>)
+Primary key (assigned automatically for new customers)
 
 
-=item refnum - Advertising source (see L<FS::part_referral>)
+=item agentnum
+
+Agent (see L<FS::agent>)
+
+=item refnum
+
+Advertising source (see L<FS::part_referral>)
+
+=item first
+
+First name
+
+=item last
 
 
-=item first - name
+Last name
 
 
-=item last - name
+=item ss
 
 
-=item ss - social security number (optional)
+Cocial security number (optional)
 
 
-=item company - (optional)
+=item company
+
+(optional)
 
 =item address1
 
 
 =item address1
 
-=item address2 - (optional)
+=item address2
+
+(optional)
 
 =item city
 
 
 =item city
 
-=item county - (optional, see L<FS::cust_main_county>)
+=item county
+
+(optional, see L<FS::cust_main_county>)
 
 
-=item state - (see L<FS::cust_main_county>)
+=item state
+
+(see L<FS::cust_main_county>)
 
 =item zip
 
 
 =item zip
 
-=item country - (see L<FS::cust_main_county>)
+=item country
+
+(see L<FS::cust_main_county>)
+
+=item daytime
 
 
-=item daytime - phone (optional)
+phone (optional)
 
 
-=item night - phone (optional)
+=item night
 
 
-=item fax - phone (optional)
+phone (optional)
 
 
-=item ship_first - name
+=item fax
 
 
-=item ship_last - name
+phone (optional)
 
 
-=item ship_company - (optional)
+=item ship_first
+
+Shipping first name
+
+=item ship_last
+
+Shipping last name
+
+=item ship_company
+
+(optional)
 
 =item ship_address1
 
 
 =item ship_address1
 
-=item ship_address2 - (optional)
+=item ship_address2
+
+(optional)
 
 =item ship_city
 
 
 =item ship_city
 
-=item ship_county - (optional, see L<FS::cust_main_county>)
+=item ship_county
+
+(optional, see L<FS::cust_main_county>)
 
 
-=item ship_state - (see L<FS::cust_main_county>)
+=item ship_state
+
+(see L<FS::cust_main_county>)
 
 =item ship_zip
 
 
 =item ship_zip
 
-=item ship_country - (see L<FS::cust_main_county>)
+=item ship_country
+
+(see L<FS::cust_main_county>)
+
+=item ship_daytime
+
+phone (optional)
+
+=item ship_night
+
+phone (optional)
 
 
-=item ship_daytime - phone (optional)
+=item ship_fax
 
 
-=item ship_night - phone (optional)
+phone (optional)
 
 
-=item ship_fax - phone (optional)
+=item payby
+
+Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
 
 
-=item payby - Payment Type (See L<FS::payinfo_Mixin> for valid payby values)
+=item payinfo
 
 
-=item payinfo - Payment Information (See L<FS::payinfo_Mixin> for data format)
+Payment Information (See L<FS::payinfo_Mixin> for data format)
 
 
-=item paymask - Masked payinfo (See L<FS::payinfo_Mixin> for how this works)
+=item paymask
+
+Masked payinfo (See L<FS::payinfo_Mixin> for how this works)
 
 =item paycvv
 
 Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
 
 
 =item paycvv
 
 Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
 
-=item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
+=item paydate
+
+Expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
+
+=item paystart_month
+
+Start date month (maestro/solo cards only)
+
+=item paystart_year
+
+Start date year (maestro/solo cards only)
+
+=item payissue
+
+Issue number (maestro/solo cards only)
+
+=item payname
+
+Name on card or billing name
+
+=item payip
 
 
-=item paystart_month - start date month (maestro/solo cards only)
+IP address from which payment information was received
 
 
-=item paystart_year - start date year (maestro/solo cards only)
+=item tax
 
 
-=item payissue - issue number (maestro/solo cards only)
+Tax exempt, empty or `Y'
 
 
-=item payname - name on card or billing name
+=item otaker
 
 
-=item payip - IP address from which payment information was received
+Order taker (assigned automatically, see L<FS::UID>)
 
 
-=item tax - tax exempt, empty or `Y'
+=item comments
 
 
-=item otaker - order taker (assigned automatically, see L<FS::UID>)
+Comments (optional)
 
 
-=item comments - comments (optional)
+=item referral_custnum
 
 
-=item referral_custnum - referring customer number
+Referring customer number
 
 
-=item spool_cdr - Enable individual CDR spooling, empty or `Y'
+=item spool_cdr
 
 
-=item dundate - a suggestion to events (see L<FS::part_bill_event">) to delay until this unix timestamp
+Enable individual CDR spooling, empty or `Y'
 
 
-=item squelch_cdr - Discourage individual CDR printing, empty or `Y'
+=item dundate
+
+A suggestion to events (see L<FS::part_bill_event">) to delay until this unix timestamp
+
+=item squelch_cdr
+
+Discourage individual CDR printing, empty or `Y'
 
 =back
 
 
 =back
 
@@ -2100,6 +2180,7 @@ sub bill {
     if $DEBUG;
 
   my $time = $options{'time'} || time;
     if $DEBUG;
 
   my $time = $options{'time'} || time;
+  my $invoice_time = $options{'invoice_time'} || $time;
 
   #put below somehow?
   local $SIG{HUP} = 'IGNORE';
 
   #put below somehow?
   local $SIG{HUP} = 'IGNORE';
@@ -2210,7 +2291,11 @@ sub bill {
   foreach my $tax ( keys %taxlisthash ) {
     my $tax_object = shift @{ $taxlisthash{$tax} };
     warn "found ". $tax_object->taxname. " as $tax\n" if $DEBUG > 2;
   foreach my $tax ( keys %taxlisthash ) {
     my $tax_object = shift @{ $taxlisthash{$tax} };
     warn "found ". $tax_object->taxname. " as $tax\n" if $DEBUG > 2;
-    my $listref_or_error = $tax_object->taxline( @{ $taxlisthash{$tax} } );
+    my $listref_or_error =
+      $tax_object->taxline( $taxlisthash{$tax},
+                            'custnum'      => $self->custnum,
+                            'invoice_time' => $invoice_time
+                          );
     unless (ref($listref_or_error)) {
       $dbh->rollback if $oldAutoCommit;
       return $listref_or_error;
     unless (ref($listref_or_error)) {
       $dbh->rollback if $oldAutoCommit;
       return $listref_or_error;
@@ -2220,15 +2305,26 @@ sub bill {
     warn "adding ". $listref_or_error->[1].
          " as ". $listref_or_error->[0]. "\n"
       if $DEBUG > 2;
     warn "adding ". $listref_or_error->[1].
          " as ". $listref_or_error->[0]. "\n"
       if $DEBUG > 2;
-    $tax{ $tax_object->taxname } += $listref_or_error->[1];
+    $tax{ $tax } += $listref_or_error->[1];
     if ( $taxname{ $listref_or_error->[0] } ) {
     if ( $taxname{ $listref_or_error->[0] } ) {
-      push @{ $taxname{ $listref_or_error->[0] } }, $tax_object->taxname;
+      push @{ $taxname{ $listref_or_error->[0] } }, $tax;
     }else{
     }else{
-      $taxname{ $listref_or_error->[0] } = [ $tax_object->taxname ];
+      $taxname{ $listref_or_error->[0] } = [ $tax ];
     }
   
   }
 
     }
   
   }
 
+  #move the cust_tax_exempt_pkg records to the cust_bill_pkgs we will commit
+  my %packagemap = map { $_->pkgnum => $_ } @cust_bill_pkg;
+  foreach my $tax ( keys %taxlisthash ) {
+    foreach ( @{ $taxlisthash{$tax} }[1 ... scalar(@{ $taxlisthash{$tax} })] ) {
+      next unless ref($_) eq 'FS::cust_bill_pkg'; # shouldn't happen
+
+      push @{ $packagemap{$_->pkgnum}->_cust_tax_exempt_pkg }, 
+        splice( @{ $_->_cust_tax_exempt_pkg } );
+    }
+  }
+
   #some taxes are taxed
   my %totlisthash;
   
   #some taxes are taxed
   my %totlisthash;
   
@@ -2248,9 +2344,9 @@ sub bill {
                                                       # existing taxes
       warn "adding $totname to taxed taxes\n" if $DEBUG > 2;
       if ( exists( $totlisthash{ $totname } ) ) {
                                                       # existing taxes
       warn "adding $totname to taxed taxes\n" if $DEBUG > 2;
       if ( exists( $totlisthash{ $totname } ) ) {
-        push @{ $totlisthash{ $totname  } }, $tax{ $tax_object->taxname };
+        push @{ $totlisthash{ $totname  } }, $tax{ $tax };
       }else{
       }else{
-        $totlisthash{ $totname } = [ $tot, $tax{ $tax_object->taxname } ];
+        $totlisthash{ $totname } = [ $tot, $tax{ $tax } ];
       }
     }
   }
       }
     }
   }
@@ -2260,7 +2356,11 @@ sub bill {
     my $tax_object = shift @{ $totlisthash{$tax} };
     warn "found previously found taxed tax ". $tax_object->taxname. "\n"
       if $DEBUG > 2;
     my $tax_object = shift @{ $totlisthash{$tax} };
     warn "found previously found taxed tax ". $tax_object->taxname. "\n"
       if $DEBUG > 2;
-    my $listref_or_error = $tax_object->taxline( @{ $totlisthash{$tax} } );
+    my $listref_or_error =
+      $tax_object->taxline( $totlisthash{$tax},
+                            'custnum'      => $self->custnum,
+                            'invoice_time' => $invoice_time
+                          );
     unless (ref($listref_or_error)) {
       $dbh->rollback if $oldAutoCommit;
       return $listref_or_error;
     unless (ref($listref_or_error)) {
       $dbh->rollback if $oldAutoCommit;
       return $listref_or_error;
@@ -2269,7 +2369,7 @@ sub bill {
     warn "adding taxed tax amount ". $listref_or_error->[1].
          " as ". $tax_object->taxname. "\n"
       if $DEBUG;
     warn "adding taxed tax amount ". $listref_or_error->[1].
          " as ". $tax_object->taxname. "\n"
       if $DEBUG;
-    $tax{ $tax_object->taxname } += $listref_or_error->[1];
+    $tax{ $tax } += $listref_or_error->[1];
   }
   
   #consolidate and create tax line items
   }
   
   #consolidate and create tax line items
@@ -2304,7 +2404,7 @@ sub bill {
   #create the new invoice
   my $cust_bill = new FS::cust_bill ( {
     'custnum' => $self->custnum,
   #create the new invoice
   my $cust_bill = new FS::cust_bill ( {
     'custnum' => $self->custnum,
-    '_date'   => ( $options{'invoice_time'} || $time ),
+    '_date'   => ( $invoice_time ),
     'charged' => $charged,
   } );
   my $error = $cust_bill->insert;
     'charged' => $charged,
   } );
   my $error = $cust_bill->insert;
@@ -2885,6 +2985,11 @@ Only return events for the specified eventtable (by default, events of all event
 
 Explicitly pass the objects to be tested (typically used with eventtable).
 
 
 Explicitly pass the objects to be tested (typically used with eventtable).
 
+=item testonly
+
+Set to true to return the objects, but not actually insert them into the
+database.
+
 =back
 
 =cut
 =back
 
 =cut
@@ -2915,7 +3020,8 @@ sub due_cust_event {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  $self->select_for_update; #mutex
+  $self->select_for_update #mutex
+    unless $opt{testonly};
 
   ###
   # 1: find possible events (initial search)
 
   ###
   # 1: find possible events (initial search)
@@ -6657,7 +6763,7 @@ sub generate_letter {
 
   $letter_data{company_name} = $conf->config('company_name');
 
 
   $letter_data{company_name} = $conf->config('company_name');
 
-  my $dir = $FS::UID::conf_dir."cache.". $FS::UID::datasrc;
+  my $dir = $FS::UID::conf_dir."/cache.". $FS::UID::datasrc;
   my $fh = new File::Temp( TEMPLATE => 'letter.'. $self->custnum. '.XXXXXXXX',
                            DIR      => $dir,
                            SUFFIX   => '.tex',
   my $fh = new File::Temp( TEMPLATE => 'letter.'. $self->custnum. '.XXXXXXXX',
                            DIR      => $dir,
                            SUFFIX   => '.tex',