freeside/FS/FS Conf.pm,1.130,1.131 cust_main.pm,1.184,1.185 svc_acct.pm,1.181,1.182

Ivan Kohler ivan at pouncequick.420.am
Wed Jun 8 02:03:10 PDT 2005


Update of /home/cvs/cvsroot/freeside/FS/FS
In directory pouncequick:/tmp/cvs-serv25069/FS/FS

Modified Files:
	Conf.pm cust_main.pm svc_acct.pm 
Log Message:
prepaid card recharge

Index: cust_main.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/cust_main.pm,v
retrieving revision 1.184
retrieving revision 1.185
diff -u -d -r1.184 -r1.185
--- cust_main.pm	15 May 2005 13:00:59 -0000	1.184
+++ cust_main.pm	8 Jun 2005 09:03:05 -0000	1.185
@@ -1,7 +1,7 @@
 package FS::cust_main;
 
 use strict;
-use vars qw( @ISA @EXPORT_OK $DEBUG $conf @encrypted_fields
+use vars qw( @ISA @EXPORT_OK $DEBUG $me $conf @encrypted_fields
              $import $skip_fuzzyfiles );
 use vars qw( $realtime_bop_decline_quiet ); #ugh
 use Safe;
@@ -51,7 +51,7 @@
 $realtime_bop_decline_quiet = 0;
 
 $DEBUG = 0;
-#$DEBUG = 1;
+$me = '[FS::cust_main]';
 
 $import = 0;
 $skip_fuzzyfiles = 0;
@@ -349,33 +349,21 @@
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
-  my $prepay_credit = '';
-  my $seconds = 0;
+  my $prepay_identifier = '';
+  my( $amount, $seconds ) = ( 0, 0 );
   if ( $self->payby eq 'PREPAY' ) {
+
     $self->payby('BILL');
-    $prepay_credit = qsearchs(
-      'prepay_credit',
-      { 'identifier' => $self->payinfo },
-      '',
-      'FOR UPDATE'
-    );
-    unless ( $prepay_credit ) {
-      $dbh->rollback if $oldAutoCommit;
-      return "Invalid prepaid card: ". $self->payinfo;
-    }
-    $seconds = $prepay_credit->seconds;
-    if ( $prepay_credit->agentnum ) {
-      if ( $self->agentnum && $self->agentnum != $prepay_credit->agentnum ) {
-        $dbh->rollback if $oldAutoCommit;
-        return "prepaid card not valid for agent ". $self->agentnum;
-      }
-      $self->agentnum($prepay_credit->agentnum);
-    }
-    my $error = $prepay_credit->delete;
+    $prepay_identifier = $self->payinfo;
+    $self->payinfo('');
+
+    my $error = $self->get_prepay($prepay_identifier, \$amount, \$seconds);
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
-      return "removing prepay_credit (transaction rolled back): $error";
+      #return "error applying prepaid card (transaction rolled back): $error";
+      return $error;
     }
+
   }
 
   my $error = $self->SUPER::insert;
@@ -407,15 +395,8 @@
     return "No svc_acct record to apply pre-paid time";
   }
 
-  if ( $prepay_credit && $prepay_credit->amount ) {
-    my $cust_pay = new FS::cust_pay {
-      'custnum' => $self->custnum,
-      'paid'    => $prepay_credit->amount,
-      #'_date'   => #date the prepaid card was purchased???
-      'payby'   => 'PREP',
-      'payinfo' => $prepay_credit->identifier,
-    };
-    $error = $cust_pay->insert;
+  if ( $amount ) {
+    $error = $self->insert_prepay($amount, $prepay_identifier);
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return "inserting prepayment (transaction rolled back): $error";
@@ -524,6 +505,195 @@
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
   ''; #no error
+}
+
+=item recharge_prepay IDENTIFIER | PREPAY_CREDIT_OBJ [ , AMOUNTREF, SECONDSREF ]
+
+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
+card.
+
+=cut
+
+sub recharge_prepay { 
+  my( $self, $prepay_credit, $amountref, $secondsref ) = @_;
+
+  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 $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my( $amount, $seconds ) = ( 0, 0 );
+
+  my $error = $self->get_prepay($prepay_credit, \$amount, \$seconds)
+           || $self->increment_seconds($seconds)
+           || $self->insert_cust_pay_prepay( $amount,
+                                             ref($prepay_credit)
+                                               ? $prepay_credit->identifier
+                                               : $prepay_credit
+                                           );
+
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  if ( defined($amountref)  ) { $$amountref  = $amount;  }
+  if ( defined($secondsref) ) { $$secondsref = $seconds; }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
+
+=item get_prepay IDENTIFIER | PREPAY_CREDIT_OBJ , AMOUNTREF, SECONDSREF
+
+Looks up and deletes a prepaid card (see L<FS::prepay_credit>),
+specified either by I<identifier> or as an FS::prepay_credit object.
+
+References to I<amount> and I<seconds> scalars should be passed as arguments
+and will be incremented by the values of the prepaid card.
+
+If the prepaid card specifies an I<agentnum> (see L<FS::agent>), it is used to
+check or set this customer's I<agentnum>.
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+
+sub get_prepay {
+  my( $self, $prepay_credit, $amountref, $secondsref ) = @_;
+
+  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 $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  unless ( ref($prepay_credit) ) {
+
+    my $identifier = $prepay_credit;
+
+    $prepay_credit = qsearchs(
+      'prepay_credit',
+      { 'identifier' => $prepay_credit },
+      '',
+      'FOR UPDATE'
+    );
+
+    unless ( $prepay_credit ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Invalid prepaid card: ". $identifier;
+    }
+
+  }
+
+  if ( $prepay_credit->agentnum ) {
+    if ( $self->agentnum && $self->agentnum != $prepay_credit->agentnum ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "prepaid card not valid for agent ". $self->agentnum;
+    }
+    $self->agentnum($prepay_credit->agentnum);
+  }
+
+  my $error = $prepay_credit->delete;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return "removing prepay_credit (transaction rolled back): $error";
+  }
+
+  $$amountref  += $prepay_credit->amount;
+  $$secondsref += $prepay_credit->seconds;
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
+
+=item increment_seconds SECONDS
+
+Updates this customer's single or primary account (see L<FS::svc_acct>) by
+the specified number of seconds.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+sub increment_seconds {
+  my( $self, $seconds ) = @_;
+  warn "$me increment_seconds called: $seconds seconds\n"
+    if $DEBUG;
+
+  my @cust_pkg = grep { $_->part_pkg->svcpart('svc_acct') }
+                      $self->ncancelled_pkgs;
+
+  if ( ! @cust_pkg ) {
+    return 'No packages with primary or single services found'.
+           ' to apply pre-paid time';
+  } elsif ( scalar(@cust_pkg) > 1 ) {
+    #maybe have a way to specify the package/account?
+    return 'Multiple packages found to apply pre-paid time';
+  }
+
+  my $cust_pkg = $cust_pkg[0];
+  warn "  found package pkgnum ". $cust_pkg->pkgnum. "\n"
+    if $DEBUG;
+
+  my @cust_svc =
+    $cust_pkg->cust_svc( $cust_pkg->part_pkg->svcpart('svc_acct') );
+
+  if ( ! @cust_svc ) {
+    return 'No account found to apply pre-paid time';
+  } elsif ( scalar(@cust_svc) > 1 ) {
+    return 'Multiple accounts found to apply pre-paid time';
+  }
+  
+  my $svc_acct = $cust_svc[0]->svc_x;
+  warn "  found service svcnum ". $svc_acct->pkgnum.
+       ' ('. $svc_acct->email. ")\n"
+    if $DEBUG;
+
+  $svc_acct->increment_seconds($seconds);
+
+}
+
+=item insert_cust_pay_prepay AMOUNT [ PAYINFO ]
+
+Inserts a prepayment in the specified amount for this customer.  An optional
+second argument can specify the prepayment identifier for tracking purposes.
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub insert_cust_pay_prepay {
+  my( $self, $amount ) = splice(@_, 0, 2);
+  my $payinfo = scalar(@_) ? shift : '';
+
+  my $cust_pay = new FS::cust_pay {
+    'custnum' => $self->custnum,
+    'paid'    => $amount,
+    #'_date'   => #date the prepaid card was purchased???
+    'payby'   => 'PREP',
+    'payinfo' => $payinfo,
+  };
+  $cust_pay->insert;
+
 }
 
 =item reexport

Index: svc_acct.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/svc_acct.pm,v
retrieving revision 1.181
retrieving revision 1.182
diff -u -d -r1.181 -r1.182
--- svc_acct.pm	22 May 2005 23:38:23 -0000	1.181
+++ svc_acct.pm	8 Jun 2005 09:03:05 -0000	1.182
@@ -37,7 +37,6 @@
 @ISA = qw( FS::svc_Common );
 
 $DEBUG = 0;
-#$DEBUG = 1;
 $me = '[FS::svc_acct]';
 
 #ask FS::UID to run this stuff for us later
@@ -1117,12 +1116,45 @@
 
 =item decrement_seconds SECONDS
 
-Decrements the I<seconds> field of this record by the given amount.
+Decrements the I<seconds> field of this record by the given amount.  If there
+is an error, returns the error, otherwise returns false.
 
 =cut
 
 sub decrement_seconds {
-  my( $self, $seconds ) = @_;
+  shift->_op_seconds('-', @_);
+}
+
+=item increment_seconds SECONDS
+
+Increments the I<seconds> field of this record by the given amount.  If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub increment_seconds {
+  shift->_op_seconds('+', @_);
+}
+
+
+my %op2action = (
+  '-' => 'suspend',
+  '+' => 'unsuspend',
+);
+my %op2condition = (
+  '-' => sub { my($self, $seconds) = @_;
+               $self->seconds - $seconds <= 0;
+             },
+  '+' => sub { my($self, $seconds) = @_;
+               $self->seconds + $seconds > 0;
+             },
+);
+
+sub _op_seconds {
+  my( $self, $op, $seconds ) = @_;
+  warn "$me _op_seconds called for svcnum ". $self->svcnum.
+       ' ('. $self->email. "): $op $seconds\n"
+    if $DEBUG;
 
   local $SIG{HUP} = 'IGNORE';
   local $SIG{INT} = 'IGNORE';
@@ -1134,21 +1166,40 @@
   my $oldAutoCommit = $FS::UID::AutoCommit;
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
-  
-  my $sth = dbh->prepare(
-    'UPDATE svc_acct SET seconds = seconds - ? WHERE svcnum = ?'
-  ) or die dbh->errstr;;
-  $sth->execute($seconds, $self->svcnum) or die $sth->errstr;
-  if ( $conf->exists('svc_acct-usage_suspend')
-       && $self->seconds - $seconds <= 0       ) {
-    #my $error = $self->suspend;
-    my $error = $self->cust_svc->cust_pkg->suspend;
-    die $error if $error;
+
+  my $sql = "UPDATE svc_acct SET seconds = ".
+            " CASE WHEN seconds IS NULL THEN 0 ELSE seconds END ". #$seconds||0
+            " $op ? WHERE svcnum = ?";
+  warn "$me $sql\n"
+    if $DEBUG;
+
+  my $sth = $dbh->prepare( $sql )
+    or die "Error preparing $sql: ". $dbh->errstr;
+  my $rv = $sth->execute($seconds, $self->svcnum);
+  die "Error executing $sql: ". $sth->errstr
+    unless defined($rv);
+  die "Can't update seconds for svcnum". $self->svcnum
+    if $rv == 0;
+
+  my $action = $op2action{$op};
+
+  if ( $conf->exists("svc_acct-usage_$action")
+       && &{$op2condition{$op}}($self, $seconds)    ) {
+    #my $error = $self->$action();
+    my $error = $self->cust_svc->cust_pkg->$action();
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Error ${action}ing: $error";
+    }
   }
 
+  warn "$me update sucessful; committing\n"
+    if $DEBUG;
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
 
 }
+
 
 =item seconds_since TIMESTAMP
 

Index: Conf.pm
===================================================================
RCS file: /home/cvs/cvsroot/freeside/FS/FS/Conf.pm,v
retrieving revision 1.130
retrieving revision 1.131
diff -u -d -r1.130 -r1.131
--- Conf.pm	14 May 2005 17:04:55 -0000	1.130
+++ Conf.pm	8 Jun 2005 09:03:05 -0000	1.131
@@ -1531,6 +1531,13 @@
     'type'        => 'checkbox',
   },
 
+  {
+    'key'         => 'svc_acct-usage_unsuspend',
+    'section'     => 'billing',
+    'description' => 'Unuspends the package an account belongs to when svc_acct.seconds is incremented from 0 or below to a positive value (accounts with an empty seconds value are ignored).  Typically used in conjunction with prepaid packages and freeside-sqlradius-radacctd.',
+    'type'        => 'checkbox',
+  },
+
 );
 
 1;




More information about the freeside-commits mailing list