Added support for FAX invoice destinations using a HylaFAX server.
authorkhoff <khoff>
Mon, 21 Mar 2005 22:13:39 +0000 (22:13 +0000)
committerkhoff <khoff>
Mon, 21 Mar 2005 22:13:39 +0000 (22:13 +0000)
Faxing plain text invoices is not supported.

20 files changed:
FS/FS/ClientAPI/MyAccount.pm
FS/FS/Conf.pm
FS/FS/Misc.pm
FS/FS/cust_bill.pm
FS/FS/cust_main.pm
FS/FS/cust_main_invoice.pm
FS/FS/cust_pay.pm
FS/FS/cust_pkg.pm
FS/FS/part_export/http.pm
FS/FS/part_export/infostreet.pm
FS/FS/part_export/shellcommands.pm
FS/FS/svc_acct.pm
htetc/handler.pl
httemplate/docs/install.html
httemplate/edit/cust_main.cgi
httemplate/edit/process/cust_main.cgi
httemplate/misc/fax-invoice.cgi [new file with mode: 0755]
httemplate/view/cust_bill.cgi
httemplate/view/cust_main/billing.html
httemplate/view/cust_main/tickets.html

index d18a6e4..4789733 100644 (file)
@@ -131,7 +131,7 @@ sub customer_info {
     }
 
     $return{'invoicing_list'} =
-      join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list );
+      join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
     $return{'postal_invoicing'} =
       0 < ( grep { $_ eq 'POST' } $cust_main->invoicing_list );
 
index e21e583..4a97626 100644 (file)
@@ -1433,6 +1433,13 @@ httemplate/docs/config.html
     'select_enum' => [ 'Cache::SharedMemoryCache', 'Cache::FileCache', ], # '_Database' ],
   },
 
+  {
+    'key'         => 'hylafax',
+    'section'     => '',
+    'description' => 'Options for a HylaFAX server to enable the FAX invoice destination.  They should be in the form of a space separated list of arguments to the Fax::Hylafax::Client::sendfax subroutine.  You probably shouldn\'t override things like \'docfile\'.  *Note* Only supported when using typeset invoices (see the invoice_latex configuration option).',
+    'type'        => [qw( checkbox textarea )],
+  },
+
 );
 
 1;
index 71fe7e7..a7cd471 100644 (file)
@@ -5,7 +5,7 @@ use vars qw ( @ISA @EXPORT_OK );
 use Exporter;
 
 @ISA = qw( Exporter );
-@EXPORT_OK = qw( send_email );
+@EXPORT_OK = qw( send_email send_fax );
 
 =head1 NAME
 
@@ -50,6 +50,7 @@ use Date::Format;
 use Mail::Header;
 use Mail::Internet 1.44;
 use MIME::Entity;
+use Fax::Hylafax::Client;
 use FS::UID;
 
 FS::UID->install_callback( sub {
@@ -111,6 +112,77 @@ sub send_email {
 
 }
 
+=item send_fax OPTION => VALUE ...
+
+Options:
+
+I<dialstring> - (required) 10-digit phone number w/ area code
+
+I<docdata> - (required) Array ref containing PostScript or TIFF Class F document
+
+-or-
+
+I<docfile> - (required) Filename of PostScript TIFF Class F document
+
+...any other options will be passed to L<Fax::Hylafax::Client::sendfax>
+
+
+=cut
+
+sub send_fax {
+
+  my %options = @_;
+
+  die 'HylaFAX support has not been configured.'
+    unless $conf->exists('hylafax');
+
+  my %hylafax_opts = map { split /\s+/ } $conf->config('hylafax');
+
+  die 'Called send_fax without a \'dialstring\'.'
+    unless exists($options{'dialstring'});
+
+  if (exists($options{'docdata'}) and ref($options{'docdata'}) eq 'ARRAY') {
+      my $dir = $FS::UID::conf_dir. "cache.". $FS::UID::datasrc;
+      my $fh = new File::Temp(
+        TEMPLATE => 'faxdoc.'. $options{'dialstring'} . '.XXXXXXXX',
+        DIR      => $dir,
+        UNLINK   => 0,
+      ) or die "can't open temp file: $!\n";
+
+      $options{docfile} = $fh->filename;
+
+      print $fh @{$options{'docdata'}};
+      close $fh;
+
+      delete $options{'docdata'};
+  }
+
+  die 'Called send_fax without a \'docfile\' or \'docdata\'.'
+    unless exists($options{'docfile'});
+
+  #FIXME: Need to send canonical dialstring to HylaFAX, but this only
+  #       works in the US.
+
+  $options{'dialstring'} =~ s/[^\d\+]//g;
+  if ($options{'dialstring'} =~ /^\d{10}$/) {
+    $options{dialstring} = '+1' . $options{'dialstring'};
+  } else {
+    return 'Invalid dialstring ' . $options{'dialstring'} . '.';
+  }
+
+  my $faxjob = &Fax::Hylafax::Client::sendfax(%options, %hylafax_opts);
+
+  if ($faxjob->success) {
+    warn "Successfully queued fax to '$options{dialstring}' with jobid " .
+            $faxjob->jobid;
+  } else {
+    return 'Error while sending FAX: ' . $faxjob->trace;
+  }
+
+  return '';
+
+}
+
 package Mail::Internet;
 
 use Mail::Address;
@@ -191,6 +263,8 @@ This package exists.
 
 L<FS::UID>, L<FS::CGI>, L<FS::Record>, the base documentation.
 
+L<Fax::Hylafax::Client>
+
 =cut
 
 1;
index e70bb32..10b4928 100644 (file)
@@ -9,7 +9,7 @@ use File::Temp 0.14;
 use String::ShellQuote;
 use FS::UID qw( datasrc );
 use FS::Record qw( qsearch qsearchs );
-use FS::Misc qw( send_email );
+use FS::Misc qw( send_email send_fax );
 use FS::cust_main;
 use FS::cust_bill_pkg;
 use FS::cust_credit;
@@ -378,7 +378,7 @@ sub generate_email {
   if (ref($args{'to'} eq 'ARRAY')) {
     @invoicing_list = @{$args{'to'}};
   } else {
-    @invoicing_list = grep { $_ ne 'POST' } $self->cust_main->invoicing_list;
+    @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list;
   }
 
   return (
@@ -418,7 +418,7 @@ sub send {
   my @print_text = $self->print_text('', $template);
   my @invoicing_list = $self->cust_main->invoicing_list;
 
-  if ( grep { $_ ne 'POST' } @invoicing_list or !@invoicing_list  ) { #email
+  if ( grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list or !@invoicing_list  ) { #email
 
     #better to notify this person than silence
     @invoicing_list = ($invoice_from) unless @invoicing_list;
@@ -426,7 +426,7 @@ sub send {
     my $error = send_email(
       $self->generate_email(
         'from'   => $invoice_from,
-        'to'     => [ grep { $_ ne 'POST' } @invoicing_list ],
+        'to'     => [ grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ],
        'print_text' => [ @print_text ],
       )
     );
@@ -435,16 +435,34 @@ sub send {
 
   }
 
-  if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal
-    @print_text = $self->print_ps('', $template)
-      if $conf->config('invoice_latex');
-    my $lpr = $conf->config('lpr');
-    open(LPR, "|$lpr")
-      or die "Can't open pipe to $lpr: $!\n";
-    print LPR @print_text;
-    close LPR
-      or die $! ? "Error closing $lpr: $!\n"
-                : "Exit status $? from $lpr\n";
+  if ( grep { $_ =~ /^(POST|FAX)$/ } @invoicing_list ) {
+    my $lpr_data;
+    if ($conf->config('invoice_latex')) {
+      $lpr_data = [ $self->print_ps('', $template) ];
+    } else {
+      $lpr_data = \@print_text;
+    }
+
+    if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal
+      my $lpr = $conf->config('lpr');
+      open(LPR, "|$lpr")
+        or die "Can't open pipe to $lpr: $!\n";
+      print LPR @{$lpr_data};
+      close LPR
+        or die $! ? "Error closing $lpr: $!\n"
+                  : "Exit status $? from $lpr\n";
+    }
+
+    if ( grep { $_ eq 'FAX' } @invoicing_list ) { #fax
+      unless ($conf->exists('invoice_latex')) {
+       die 'FAX invoice destination not supported with plain text invoices.'
+      }
+      my $dialstring = $self->cust_main->getfield('fax');
+      #Check $dialstring?
+      my $error = send_fax(docdata => $lpr_data, dialstring => $dialstring);
+      die $error if $error;
+    }
+
   }
 
   '';
index 8246b93..4fcb226 100644 (file)
@@ -2655,6 +2655,11 @@ is an error, returns the error, otherwise returns false.
 sub check_invoicing_list {
   my( $self, $arrayref ) = @_;
   foreach my $address ( @{$arrayref} ) {
+
+    if ($address eq 'FAX' and $self->getfield('fax') eq '') {
+      return 'Can\'t add FAX invoice destination with a blank FAX number.';
+    }
+
     my $cust_main_invoice = new FS::cust_main_invoice ( {
       'custnum' => $self->custnum,
       'dest'    => $address,
index 3cabd3a..03863b8 100644 (file)
@@ -43,7 +43,7 @@ FS::Record.  The following fields are currently supported:
 
 =item custnum - customer (see L<FS::cust_main>)
 
-=item dest - Invoice destination: If numeric, a svcnum (see L<FS::svc_acct>), if string, a literal email address, or `POST' to enable mailing (the default if no cust_main_invoice records exist)
+=item dest - Invoice destination: If numeric, a svcnum (see L<FS::svc_acct>), if string, a literal email address, `POST' to enable mailing (the default if no cust_main_invoice records exist), or `FAX' to enable faxing via a HylaFAX server.
 
 =back
 
@@ -127,7 +127,7 @@ sub checkdest {
   my $error = $self->ut_text('dest');
   return $error if $error;
 
-  if ( $self->dest eq 'POST' ) {
+  if ( $self->dest =~ /^(POST|FAX)$/ ) {
     #contemplate our navel
   } elsif ( $self->dest =~ /^(\d+)$/ ) {
     return "Unknown local account (specified by svcnum: ". $self->dest. ")"
@@ -144,7 +144,7 @@ sub checkdest {
 
 =item address
 
-Returns the literal email address for this record (or `POST').
+Returns the literal email address for this record (or `POST' or `FAX').
 
 =cut
 
index 80d4a14..fa97ba9 100644 (file)
@@ -167,7 +167,7 @@ sub insert {
 
   #my $cust_main = $self->cust_main;
   if ( $conf->exists('payment_receipt_email')
-       && grep { $_ ne 'POST' } $cust_main->invoicing_list
+       && grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list
   ) {
 
     my $receipt_template = new Text::Template (
@@ -178,7 +178,7 @@ sub insert {
       return '';
     };
 
-    my @invoicing_list = grep { $_ ne 'POST' } $cust_main->invoicing_list;
+    my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list;
 
     my $payby = $self->payby;
     my $payinfo = $self->payinfo;
index e3724a3..f5c1de3 100644 (file)
@@ -423,7 +423,7 @@ sub cancel {
   $dbh->commit or die $dbh->errstr if $oldAutoCommit;
 
   my $conf = new FS::Conf;
-  my @invoicing_list = grep { $_ ne 'POST' } $self->cust_main->invoicing_list;
+  my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list;
   if ( !$options{'quiet'} && $conf->exists('emailcancel') && @invoicing_list ) {
     my $conf = new FS::Conf;
     my $error = send_email(
index 0be2a0f..55d8329 100644 (file)
@@ -18,7 +18,7 @@ tie my %options, 'Tie::IxHash',
     type    => 'textarea',
     default => join("\n",
       'DomainName $svc_x->domain',
-      'Email ( grep { $_ ne "POST" } $svc_x->cust_svc->cust_pkg->cust_main->invoicing_list)[0]',
+      'Email ( grep { $_ !~ /^(POST|FAX)$/ } $svc_x->cust_svc->cust_pkg->cust_main->invoicing_list)[0]',
       'test 1',
       'reseller $svc_x->cust_svc->cust_pkg->part_pkg->pkg =~ /reseller/i',
     ),
index 309e7ce..ef16c7c 100644 (file)
@@ -67,7 +67,7 @@ sub _export_insert {
     $_ => $cust_main->getfield( $infostreet2cust_main{$_} );
   } keys %infostreet2cust_main );
 
-  my @emails = grep { $_ ne 'POST' } $cust_main->invoicing_list;
+  my @emails = grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list;
   $contact_info{'email'} = $emails[0] if @emails;
 
   #this one is kinda noment-specific
index 4f201cf..665ec47 100644 (file)
@@ -216,7 +216,7 @@ sub _export_command {
 
   my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
   if ( $cust_pkg ) {
-    $email = ( grep { $_ ne 'POST' } $cust_pkg->cust_main->invoicing_list )[0];
+    $email = ( grep { $_ !~ /^(POST|FAX)$/ } $cust_pkg->cust_main->invoicing_list )[0];
   } else {
     $email = '';
   }
index 35596e3..109ea1d 100644 (file)
@@ -296,7 +296,7 @@ sub insert {
     #welcome email
     my $to = '';
     if ( $welcome_template && $cust_pkg ) {
-      my $to = join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list );
+      my $to = join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list );
       if ( $to ) {
         my $wqueue = new FS::queue {
           'svcnum' => $self->svcnum,
index c144eca..b44e3f2 100644 (file)
@@ -118,7 +118,7 @@ sub handler
                      small_custview myexit http_header);
       use FS::UI::Web;
       use FS::Msgcat qw(gettext geterror);
-      use FS::Misc qw( send_email );
+      use FS::Misc qw( send_email send_fax );
       use FS::Report::Table::Monthly;
       use FS::TicketSystem;
 
index eaebcde..10c51e7 100644 (file)
@@ -68,6 +68,7 @@ Before installing, you need:
       <li><a href="http://search.cpan.org/dist/Frontier-RPC">Frontier::RPC (Frontier::RPC2)</a>
       <li><a href="http://search.cpan.org/search?mode=module&query=MIME::Entity">MIME::Entity (MIME-tools)</a>
 <!--      <li><a href="http://search.cpan.org/dist/Crypt-YAPassGen">Crypt::YAPassGen</a> -->
+      <li><a href="http://search.cpan.org/search?mode=module&query=MIME::Entity">Fax::Hylafax::Client</a> <i>(Required if using FAX invoice destinations</i>
       <li><a href="http://search.cpan.org/dist/ApacheDBI">Apache::DBI</a> <i>(optional but recommended for better webinterface performance)</i>
     </ul>
 </ul>
index 3910b4b..61468f3 100755 (executable)
@@ -302,8 +302,11 @@ if ( $payby_default eq 'HIDE' ) {
   print qq! CHECKED!
     if ( ! @invoicing_list && ! $conf->exists('disablepostalinvoicedefault') )
        || grep { $_ eq 'POST' } @invoicing_list;
-  print qq!>Postal mail invoice</TD></TR>!;
-  my $invoicing_list = join(', ', grep { $_ ne 'POST' } @invoicing_list );
+  print qq!>Postal mail invoice</TD></TR><TR><TD>!;
+  print qq!<INPUT TYPE="checkbox" NAME="invoicing_list_FAX" VALUE="FAX"!;
+  print qq! CHECKED! if (grep { $_ eq 'FAX' } @invoicing_list);
+  print qq!>FAX invoice</TD></TR>!;
+  my $invoicing_list = join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list );
   print qq!<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="$invoicing_list"></TD></TR>!;
 
   print "<TR><TD>Billing type</TD></TR>",
index a1d3698..d2773a6 100755 (executable)
@@ -25,6 +25,7 @@ if ( $payby ) {
 
 my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') );
 push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
+push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX');
 $cgi->param('invoicing_list', join(',', @invoicing_list) );
 
 
diff --git a/httemplate/misc/fax-invoice.cgi b/httemplate/misc/fax-invoice.cgi
new file mode 100755 (executable)
index 0000000..46b2a17
--- /dev/null
@@ -0,0 +1,25 @@
+<%
+
+my $conf = new FS::Conf;
+my $lpr = $conf->config('lpr');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d*)$/;
+my $invnum = $1;
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Can't find invoice!\n" unless $cust_bill;
+
+
+my $error = &FS::Misc::send_fax(
+  dialstring => $cust_bill->cust_main->getfield('fax'),
+  docdata       => [ $cust_bill->print_ps ],
+);
+
+die $error if $error;
+
+my $custnum = $cust_bill->getfield('custnum');
+
+print $cgi->redirect("${p}view/cust_main.cgi?$custnum");
+
+%>
index ca0612d..c217cc3 100755 (executable)
@@ -7,6 +7,8 @@ $query =~ /^((.+)-)?(\d+)$/;
 my $templatename = $2;
 my $invnum = $3;
 
+my $conf = new FS::Conf;
+
 my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
 die "Invoice #$invnum not found!" unless $cust_bill;
 my $custnum = $cust_bill->getfield('custnum');
@@ -27,9 +29,11 @@ if ( grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ) {
         qq!Re-email this invoice</A>!;
 }
 
+print qq! | <A HREF="${p}misc/fax-invoice.cgi?$invnum">Refax this invoice</A>!
+  if ($conf->exists('hylafax'));
+
 print '<BR><BR>';
 
-my $conf = new FS::Conf;
 if ( $conf->exists('invoice_latex') ) {
   my $link = "${p}view/cust_bill-pdf.cgi?";
   $link .= "$templatename-" if $templatename;
index 18a203b..561fff9 100644 (file)
@@ -17,9 +17,15 @@ Billing information
   </TD>
 </TR>
 <TR>
+  <TD ALIGN="right">FAX&nbsp;invoices</TD>
+  <TD BGCOLOR="#ffffff">
+    <%= ( grep { $_ eq 'FAX' } @invoicing_list ) ? 'yes' : 'no' %>
+  </TD>
+</TR>
+<TR>
   <TD ALIGN="right">Email&nbsp;invoices</TD>
   <TD BGCOLOR="#ffffff">
-    <%= join(', ', grep { $_ ne 'POST' } @invoicing_list ) || 'no' %>
+    <%= join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) || 'no' %>
   </TD>
 </TR>
 <TR>
index d6ddfa6..ea70d70 100644 (file)
@@ -30,7 +30,7 @@
 
 Highest priority tickets
 (<A HREF="<%= FS::TicketSystem->href_customer_tickets($cust_main->custnum) %>">View all tickets for this customer</A>)
-(<A HREF="<%= FS::TicketSystem->href_new_ticket($cust_main->custnum, join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list ) ) %>">New ticket for this customer</A>)
+(<A HREF="<%= FS::TicketSystem->href_new_ticket($cust_main->custnum, join(', ', grep { $_ !~ /^(POST|FAX)$/ } $cust_main->invoicing_list ) ) %>">New ticket for this customer</A>)
 <%= table() %>
 <TR>
   <TH>#</TH>