maintenance:
authorivan <ivan>
Mon, 19 May 2003 12:00:45 +0000 (12:00 +0000)
committerivan <ivan>
Mon, 19 May 2003 12:00:45 +0000 (12:00 +0000)
  - add FS::Misc with send_email subroutine, remove all duplicate code from
    various modules
  - move the realtime_bop from cust_bill to cust_main & change usage slightly.
    invnum is no longer required.  FS::cust_bill::realtime_bop remains as a
    wrapper.
self-service:
  - fix some syntax errors, make payment UI (step one) really should be
    working now

FS/FS/ClientAPI/MyAccount.pm
FS/FS/Misc.pm [new file with mode: 0644]
FS/FS/cust_bill.pm
FS/FS/cust_main.pm
FS/FS/cust_pay.pm
FS/FS/cust_pkg.pm
FS/FS/svc_acct.pm
FS/FS/svc_domain.pm
FS/MANIFEST
FS/t/Misc.t [new file with mode: 0644]

index c750ada..a64cfb5 100644 (file)
@@ -167,7 +167,7 @@ sub payment_info {
   };
 
   my $_date = time;
-  $return{paybatch} = 'webui-MyAccount-$_date-$$-". rand() * 2**32
+  $return{paybatch} = "webui-MyAccount-$_date-$$-". rand() * 2**32;
 
   return { 'error' => '',
            %return,
@@ -175,6 +175,20 @@ sub payment_info {
 
 };
 
+sub make_payment{
+  my $p = shift;
+
+  my $session = $cache->get($p->{'session_id'})
+    or return { 'error' => "Can't resume session" }; #better error message
+
+  my %return;
+
+  my $custnum = $session->{'custnum'};
+
+
+
+}
+
 sub invoice {
   my $p = shift;
   my $session = $cache->get($p->{'session_id'})
diff --git a/FS/FS/Misc.pm b/FS/FS/Misc.pm
new file mode 100644 (file)
index 0000000..56dc72e
--- /dev/null
@@ -0,0 +1,95 @@
+package FS::Misc;
+
+use strict;
+use vars qw ( @ISA @EXPORT_OK );
+use Exporter;
+
+@ISA = qw( Exporter );
+@EXPORT_OK = qw( send_email );
+
+=head1 NAME
+
+FS::Misc - Miscellaneous subroutines
+
+=head1 SYNOPSIS
+
+  use FS::Misc qw(send_email);
+
+  send_email();
+
+=head1 DESCRIPTION
+
+Miscellaneous subroutines.  This module contains miscellaneous subroutines
+called from multiple other modules.  These are not OO or necessarily related,
+but are collected here to elimiate code duplication.
+
+=head1 SUBROUTINES
+
+=over 4
+
+=item send_email OPTION => VALUE ...
+
+Options:
+
+I<from> - (required)
+
+I<to> - (required) comma-separated scalar or arrayref of recipients
+
+I<subject> - (required)
+
+I<content-type> - (optional) MIME type
+
+I<body> - (required) arrayref of body text lines
+
+=cut
+
+use vars qw( $conf );
+use Date::Format;
+use Mail::Header;
+use Mail::Internet 1.44;
+use FS::UID;
+
+FS::UID->install_callback( sub {
+  $conf = new FS::Conf;
+} );
+
+sub send_email {
+  my(%options) = shift;
+
+  $ENV{MAILADDRESS} = $options{'from'};
+  my $to = ref($options{to}) ? join(', ', @{ $options{to} } ) : $options{to};
+  my @header = (
+    'From: '.     $options{'from'},
+    'To: '.       $to,
+    'Sender: '.   $options{'from'},
+    'Reply-To: '. $options{'from'},
+    'Date: '.     time2str("%a, %d %b %Y %X %z", time),
+    'Subject: '.  $options{'subject'},
+  );
+  push @header, 'Content-Type: '. $options{'content-type'}
+    if exists($options{'content-type'});
+  my $header = new Mail::Header ( \@header );
+
+  my $message = new Mail::Internet (
+    'Header' => $header,
+    'Body'   => $options{'body'},
+  );
+
+  my $smtpmachine = $conf->config('smtpmachine');
+  $!=0;
+  $message->smtpsend( 'Host' => $smtpmachine )
+    or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
+      or return "can't send email to $to via server $smtpmachine with SMTP: $!";
+}
+
+=head1 BUGS
+
+This package exists.
+
+=head1 SEE ALSO
+
+L<FS::UID>, L<FS::CGI>, L<FS::Record>, the base documentation.
+
+=cut
+
+1;
index a0634d9..a22f44b 100644 (file)
@@ -2,18 +2,12 @@ package FS::cust_bill;
 
 use strict;
 use vars qw( @ISA $conf $money_char );
-use vars qw( $lpr $invoice_from $smtpmachine );
-use vars qw( $xaction $E_NoErr );
-use vars qw( $bop_processor $bop_login $bop_password $bop_action @bop_options );
-use vars qw( $ach_processor $ach_login $ach_password $ach_action @ach_options );
 use vars qw( $invoice_lines @buf ); #yuck
-use vars qw( $quiet );
 use Date::Format;
-use Mail::Internet 1.44;
-use Mail::Header;
 use Text::Template;
 use FS::UID qw( datasrc );
 use FS::Record qw( qsearch qsearchs );
+use FS::Misc qw( send_email );
 use FS::cust_main;
 use FS::cust_bill_pkg;
 use FS::cust_credit;
@@ -26,46 +20,10 @@ use FS::cust_bill_event;
 @ISA = qw( FS::Record );
 
 #ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::cust_bill'} = sub { 
-
+FS::UID->install_callback( sub { 
   $conf = new FS::Conf;
-
   $money_char = $conf->config('money_char') || '$';  
-
-  $lpr = $conf->config('lpr');
-  $invoice_from = $conf->config('invoice_from');
-  $smtpmachine = $conf->config('smtpmachine');
-
-  ( $bop_processor,$bop_login, $bop_password, $bop_action ) = ( '', '', '', '');
-  @bop_options = ();
-  ( $ach_processor,$ach_login, $ach_password, $ach_action ) = ( '', '', '', '');
-  @ach_options = ();
-
-  if ( $conf->exists('business-onlinepayment') ) {
-    ( $bop_processor,
-      $bop_login,
-      $bop_password,
-      $bop_action,
-      @bop_options
-    ) = $conf->config('business-onlinepayment');
-    $bop_action ||= 'normal authorization';
-    ( $ach_processor, $ach_login, $ach_password, $ach_action, @ach_options ) =
-      ( $bop_processor, $bop_login, $bop_password, $bop_action, @bop_options );
-    eval "use Business::OnlinePayment";  
-  }
-
-  if ( $conf->exists('business-onlinepayment-ach') ) {
-    ( $ach_processor,
-      $ach_login,
-      $ach_password,
-      $ach_action,
-      @ach_options
-    ) = $conf->config('business-onlinepayment-ach');
-    $ach_action ||= 'normal authorization';
-    eval "use Business::OnlinePayment";  
-  }
-
-};
+} );
 
 =head1 NAME
 
@@ -373,33 +331,20 @@ sub send {
   if ( grep { $_ ne 'POST' } @invoicing_list or !@invoicing_list  ) { #email
 
     #better to notify this person than silence
-    @invoicing_list = ($invoice_from) unless @invoicing_list;
-
-    #false laziness w/FS::cust_pay::delete & fs_signup_server && ::realtime_card
-    #$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' => [ @print_text ], #( date)
+    @invoicing_list = ($conf->config('invoice_from')) unless @invoicing_list;
+
+    my $error = send_email(
+      'from'    => $conf->config('invoice_from'),
+      'to'      => [ grep { $_ ne 'POST' } @invoicing_list ],
+      'subject' => 'Invoice',
+      'body'    => \@print_text,
     );
-    $!=0;
-    $message->smtpsend( Host => $smtpmachine )
-      or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
-        or return "(customer # ". $self->custnum. ") can't send invoice email".
-                  " to ". join(', ', grep { $_ ne 'POST' } @invoicing_list ).
-                  " via server $smtpmachine with SMTP: $!";
+    return "can't send invoice: $error" if $error;
 
   }
 
   if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal
+    my $lpr = $conf->config('lpr');
     open(LPR, "|$lpr")
       or return "Can't open pipe to $lpr: $!";
     print LPR @print_text;
@@ -610,15 +555,7 @@ for supported processors.
 
 sub realtime_card {
   my $self = shift;
-  $self->realtime_bop(
-    'CC',
-    $bop_processor,
-    $bop_login,
-    $bop_password,
-    $bop_action,
-    \@bop_options,
-    @_
-  );
+  $self->realtime_bop( 'CC', @_ );
 }
 
 =item realtime_ach
@@ -632,15 +569,7 @@ for supported processors.
 
 sub realtime_ach {
   my $self = shift;
-  $self->realtime_bop(
-    'ECHECK',
-    $ach_processor,
-    $ach_login,
-    $ach_password,
-    $ach_action,
-    \@ach_options,
-    @_
-  );
+  $self->realtime_bop( 'ECHECK', @_ );
 }
 
 =item realtime_lec
@@ -654,53 +583,15 @@ for supported processors.
 
 sub realtime_lec {
   my $self = shift;
-  $self->realtime_bop(
-    'LEC',
-    $bop_processor,
-    $bop_login,
-    $bop_password,
-    $bop_action,
-    \@bop_options,
-    @_
-  );
+  $self->realtime_bop( 'LEC', @_ );
 }
 
 sub realtime_bop {
-  my( $self, $method, $processor, $login, $password, $action, $options ) = @_;
-
-  #trim an extraneous blank line
-  pop @$options if scalar(@$options) % 2 && $options->[-1] =~ /^\s*$/;
+  my( $self, $method ) = @_;
 
   my $cust_main = $self->cust_main;
   my $amount = $self->owed;
 
-  my $address = $cust_main->address1;
-  $address .= ", ". $cust_main->address2 if $cust_main->address2;
-
-  my($payname, $payfirst, $paylast);
-  if ( $cust_main->payname && $method ne 'ECHECK' ) {
-    $payname = $cust_main->payname;
-    $payname =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
-      or do {
-              #$dbh->rollback if $oldAutoCommit;
-              return "Illegal payname $payname";
-            };
-    ($payfirst, $paylast) = ($1, $2);
-  } else {
-    $payfirst = $cust_main->getfield('first');
-    $paylast = $cust_main->getfield('last');
-    $payname =  "$payfirst $paylast";
-  }
-
-  my @invoicing_list = grep { $_ ne 'POST' } $cust_main->invoicing_list;
-  if ( $conf->exists('emailinvoiceauto')
-       || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
-    push @invoicing_list, $cust_main->all_emails;
-  }
-  my $email = $invoicing_list[0];
-
-  my( $action1, $action2 ) = split(/\s*\,\s*/, $action );
-
   my $description = 'Internet Services';
   if ( $conf->exists('business-onlinepayment-description') ) {
     my $dtempl = $conf->config('business-onlinepayment-description');
@@ -714,163 +605,12 @@ sub realtime_bop {
         grep { $_->pkgnum } $self->cust_bill_pkg
     );
     $description = eval qq("$dtempl");
-
   }
 
-  my %content;
-  if ( $method eq 'CC' ) { 
-    $content{card_number} = $cust_main->payinfo;
-    $cust_main->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
-    $content{expiration} = "$2/$1";
-  } elsif ( $method eq 'ECHECK' ) {
-    my($account_number,$routing_code) = $cust_main->payinfo;
-    ( $content{account_number}, $content{routing_code} ) =
-      split('@', $cust_main->payinfo);
-    $content{bank_name} = $cust_main->payname;
-  } elsif ( $method eq 'LEC' ) {
-    $content{phone} = $cust_main->payinfo;
-  }
-  
-  my $transaction =
-    new Business::OnlinePayment( $processor, @$options );
-  $transaction->content(
-    'type'           => $method,
-    'login'          => $login,
-    'password'       => $password,
-    'action'         => $action1,
-    'description'    => $description,
-    'amount'         => $amount,
-    'invoice_number' => $self->invnum,
-    'customer_id'    => $self->custnum,
-    'last_name'      => $paylast,
-    'first_name'     => $payfirst,
-    'name'           => $payname,
-    'address'        => $address,
-    'city'           => $cust_main->city,
-    'state'          => $cust_main->state,
-    'zip'            => $cust_main->zip,
-    'country'        => $cust_main->country,
-    'referer'        => 'http://cleanwhisker.420.am/',
-    'email'          => $email,
-    'phone'          => $cust_main->daytime || $cust_main->night,
-    %content, #after
+  $cust_main->realtime_bop($method, $amount,
+    'description' => $description,
+    'invnum'      => $self->invnum,
   );
-  $transaction->submit();
-
-  if ( $transaction->is_success() && $action2 ) {
-    my $auth = $transaction->authorization;
-    my $ordernum = $transaction->can('order_number')
-                   ? $transaction->order_number
-                   : '';
-
-    #warn "********* $auth ***********\n";
-    #warn "********* $ordernum ***********\n";
-    my $capture =
-      new Business::OnlinePayment( $processor, @$options );
-
-    my %capture = (
-      %content,
-      type           => $method,
-      action         => $action2,
-      login          => $login,
-      password       => $password,
-      order_number   => $ordernum,
-      amount         => $amount,
-      authorization  => $auth,
-      description    => $description,
-    );
-
-    foreach my $field (qw( authorization_source_code returned_ACI                                          transaction_identifier validation_code           
-                           transaction_sequence_num local_transaction_date    
-                           local_transaction_time AVS_result_code          )) {
-      $capture{$field} = $transaction->$field() if $transaction->can($field);
-    }
-
-    $capture->content( %capture );
-
-    $capture->submit();
-
-    unless ( $capture->is_success ) {
-      my $e = "Authorization sucessful but capture failed, invnum #".
-              $self->invnum. ': '.  $capture->result_code.
-              ": ". $capture->error_message;
-      warn $e;
-      return $e;
-    }
-
-  }
-
-  if ( $transaction->is_success() ) {
-
-    my %method2payby = (
-      'CC'     => 'CARD',
-      'ECHECK' => 'CHEK',
-      'LEC'    => 'LECB',
-    );
-
-    my $cust_pay = new FS::cust_pay ( {
-       'invnum'   => $self->invnum,
-       'paid'     => $amount,
-       '_date'     => '',
-       'payby'    => $method2payby{$method},
-       'payinfo'  => $cust_main->payinfo,
-       'paybatch' => "$processor:". $transaction->authorization,
-    } );
-    my $error = $cust_pay->insert;
-    if ( $error ) {
-      # gah, even with transactions.
-      my $e = 'WARNING: Card/ACH debited but database not updated - '.
-              'error applying payment, invnum #' . $self->invnum.
-              " ($processor): $error";
-      warn $e;
-      return $e;
-    } else {
-      return '';
-    }
-  #} elsif ( $options{'report_badcard'} ) {
-  } else {
-
-    my $perror = "$processor error, invnum #". $self->invnum. ': '.
-                 $transaction->result_code. ": ". $transaction->error_message;
-
-    if ( !$quiet && $conf->exists('emaildecline')
-         && grep { $_ ne 'POST' } $cust_main->invoicing_list
-    ) {
-      my @templ = $conf->config('declinetemplate');
-      my $template = new Text::Template (
-        TYPE   => 'ARRAY',
-        SOURCE => [ map "$_\n", @templ ],
-      ) or return "($perror) can't create template: $Text::Template::ERROR";
-      $template->compile()
-        or return "($perror) can't compile template: $Text::Template::ERROR";
-
-      my $templ_hash = { error => $transaction->error_message };
-
-      #false laziness w/FS::cust_pay::delete & fs_signup_server && ::send
-      $ENV{MAILADDRESS} = $invoice_from;
-      my $header = new Mail::Header ( [
-        "From: $invoice_from",
-        "To: ". join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list ),
-        "Sender: $invoice_from",
-        "Reply-To: $invoice_from",
-        "Date: ". time2str("%a, %d %b %Y %X %z", time),
-        "Subject: Your payment could not be processed",
-      ] );
-      my $message = new Mail::Internet (
-        'Header' => $header,
-        'Body' => [ $template->fill_in(HASH => $templ_hash) ],
-      );
-      $!=0;
-      $message->smtpsend( Host => $smtpmachine )
-        or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
-          or return "($perror) (customer # ". $self->custnum.
-            ") can't send card decline email to ".
-            join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list ).
-            " via server $smtpmachine with SMTP: $!";
-    }
-  
-    return $perror;
-  }
 
 }
 
index 608c5e3..b162622 100644 (file)
@@ -10,6 +10,7 @@ use Date::Format;
 use Business::CreditCard;
 use FS::UID qw( getotaker dbh );
 use FS::Record qw( qsearchs qsearch dbdef );
+use FS::Misc qw( send_email );
 use FS::cust_pkg;
 use FS::cust_bill;
 use FS::cust_bill_pkg;
@@ -1408,6 +1409,221 @@ sub collect {
 
 }
 
+=item realtime_bop METHOD AMOUNT [ OPTION => VALUE ... ]
+
+
+Runs a realtime credit card, ACH (electronic check) or phone bill transaction
+via a Business::OnlinePayment realtime gateway.  See
+L<http://420.am/business-onlinepayment> for supported gateways.
+
+Available methods are: I<CC>, I<ECHECK> and I<LEC>
+
+Available options are: I<description>, I<invnum>, I<quiet>
+
+I<description> is a free-text field passed to the gateway.  It defaults to
+"Internet services".
+
+If an I<invnum> is specified, this payment (if sucessful) is applied to the
+specified invoice.  If you don't specify an I<invnum> you might want to
+call the B<apply_payments> method.
+
+I<quiet> can be set true to surpress email decline notices.
+
+(moved from cust_bill) (probably should get realtime_{card,ach,lec} here too)
+
+=cut
+
+sub realtime_bop {
+  my( $self, $method, $amount, %options ) = @_;
+  $options{'description'} ||= 'Internet services';
+
+  #pre-requisites
+  die "Real-time processing not enabled\n"
+    unless $conf->exists('business-onlinepayment');
+  eval "use Business::OnlinePayment";  
+  die $@ if $@;
+
+  #load up config
+  my $bop_config = 'business-onlinepayment';
+  $bop_config .= '-ach'
+    if $method eq 'ECHECK' && $conf->exists($bop_config. '-ach');
+  my ( $processor, $login, $password, $action, @bop_options ) =
+    $conf->config($bop_config);
+  $action ||= 'normal authorization';
+  pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/;
+
+  #massage data
+
+  my $address = $self->address1;
+  $address .= ", ". $self->address2 if $self->address2;
+
+  my($payname, $payfirst, $paylast);
+  if ( $self->payname && $method ne 'ECHECK' ) {
+    $payname = $self->payname;
+    $payname =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
+      or return "Illegal payname $payname";
+    ($payfirst, $paylast) = ($1, $2);
+  } else {
+    $payfirst = $self->getfield('first');
+    $paylast = $self->getfield('last');
+    $payname =  "$payfirst $paylast";
+  }
+
+  my @invoicing_list = grep { $_ ne 'POST' } $self->invoicing_list;
+  if ( $conf->exists('emailinvoiceauto')
+       || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
+    push @invoicing_list, $self->all_emails;
+  }
+  my $email = $invoicing_list[0];
+
+  my %content;
+  if ( $method eq 'CC' ) { 
+    $content{card_number} = $self->payinfo;
+    $self->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+    $content{expiration} = "$2/$1";
+  } elsif ( $method eq 'ECHECK' ) {
+    my($account_number,$routing_code) = $self->payinfo;
+    ( $content{account_number}, $content{routing_code} ) =
+      split('@', $self->payinfo);
+    $content{bank_name} = $self->payname;
+  } elsif ( $method eq 'LEC' ) {
+    $content{phone} = $self->payinfo;
+  }
+
+  #transaction(s)
+
+  my( $action1, $action2 ) = split(/\s*\,\s*/, $action );
+
+  my $transaction =
+    new Business::OnlinePayment( $processor, @bop_options );
+  $transaction->content(
+    'type'           => $method,
+    'login'          => $login,
+    'password'       => $password,
+    'action'         => $action1,
+    'description'    => $options{'description'},
+    'amount'         => $amount,
+    'invoice_number' => $options{'invnum'},
+    'customer_id'    => $self->custnum,
+    'last_name'      => $paylast,
+    'first_name'     => $payfirst,
+    'name'           => $payname,
+    'address'        => $address,
+    'city'           => $self->city,
+    'state'          => $self->state,
+    'zip'            => $self->zip,
+    'country'        => $self->country,
+    'referer'        => 'http://cleanwhisker.420.am/',
+    'email'          => $email,
+    'phone'          => $self->daytime || $self->night,
+    %content, #after
+  );
+  $transaction->submit();
+
+  if ( $transaction->is_success() && $action2 ) {
+    my $auth = $transaction->authorization;
+    my $ordernum = $transaction->can('order_number')
+                   ? $transaction->order_number
+                   : '';
+
+    my $capture =
+      new Business::OnlinePayment( $processor, @bop_options );
+
+    my %capture = (
+      %content,
+      type           => $method,
+      action         => $action2,
+      login          => $login,
+      password       => $password,
+      order_number   => $ordernum,
+      amount         => $amount,
+      authorization  => $auth,
+      description    => $options{'description'},
+    );
+
+    foreach my $field (qw( authorization_source_code returned_ACI                                          transaction_identifier validation_code           
+                           transaction_sequence_num local_transaction_date    
+                           local_transaction_time AVS_result_code          )) {
+      $capture{$field} = $transaction->$field() if $transaction->can($field);
+    }
+
+    $capture->content( %capture );
+
+    $capture->submit();
+
+    unless ( $capture->is_success ) {
+      my $e = "Authorization sucessful but capture failed, custnum #".
+              $self->custnum. ': '.  $capture->result_code.
+              ": ". $capture->error_message;
+      warn $e;
+      return $e;
+    }
+
+  }
+
+  #result handling
+  if ( $transaction->is_success() ) {
+
+    my %method2payby = (
+      'CC'     => 'CARD',
+      'ECHECK' => 'CHEK',
+      'LEC'    => 'LECB',
+    );
+
+    my $cust_pay = new FS::cust_pay ( {
+       'invnum'   => $self->invnum, #!!!!!!!!
+       'paid'     => $amount,
+       '_date'     => '',
+       'payby'    => $method2payby{$method},
+       'payinfo'  => $self->payinfo,
+       'paybatch' => "$processor:". $transaction->authorization,
+    } );
+    my $error = $cust_pay->insert;
+    if ( $error ) {
+      # gah, even with transactions.
+      my $e = 'WARNING: Card/ACH debited but database not updated - '.
+              'error applying payment, invnum #' . $self->invnum.
+              " ($processor): $error";
+      warn $e;
+      return $e;
+    } else {
+      return '';
+    }
+
+  } else {
+
+    my $perror = "$processor error: ". $transaction->error_message;
+
+    if ( !$options{'quiet'} && $conf->exists('emaildecline')
+         && grep { $_ ne 'POST' } $self->invoicing_list
+    ) {
+      my @templ = $conf->config('declinetemplate');
+      my $template = new Text::Template (
+        TYPE   => 'ARRAY',
+        SOURCE => [ map "$_\n", @templ ],
+      ) or return "($perror) can't create template: $Text::Template::ERROR";
+      $template->compile()
+        or return "($perror) can't compile template: $Text::Template::ERROR";
+
+      my $templ_hash = { error => $transaction->error_message };
+
+      my $error = send_email(
+        'from'    => $conf->config('invoice_from'),
+        'to'      => [ grep { $_ ne 'POST' } $self->invoicing_list ],
+        'subject' => 'Your payment could not be processed',
+        'body'    => [ $template->fill_in(HASH => $templ_hash) ],
+      );
+
+      $perror .= " (also received error sending decline notification: $error)"
+        if $error;
+
+    }
+  
+    return $perror;
+  }
+
+}
+
 =item total_owed
 
 Returns the total owed for this customer on all invoices
index 79cf827..55f2fc4 100644 (file)
@@ -1,13 +1,12 @@
 package FS::cust_pay;
 
 use strict;
-use vars qw( @ISA $conf $unsuspendauto $smtpmachine $invoice_from );
+use vars qw( @ISA $conf $unsuspendauto );
 use Date::Format;
-use Mail::Header;
-use Mail::Internet 1.44;
 use Business::CreditCard;
 use FS::UID qw( dbh );
 use FS::Record qw( dbh qsearch qsearchs dbh );
+use FS::Misc qw(send_email);
 use FS::cust_bill;
 use FS::cust_bill_pay;
 use FS::cust_main;
@@ -15,14 +14,10 @@ use FS::cust_main;
 @ISA = qw( FS::Record );
 
 #ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::cust_pay'} = sub { 
-
+FS::UID->install_callback( sub { 
   $conf = new FS::Conf;
   $unsuspendauto = $conf->exists('unsuspendauto');
-  $smtpmachine = $conf->config('smtpmachine');
-  $invoice_from = $conf->config('invoice_from');
-
-};
+} );
 
 =head1 NAME
 
@@ -265,19 +260,12 @@ sub delete {
   if ( $conf->config('deletepayments') ne '' ) {
 
     my $cust_main = qsearchs('cust_main',{ 'custnum' => $self->custnum });
-    #false laziness w/FS::cust_bill::send & fs_signup_server
-    $ENV{MAILADDRESS} = $invoice_from; #??? well as good as any
-    my $header = new Mail::Header ( [
-      "From: $invoice_from",
-      "To: ". $conf->config('deletepayments'),
-      "Sender: $invoice_from",
-      "Reply-To: $invoice_from",
-      "Date: ". time2str("%a, %d %b %Y %X %z", time),
-      "Subject: FREESIDE NOTIFICATION: Payment deleted",
-    ] );
-    my $message = new Mail::Internet (
-      'Header' => $header,
-      'Body' => [ 
+
+    my $error = send_email(
+      'from'    => $conf->config('invoice_from'), #??? well as good as any
+      'to'      => $conf->config('deletepayments'),
+      'subject' => 'FREESIDE NOTIFICATION: Payment deleted',
+      'body'    => [
         "This is an automatic message from your Freeside installation\n",
         "informing you that the following payment has been deleted:\n",
         "\n",
@@ -291,16 +279,12 @@ sub delete {
         'paybatch: '. $self->paybatch. "\n",
       ],
     );
-    $!=0;
-    $message->smtpsend( Host => $smtpmachine )
-      or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
-        or do {
-          $dbh->rollback if $oldAutoCommit;
-          return "(customer # ". $self->custnum.
-                 ") can't send payment deletion email to ".
-                 $conf->config('deletepayments').
-                 " via server $smtpmachine with SMTP: $!";
-        };
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "can't send payment deletion notification: $error";
+    }
+
   }
 
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -406,7 +390,7 @@ sub unapplied {
 
 =head1 VERSION
 
-$Id: cust_pay.pm,v 1.23 2002-11-19 09:51:58 ivan Exp $
+$Id: cust_pay.pm,v 1.24 2003-05-19 12:00:44 ivan Exp $
 
 =head1 BUGS
 
index bd3d1f5..9ab2695 100644 (file)
@@ -5,6 +5,7 @@ use vars qw(@ISA $disable_agentcheck);
 use vars qw( $quiet );
 use FS::UID qw( getotaker dbh );
 use FS::Record qw( qsearch qsearchs );
+use FS::Misc qw( send_email );
 use FS::cust_svc;
 use FS::part_pkg;
 use FS::cust_main;
@@ -20,12 +21,8 @@ use FS::svc_domain;
 use FS::svc_www;
 use FS::svc_forward;
 
-# need all this for sending cancel emails in sub cancel
-
+# for sending cancel emails in sub cancel
 use FS::Conf;
-use Date::Format;
-use Mail::Internet 1.44;
-use Mail::Header;
 
 @ISA = qw( FS::Record );
 
@@ -304,38 +301,16 @@ sub cancel {
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
   my $conf = new FS::Conf;
-
-  if ( !$quiet && $conf->exists('emailcancel')
-       && grep { $_ ne 'POST' } $self->cust_main->invoicing_list) {
-  
-      my @invoicing_list = $self->cust_main->invoicing_list;
-  
-      my $invoice_from = $conf->config('invoice_from');
-      my @print_text = map "$_\n", $conf->config('cancelmessage');
-      my $subject = $conf->config('cancelsubject');
-      my $smtpmachine = $conf->config('smtpmachine');
-      
-      if ( grep { $_ ne 'POST' } @invoicing_list ) { #email invoice
-         #false laziness w/FS::cust_pay::delete & fs_signup_server && ::realtime_card
-         #$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: $subject",           
-                                     ] );
-         my $message = new Mail::Internet (
-              'Header' => $header,
-              'Body' => [ @print_text ],      
-                                      );
-         $!=0;
-         $message->smtpsend( Host => $smtpmachine )
-             or $message->smtpsend( Host => $smtpmachine, Debug => 1 );
-         #should this return an error?
-         }
+  my @invoicing_list = grep { $_ ne 'POST' } $self->cust_main->invoicing_list;
+  if ( !$quiet && $conf->exists('emailcancel') && @invoicing_list ) {
+    my $conf = new FS::Conf;
+    my $error = send_email(
+      'from'    => $conf->config('invoice_from'),
+      'to'      => \@invoicing_list,
+      'subject' => $conf->config('cancelsubject'),
+      'body'    => [ map "$_\n", $conf->config('cancelmessage') ],
+    );
+    #should this do something on errors?
   }
 
   ''; #no errors
index 8f2861b..497e59c 100644 (file)
@@ -1024,36 +1024,28 @@ sub radius_groups {
 
 =item send_email
 
+This is the FS::svc_acct job-queue-able version.  It still uses
+FS::Misc::send_email under-the-hood.
+
 =cut
 
 sub send_email {
   my %opt = @_;
 
-  use Date::Format;
-  use Mail::Internet 1.44;
-  use Mail::Header;
+  eval "use FS::Misc qw(send_email)";
+  die $@ if $@;
 
   $opt{mimetype} ||= 'text/plain';
   $opt{mimetype} .= '; charset="iso-8859-1"' unless $opt{mimetype} =~ /charset/;
 
-  $ENV{MAILADDRESS} = $opt{from};
-  my $header = new Mail::Header ( [
-    "From: $opt{from}",
-    "To: $opt{to}",
-    "Sender: $opt{from}",
-    "Reply-To: $opt{from}",
-    "Date: ". time2str("%a, %d %b %Y %X %z", time),
-    "Subject: $opt{subject}",
-    "Content-Type: $opt{mimetype}",
-  ] );
-  my $message = new Mail::Internet (
-    'Header' => $header,
-    'Body' => [ map "$_\n", split("\n", $opt{body}) ],
+  my $error = send_email(
+    'from'         => $opt{from},
+    'to'           => $opt{to},
+    'subject'      => $opt{subject},
+    'content-type' => $opt{mimetype},
+    'body'         => [ map "$_\n", split("\n", $opt{body}) ],
   );
-  $!=0;
-  $message->smtpsend( Host => $smtpmachine )
-    or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
-      or die "can't send email to $opt{to} via $smtpmachine with SMTP: $!";
+  die $error if $error;
 }
 
 =item check_and_rebuild_fuzzyfiles
index 81edc33..2e8866a 100644 (file)
@@ -1,13 +1,11 @@
 package FS::svc_domain;
 
 use strict;
-use vars qw( @ISA $whois_hack $conf $smtpmachine
+use vars qw( @ISA $whois_hack $conf
   @defaultrecords $soadefaultttl $soaemail $soaexpire $soamachine
   $soarefresh $soaretry
 );
 use Carp;
-use Mail::Internet 1.44;
-use Mail::Header;
 use Date::Format;
 use Net::Whois 1.0;
 use FS::Record qw(fields qsearch qsearchs dbh);
@@ -26,8 +24,6 @@ use FS::queue;
 $FS::UID::callback{'FS::domain'} = sub { 
   $conf = new FS::Conf;
 
-  $smtpmachine = $conf->config('smtpmachine');
-
   @defaultrecords = $conf->config('defaultrecords');
   $soadefaultttl = $conf->config('soadefaultttl');
   $soaemail      = $conf->config('soaemail');
index 6397cc4..272b5b7 100644 (file)
@@ -36,6 +36,7 @@ FS/ClientAPI/passwd.pm
 FS/ClientAPI/MyAccount.pm
 FS/Conf.pm
 FS/ConfItem.pm
+FS/Misc.pm
 FS/Record.pm
 FS/SearchCache.pm
 FS/UI/Base.pm
@@ -125,6 +126,7 @@ t/InitHandler.t
 t/ClientAPI.t
 t/Conf.t
 t/ConfItem.t
+t/Misc.t
 t/Record.t
 t/UID.t
 t/Msgcat.t
diff --git a/FS/t/Misc.t b/FS/t/Misc.t
new file mode 100644 (file)
index 0000000..cc7751a
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Misc;
+$loaded=1;
+print "ok 1\n";