[freeside-commits] branch master updated. 1ac4a177bd93ad7a97a45aacb66aa0bc9c23726b

Jonathan Prykop jonathan at 420.am
Fri Feb 27 22:06:22 PST 2015


The branch, master has been updated
       via  1ac4a177bd93ad7a97a45aacb66aa0bc9c23726b (commit)
      from  aec7743508bd9565dd076ec019dbb3d5b7cedd9c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 1ac4a177bd93ad7a97a45aacb66aa0bc9c23726b
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Sat Feb 28 00:04:43 2015 -0600

    RT#30825: Modernize Bulk payment importing

diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index 35ed6f7..0f3685b 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -1803,6 +1803,7 @@ sub process_batch_import {
     format_xml_formats         => $opt->{format_xml_formats},
     format_asn_formats         => $opt->{format_asn_formats},
     format_row_callbacks       => $opt->{format_row_callbacks},
+    format_hash_callbacks      => $opt->{format_hash_callbacks},
     #per-import
     job                        => $job,
     file                       => $file,
@@ -1811,6 +1812,7 @@ sub process_batch_import {
     params                     => { map { $_ => $param->{$_} } @pass_params },
     #?
     default_csv                => $opt->{default_csv},
+    preinsert_callback         => $opt->{preinsert_callback},
     postinsert_callback        => $opt->{postinsert_callback},
     insert_args_callback       => $opt->{insert_args_callback},
   );
@@ -1849,6 +1851,8 @@ Class method for batch imports.  Available params:
 
 =item format_row_callbacks
 
+=item format_hash_callbacks - After parsing, before object creation
+
 =item fields - Alternate way to specify import, specifying import fields directly as a listref
 
 =item preinsert_callback
@@ -1891,7 +1895,7 @@ sub batch_import {
 
   my( $type, $header, $sep_char,
       $fixedlength_format, $xml_format, $asn_format,
-      $parser_opt, $row_callback, @fields );
+      $parser_opt, $row_callback, $hash_callback, @fields );
 
   my $postinsert_callback = '';
   $postinsert_callback = $param->{'postinsert_callback'}
@@ -1947,6 +1951,11 @@ sub batch_import {
         ? $param->{'format_row_callbacks'}{ $param->{'format'} }
         : '';
 
+    $hash_callback =
+      $param->{'format_hash_callbacks'}
+        ? $param->{'format_hash_callbacks'}{ $param->{'format'} }
+        : '';
+
     @fields = @{ $formats->{ $format } };
 
   } elsif ( $param->{'fields'} ) {
@@ -1956,6 +1965,7 @@ sub batch_import {
     $sep_char = ',';
     $fixedlength_format = '';
     $row_callback = '';
+    $hash_callback = '';
     @fields = @{ $param->{'fields'} };
 
   } else {
@@ -2181,6 +2191,8 @@ sub batch_import {
       $hash{custnum} = $2;
     }
 
+    %hash = &{$hash_callback}(%hash) if $hash_callback;
+
     #my $table   = $param->{table};
     my $class = "FS::$table";
 
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index 8274b3d..0a36aca 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -239,6 +239,12 @@ sub insert {
         $dbh->rollback if $oldAutoCommit;
         return "Unknown cust_bill.invnum: ". $self->invnum;
       };
+    if ($self->custnum && ($cust_bill->custnum ne $self->custnum)) {
+      $dbh->rollback if $oldAutoCommit;
+      return "Invoice custnum ".$cust_bill->custnum
+        ." does not match specified custnum ".$self->custnum
+        ." for invoice ".$self->invnum;
+    }
     $self->custnum($cust_bill->custnum );
   }
 
@@ -1157,6 +1163,87 @@ sub process_upgrade_paybatch {
 
 =over 4 
 
+=item process_batch_import
+
+=cut
+
+sub process_batch_import {
+  my $job = shift;
+
+  #agent_custid isn't a cust_pay field, see hash callback
+  my $format = [ qw(custnum agent_custid paid payinfo invnum) ];
+  my $hashcb = sub {
+    my %hash = @_;
+    my $custnum = $hash{'custnum'};
+    my $agent_custid = $hash{'agent_custid'};
+    #standardize date
+    $hash{'_date'} = parse_datetime($hash{'_date'})
+      if $hash{'_date'} && $hash{'_date'} =~ /\D/;
+    # translate agent_custid into regular custnum
+    if ($custnum && $agent_custid) {
+      die "can't specify both custnum and agent_custid\n";
+    } elsif ($agent_custid) {
+      # here is the agent virtualization
+      my $extra_sql = ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql;
+      my $agentnum = $hash{'agentnum'};
+      my %search = (
+        'agent_custid' => $agent_custid,
+        'agentnum'     => $agentnum,
+      );
+      my $cust_main = qsearchs({
+        'table'     => 'cust_main',
+        'hashref'   => \%search,
+        'extra_sql' => $extra_sql,
+      });
+      die "can't find customer with agent_custid $agent_custid\n"
+        unless $cust_main;
+      $custnum = $cust_main->custnum;
+    }
+    #remove custnum_prefix
+    my $custnum_prefix = $conf->config('cust_main-custnum-display_prefix');
+    my $custnum_length = $conf->config('cust_main-custnum-display_length') || 8;
+    if (
+      $custnum_prefix 
+      && $custnum =~ /^$custnum_prefix(0*([1-9]\d*))$/
+      && length($1) == $custnum_length 
+    ) {
+      $custnum = $2;
+    }
+    $hash{'custnum'} = $custnum;
+    delete($hash{'agent_custid'});
+    return %hash;
+  };
+
+  my $opt = { 'table'   => 'cust_pay',
+              'params'  => [ '_date', 'agentnum', 'payby', 'paybatch' ],
+              'formats' => {
+                'simple-csv' => $format,
+                'simple-xls' => $format,
+              },
+              'format_types' => {
+                'simple-csv' => 'csv',
+                'simple-xls' => 'xls',
+              },
+              'default_csv' => 1,
+              'format_hash_callbacks' => { 
+                'simple-csv' => $hashcb,
+                'simple-xls' => $hashcb,
+              },
+              'postinsert_callback' => sub {
+                 my $cust_pay = shift;
+                 my $cust_main = $cust_pay->cust_main ||
+                   return "can't find customer to which payments apply";
+                 my $error = $cust_main->apply_payments_and_credits;
+                 return $error
+                   ? "can't apply payments to customer ".$cust_pay->custnum."$error"
+                   : '';
+              },
+            };
+
+  FS::Record::process_batch_import( $job, $opt, @_ );
+
+}
+
 =item batch_import HASHREF
 
 Inserts new payments.
diff --git a/FS/FS/payby.pm b/FS/FS/payby.pm
index 13423c4..5b4559d 100644
--- a/FS/FS/payby.pm
+++ b/FS/FS/payby.pm
@@ -224,6 +224,28 @@ sub cust_payby2longname {
   map { $_ => $hash{$_}->{longname} } $self->cust_payby;
 }
 
+=item payment_payby
+
+Returns all values of payby that can be used by payments.
+
+=cut
+
+sub payment_payby {
+  my $self = shift;
+  grep { ! exists $hash{$_}->{cust_pay} } $self->payby;
+}
+
+=item payment_payby2longname
+
+Returns hash, keys are L</payment_payby> types, values are payby longname.
+
+=cut
+
+sub payment_payby2longname {
+  my $self = shift;
+  map { $_ => $hash{$_}->{longname} } $self->payment_payby;
+}
+
 =back
 
 =head1 BUGS
diff --git a/httemplate/elements/select-payby.html b/httemplate/elements/select-payby.html
index b2d5421..2018874 100644
--- a/httemplate/elements/select-payby.html
+++ b/httemplate/elements/select-payby.html
@@ -3,7 +3,7 @@
         <% $onchange %>
 >
 
-% unless ( $opt{'multiple'} ) {
+% unless ( $opt{'multiple'} || $opt{'no_all'} ) {
     <OPTION VALUE="" <% '' eq $value ? 'SELECTED' : '' %> ><% mt('all') |h %> 
 % }
 
diff --git a/httemplate/misc/cust_pay-import.cgi b/httemplate/misc/cust_pay-import.cgi
index 05a6c4f..ee0154d 100644
--- a/httemplate/misc/cust_pay-import.cgi
+++ b/httemplate/misc/cust_pay-import.cgi
@@ -1,22 +1,31 @@
 <& /elements/header.html, 'Batch Payment Import' &>
 
-Import a CSV file containing customer payments.
+Import a file containing customer payments.
 <BR><BR>
 
-<FORM ACTION="process/cust_pay-import.cgi" METHOD="post" ENCTYPE="multipart/form-data">
+
+<% include( '/elements/form-file_upload.html',
+     'name'      => 'OneTrueForm',
+     'action'    => 'process/cust_pay-import.cgi', #progress-init target
+     'fields'    => [ 'agentnum', '_date', 'paybatch', 'format', 'payby' ],
+     'num_files' => 1,
+     'url' => popurl(2)."search/cust_pay.html?magic=paybatch;paybatch=$paybatch",
+     'message' => 'Batch Payment Imported',
+   )
+%>
 
 <% &ntable("#cccccc", 2) %>
 
+<INPUT TYPE="hidden" NAME="paybatch" VALUE="<% $paybatch | h %>">
+
 <& /elements/tr-select-agent.html,
-     #'curr_value' => '', #$agentnum,
      'label'       => "<B>Agent</B>",
      'empty_label' => 'Select agent',
 &>
 
 <& /elements/tr-input-date-field.html, {
      'name'  => '_date',
-     #'value' => '',
-     'label' => 'Date',
+     'label' => '<B>Date</B>',
    }
 &>
 
@@ -24,18 +33,26 @@ Import a CSV file containing customer payments.
   <TH ALIGN="right">Format</TH>
   <TD>
     <SELECT NAME="format">
-      <OPTION VALUE="simple">Simple
-<!--      <OPTION VALUE="extended" SELECTED>Extended -->
+      <OPTION VALUE="simple-csv">Comma-separated (.csv)</OPTION>
+      <OPTION VALUE="simple-xls">Excel (.xls)</OPTION>
     </SELECT>
   </TD>
 </TR>
 
-<TR>
-  <TH ALIGN="right">CSV filename</TH>
-  <TD><INPUT TYPE="file" NAME="csvfile"></TD>
-</TR>
+<% include( '/elements/tr-select-payby.html',
+     'paybys' => \%paybys,
+     'no_all' => 1,
+     'label'  => '<B>Payment type</B>',
+   )
+%>
+
+<% include( '/elements/file-upload.html',
+             'field'    => 'file',
+             'label'    => 'Filename',
+   )
+%>
 
-<TR><TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"><INPUT TYPE="submit" VALUE="Import CSV file"></TD></TR>
+<TR><TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"><INPUT TYPE="submit" VALUE="Import file"></TD></TR>
 
 </TABLE>
 
@@ -43,11 +60,10 @@ Import a CSV file containing customer payments.
 
 <BR>
 
-Simple file format is CSV, with the following field order: <i>custnum, agent_custid, amount, checknum</i>
+Simple file format is CSV or XLS, with the following field order: <i>custnum, agent_custid, amount, checknum, invnum</i>
 <BR><BR>
 
-<!-- Extended file format is not yet defined</i>
-<BR><BR> -->
+<!-- Extended file format is not yet defined -->
 
 Field information:
 
@@ -68,3 +84,9 @@ Field information:
 <BR>
 
 <& /elements/footer.html &>
+
+<%init>
+my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+my %paybys;
+tie %paybys, 'Tie::IxHash', FS::payby->payment_payby2longname();
+</%init>
diff --git a/httemplate/misc/process/cust_pay-import.cgi b/httemplate/misc/process/cust_pay-import.cgi
index 7711773..2981da4 100644
--- a/httemplate/misc/process/cust_pay-import.cgi
+++ b/httemplate/misc/process/cust_pay-import.cgi
@@ -1,23 +1,5 @@
-<% $cgi->redirect(popurl(3). "search/cust_pay.html?magic=paybatch;paybatch=$paybatch") %> 
+<% $server->process %>
 <%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,
-      'format'     => scalar($cgi->param('format')),
-
-      'agentnum'   => scalar($cgi->param('agentnum')),
-      '_date'      => scalar($cgi->param('_date')),
-      'paybatch'   => $paybatch,
-    } )
-  : 'No file';
-
-errorpage($error)
-  if ( $error );
-
+my $server = new FS::UI::Web::JSRPC 'FS::cust_pay::process_batch_import', $cgi; 
 </%init>
+

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Record.pm                             |   14 ++++-
 FS/FS/cust_pay.pm                           |   87 +++++++++++++++++++++++++++
 FS/FS/payby.pm                              |   22 +++++++
 httemplate/elements/select-payby.html       |    2 +-
 httemplate/misc/cust_pay-import.cgi         |   52 +++++++++++-----
 httemplate/misc/process/cust_pay-import.cgi |   24 +-------
 6 files changed, 163 insertions(+), 38 deletions(-)




More information about the freeside-commits mailing list