simple payment CSV import
authorjeff <jeff>
Fri, 25 Jan 2008 18:29:04 +0000 (18:29 +0000)
committerjeff <jeff>
Fri, 25 Jan 2008 18:29:04 +0000 (18:29 +0000)
FS/FS/cust_pay.pm
httemplate/elements/menu.html
httemplate/misc/cust_pay-import.cgi [new file with mode: 0644]
httemplate/misc/process/cust_pay-import.cgi [new file with mode: 0644]

index f969460..2b2e944 100644 (file)
@@ -650,6 +650,152 @@ sub unapplied_sql {
 
 =back
 
+=head1 SUBROUTINES
+
+=over 4 
+
+=item batch_import HASHREF
+
+Inserts new payments.
+
+=cut
+
+sub batch_import {
+  my $param = shift;
+
+  my $fh = $param->{filehandle};
+  my $agentnum = $param->{agentnum};
+  my $format = $param->{'format'};
+  my $paybatch = $param->{'paybatch'};
+
+  # here is the agent virtualization
+  my $extra_sql = ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+  my @fields;
+  my $payby;
+  if ( $format eq 'simple' ) {
+    @fields = qw( custnum agent_custid paid payinfo );
+    $payby = 'BILL';
+  } elsif ( $format eq 'extended' ) {
+    die "unimplemented\n";
+    @fields = qw( );
+    $payby = 'BILL';
+  } else {
+    die "unknown format $format";
+  }
+
+  eval "use Text::CSV_XS;";
+  die $@ if $@;
+
+  my $csv = new Text::CSV_XS;
+
+  my $imported = 0;
+
+  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 $line;
+  while ( defined($line=<$fh>) ) {
+
+    $csv->parse($line) or do {
+      $dbh->rollback if $oldAutoCommit;
+      return "can't parse: ". $csv->error_input();
+    };
+
+    my @columns = $csv->fields();
+
+    my %cust_pay = (
+      payby    => $payby,
+      paybatch => $paybatch,
+    );
+
+    my $cust_main;
+    foreach my $field ( @fields ) {
+
+      if ( $field eq 'agent_custid'
+        && $agentnum
+        && $columns[0] =~ /\S+/ )
+      {
+
+        my $agent_custid = $columns[0];
+        my %hash = ( 'agent_custid' => $agent_custid,
+                     'agentnum'     => $agentnum,
+                   );
+
+        if ( $cust_pay{'custnum'} !~ /^\s*$/ ) {
+          $dbh->rollback if $oldAutoCommit;
+          return "can't specify custnum with agent_custid $agent_custid";
+        }
+
+        $cust_main = qsearchs({
+                                'table'     => 'cust_main',
+                                'hashref'   => \%hash,
+                                'extra_sql' => $extra_sql,
+                             });
+
+        unless ( $cust_main ) {
+          $dbh->rollback if $oldAutoCommit;
+          return "can't find customer with agent_custid $agent_custid";
+        }
+
+        $field = 'custnum';
+        $columns[0] = $cust_main->custnum;
+      }
+
+      $cust_pay{$field} = shift @columns; 
+    }
+
+    my $cust_pay = new FS::cust_pay( \%cust_pay );
+    my $error = $cust_pay->insert;
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "can't insert payment for $line: $error";
+    }
+
+    if ( $format eq 'simple' ) {
+      # include agentnum for less surprise?
+      $cust_main = qsearchs({
+                             'table'     => 'cust_main',
+                             'hashref'   => { 'custnum' => $cust_pay->custnum },
+                             'extra_sql' => $extra_sql,
+                           })
+        unless $cust_main;
+
+      unless ( $cust_main ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "can't find customer to which payments apply at line: $line";
+      }
+
+      $error = $cust_main->apply_payments_and_credits;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "can't apply payments to customer for $line: $error";
+      }
+
+    }
+
+    $imported++;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  return "Empty file!" unless $imported;
+
+  ''; #no error
+
+}
+
+=back
+
 =head1 BUGS
 
 Delete and replace methods.  
index 8d56adc..f43fca1 100644 (file)
@@ -216,6 +216,8 @@ $report_menu{'Financial'}  = [ \%report_financial, 'Financial reports' ]
 
 tie my %tools_importing, 'Tie::IxHash',
   'Import customers from CSV file' => [ $fsurl.'misc/cust_main-import.cgi', '' ],
+  'Import payments from CSV file' => [ $fsurl.'misc/cust_pay-import.cgi', '' ],
+
   'Import customer comments from CSV file' => [ $fsurl.'misc/cust_main_note-import.html', '' ],
   'Import one-time charges from CSV file' => [ $fsurl.'misc/cust_main-import_charges.cgi', '' ],
   'Import Call Detail Records (CDRs) from CSV file' => [ $fsurl.'misc/cdr-import.html', '' ],
diff --git a/httemplate/misc/cust_pay-import.cgi b/httemplate/misc/cust_pay-import.cgi
new file mode 100644 (file)
index 0000000..849a25b
--- /dev/null
@@ -0,0 +1,62 @@
+<% include("/elements/header.html",'Batch Payment Import') %>
+
+Import a CSV file containing customer payments.
+<BR><BR>
+
+<FORM ACTION="process/cust_pay-import.cgi" METHOD="post" ENCTYPE="multipart/form-data">
+
+<% &ntable("#cccccc", 2) %>
+
+<% include('/elements/tr-select-agent.html',
+              #'curr_value' => '', #$agentnum,
+              'label'       => "<B>Agent</B>",
+              'empty_label' => 'Select agent',
+           )
+%>
+
+<TR>
+  <TH ALIGN="right">Format</TH>
+  <TD>
+    <SELECT NAME="format">
+      <OPTION VALUE="simple">Simple
+<!--      <OPTION VALUE="extended" SELECTED>Extended -->
+    </SELECT>
+  </TD>
+</TR>
+
+<TR>
+  <TH ALIGN="right">CSV filename</TH>
+  <TD><INPUT TYPE="file" NAME="csvfile"></TD>
+</TR>
+
+<TR><TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"><INPUT TYPE="submit" VALUE="Import CSV file"></TD></TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+
+Simple file format is CSV, with the following field order: <i>custnum, agent_custid, amount, checknum</i>
+<BR><BR>
+
+<!-- Extended file format is not yet defined</i>
+<BR><BR> -->
+
+Field information:
+
+<ul>
+
+  <li><i>custnum</i>: This is the freeside customer number.  It may be left blank.  If specified, agent_custid must be blank.
+
+  <li><i>agent_custid</i>: This is the reseller's idea of the customer number or identifier.  It may be left blank.  If specified, custnum must be blank.
+
+  <li><i>amount</i>: A positive numeric value with at most two digits after the decimal point.
+
+  <li><i>checknum</i>: A sequences of digits.  May be left blank.
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
diff --git a/httemplate/misc/process/cust_pay-import.cgi b/httemplate/misc/process/cust_pay-import.cgi
new file mode 100644 (file)
index 0000000..d4ff226
--- /dev/null
@@ -0,0 +1,21 @@
+<% $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %> 
+<%init>
+
+my $fh = $cgi->upload('csvfile');
+
+# webbatch?  I suppose
+my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+my $error = defined($fh)
+  ? FS::cust_pay::batch_import( {
+      'filehandle' => $fh,
+      'agentnum'   => scalar($cgi->param('agentnum')),
+      'format'     => scalar($cgi->param('format')),
+      'paybatch'   => $paybatch,
+    } )
+  : 'No file';
+
+errorpage($error)
+  if ( $error );
+
+</%init>