prepaid download/upload tracking
[freeside.git] / FS / FS / cust_main.pm
index 875db93..210ab63 100644 (file)
@@ -397,6 +397,8 @@ sub insert {
   warn "  inserting $self\n"
     if $DEBUG > 1;
 
+  $self->signupdate(time) unless $self->signupdate;
+
   my $error = $self->SUPER::insert;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
@@ -690,21 +692,23 @@ sub order_pkgs {
   ''; #no error
 }
 
-=item recharge_prepay IDENTIFIER | PREPAY_CREDIT_OBJ [ , AMOUNTREF, SECONDSREF ]
+=item recharge_prepay IDENTIFIER | PREPAY_CREDIT_OBJ [ , AMOUNTREF, SECONDSREF, UPBYTEREF, DOWNBYTEREF ]
 
 Recharges this (existing) customer with the specified prepaid card (see
 L<FS::prepay_credit>), specified either by I<identifier> or as an
 FS::prepay_credit object.  If there is an error, returns the error, otherwise
 returns false.
 
-Optionally, two scalar references can be passed as well.  They will have their
-values filled in with the amount and number of seconds applied by this prepaid
+Optionally, four scalar references can be passed as well.  They will have their
+values filled in with the amount, number of seconds, and number of upload and
+download bytes applied by this prepaid
 card.
 
 =cut
 
 sub recharge_prepay { 
-  my( $self, $prepay_credit, $amountref, $secondsref ) = @_;
+  my( $self, $prepay_credit, $amountref, $secondsref, 
+      $upbytesref, $downbytesref ) = @_;
 
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
@@ -717,10 +721,14 @@ sub recharge_prepay {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my( $amount, $seconds ) = ( 0, 0 );
+  my( $amount, $seconds, $upbytes, $downbytes ) = ( 0, 0, 0, 0 );
 
-  my $error = $self->get_prepay($prepay_credit, \$amount, \$seconds)
+  my $error = $self->get_prepay($prepay_credit, \$amount,
+                                \$seconds, \$upbytes, \$downbytes)
            || $self->increment_seconds($seconds)
+           || $self->increment_upbytes($upbytes)
+           || $self->increment_downbytes($downbytes)
+           || $self->increment_totalbytes($upbytes + $downbytes)
            || $self->insert_cust_pay_prepay( $amount,
                                              ref($prepay_credit)
                                                ? $prepay_credit->identifier
@@ -734,6 +742,8 @@ sub recharge_prepay {
 
   if ( defined($amountref)  ) { $$amountref  = $amount;  }
   if ( defined($secondsref) ) { $$secondsref = $seconds; }
+  if ( defined($upbytesref) ) { $$upbytesref = $upbytes; }
+  if ( defined($downbytesref) ) { $$downbytesref = $downbytes; }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
@@ -757,7 +767,7 @@ If there is an error, returns the error, otherwise returns false.
 
 
 sub get_prepay {
-  my( $self, $prepay_credit, $amountref, $secondsref ) = @_;
+  my( $self, $prepay_credit, $amountref, $secondsref, $upref, $downref) = @_;
 
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
@@ -804,12 +814,50 @@ sub get_prepay {
 
   $$amountref  += $prepay_credit->amount;
   $$secondsref += $prepay_credit->seconds;
+  $$upref      += $prepay_credit->upbytes;
+  $$downref    += $prepay_credit->downbytes;
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   '';
 
 }
 
+=item increment_upbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of upbytes.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_upbytes {
+  _increment_column( shift, 'upbytes', @_);
+}
+
+=item increment_downbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of downbytes.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_downbytes {
+  _increment_column( shift, 'downbytes', @_);
+}
+
+=item increment_totalbytes SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of totalbytes.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_totalbytes {
+  _increment_column( shift, 'totalbytes', @_);
+}
+
 =item increment_seconds SECONDS
 
 Updates this customer's single or primary account (see L<FS::svc_acct>) by
@@ -819,10 +867,24 @@ otherwise returns false.
 =cut
 
 sub increment_seconds {
-  my( $self, $seconds ) = @_;
-  warn "$me increment_seconds called: $seconds seconds\n"
+  _increment_column( shift, 'seconds', @_);
+}
+
+=item _increment_column AMOUNT
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of seconds or bytes.  If there is an error, returns
+the error, otherwise returns false.
+
+=cut
+
+sub _increment_column {
+  my( $self, $column, $amount ) = @_;
+  warn "$me increment_column called: $column, $amount\n"
     if $DEBUG;
 
+  return '' unless $amount;
+
   my @cust_pkg = grep { $_->part_pkg->svcpart('svc_acct') }
                       $self->ncancelled_pkgs;
 
@@ -852,7 +914,8 @@ sub increment_seconds {
        ' ('. $svc_acct->email. ")\n"
     if $DEBUG > 1;
 
-  $svc_acct->increment_seconds($seconds);
+  $column = "increment_$column";
+  $svc_acct->$column($amount);
 
 }
 
@@ -1209,6 +1272,8 @@ sub check {
     || $self->ut_number('refnum')
     || $self->ut_name('last')
     || $self->ut_name('first')
+    || $self->ut_snumbern('birthdate')
+    || $self->ut_snumbern('signupdate')
     || $self->ut_textn('company')
     || $self->ut_text('address1')
     || $self->ut_textn('address2')
@@ -1641,7 +1706,7 @@ Returns a list: an empty list on success or a list of errors.
 
 sub suspend {
   my $self = shift;
-  grep { $_->suspend } $self->unsuspended_pkgs;
+  grep { $_->suspend(@_) } $self->unsuspended_pkgs;
 }
 
 =item suspend_if_pkgpart PKGPART [ , PKGPART ... ]
@@ -1655,8 +1720,14 @@ Returns a list: an empty list on success or a list of errors.
 
 sub suspend_if_pkgpart {
   my $self = shift;
-  my @pkgparts = @_;
-  grep { $_->suspend }
+  my (@pkgparts, %opt);
+  if (ref($_[0]) eq 'HASH'){
+    @pkgparts = @{$_[0]{pkgparts}};
+    %opt      = %{$_[0]};
+  }else{
+    @pkgparts = @_;
+  }
+  grep { $_->suspend(%opt) }
     grep { my $pkgpart = $_->pkgpart; grep { $pkgpart eq $_ } @pkgparts }
       $self->unsuspended_pkgs;
 }
@@ -1672,8 +1743,14 @@ Returns a list: an empty list on success or a list of errors.
 
 sub suspend_unless_pkgpart {
   my $self = shift;
-  my @pkgparts = @_;
-  grep { $_->suspend }
+  my (@pkgparts, %opt);
+  if (ref($_[0]) eq 'HASH'){
+    @pkgparts = @{$_[0]{pkgparts}};
+    %opt      = %{$_[0]};
+  }else{
+    @pkgparts = @_;
+  }
+  grep { $_->suspend(%opt) }
     grep { my $pkgpart = $_->pkgpart; ! grep { $pkgpart eq $_ } @pkgparts }
       $self->unsuspended_pkgs;
 }
@@ -1732,6 +1809,22 @@ sub _banned_pay_hashref {
   };
 }
 
+=item notes
+
+Returns all notes (see L<FS::cust_main_note>) for this customer.
+
+=cut
+
+sub notes {
+  my $self = shift;
+  #order by?
+  qsearch( 'cust_main_note',
+           { 'custnum' => $self->custnum },
+          '',
+          'ORDER BY _DATE DESC'
+        );
+}
+
 =item agent
 
 Returns the agent (see L<FS::agent>) for this customer.
@@ -2486,6 +2579,9 @@ sub realtime_bop {
   $content{customer_ip} = $payip
     if length($payip);
 
+  $content{invoice_number} = $options{'invnum'}
+    if exists($options{'invnum'}) && length($options{'invnum'});
+
   if ( $method eq 'CC' ) { 
 
     $content{card_number} = $payinfo;
@@ -2551,7 +2647,7 @@ sub realtime_bop {
     'action'         => $action1,
     'description'    => $options{'description'},
     'amount'         => $amount,
-    'invoice_number' => $options{'invnum'},
+    #'invoice_number' => $options{'invnum'},
     'customer_id'    => $self->custnum,
     'last_name'      => $paylast,
     'first_name'     => $payfirst,
@@ -3952,12 +4048,12 @@ sub fuzzy_search {
 
   check_and_rebuild_fuzzyfiles();
   foreach my $field ( keys %$fuzzy ) {
+
+    my $all = $self->all_X($field);
+    next unless scalar(@$all);
+
     my %match = ();
-    $match{$_}=1 foreach ( amatch( $fuzzy->{$field},
-                                   ['i'],
-                                   @{ $self->all_X($field) }
-                                 )
-                         );
+    $match{$_}=1 foreach ( amatch( $fuzzy->{$field}, ['i'], @$all ) );
 
     my @fcust = ();
     foreach ( keys %match ) {
@@ -3986,10 +4082,10 @@ sub fuzzy_search {
 
 Accepts the following options: I<search>, the string to search for.  The string
 will be searched for as a customer number, phone number, name or company name,
-first searching for an exact match then fuzzy and substring matches (in some
-cases - see the source code for the exact heuristics used).
+as an exact, or, in some cases, a substring or fuzzy match (see the source code
+for the exact heuristics used).
 
-Any additional options treated as an additional qualifier on the search
+Any additional options are treated as an additional qualifier on the search
 (i.e. I<agentnum>).
 
 Returns a (possibly empty) array of FS::cust_main objects.
@@ -4139,7 +4235,9 @@ sub smart_search {
       'extra_sql' => "$sql AND $agentnums_sql", #agent virtualization
     } );
 
-    unless ( @cust_main ) {  #no exact match, trying substring/fuzzy
+    #always do substring & fuzzy,
+    #getting complains searches are not returning enough
+    #unless ( @cust_main ) {  #no exact match, trying substring/fuzzy
 
       #still some false laziness w/ search/cust_main.cgi
 
@@ -4200,7 +4298,7 @@ sub smart_search {
           FS::cust_main->fuzzy_search( { $field => $value }, @fuzopts );
       }
 
-    }
+    #}
 
     #eliminate duplicates
     my %saw = ();
@@ -4456,7 +4554,12 @@ sub batch_import {
 
       my @svc_acct = ();
       if ( $svc_acct{'username'} ) {
-        $svc_acct{svcpart} = $cust_pkg->part_pkg->svcpart( 'svc_acct' );
+        my $part_pkg = $cust_pkg->part_pkg;
+       unless ( $part_pkg ) {
+         $dbh->rollback if $oldAutoCommit;
+         return "unknown pkgnum ". $cust_pkg{'pkgpart'};
+       } 
+        $svc_acct{svcpart} = $part_pkg->svcpart( 'svc_acct' );
         push @svc_acct, new FS::svc_acct ( \%svc_acct )
       }
 
@@ -4603,6 +4706,8 @@ No multiple currency support (probably a larger project than just this module).
 
 payinfo_masked false laziness with cust_pay.pm and cust_refund.pm
 
+Birthdates rely on negative epoch values.
+
 =head1 SEE ALSO
 
 L<FS::Record>, L<FS::cust_pkg>, L<FS::cust_bill>, L<FS::cust_credit>