summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/Record.pm14
-rw-r--r--FS/FS/cust_pay.pm87
-rw-r--r--FS/FS/payby.pm22
-rw-r--r--httemplate/elements/select-payby.html2
-rw-r--r--httemplate/misc/cust_pay-import.cgi52
-rw-r--r--httemplate/misc/process/cust_pay-import.cgi24
6 files changed, 163 insertions, 38 deletions
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index a1c87769e..88d452ae9 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -1686,6 +1686,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,
@@ -1694,6 +1695,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},
);
@@ -1732,6 +1734,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
@@ -1773,7 +1777,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'}
@@ -1829,6 +1833,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'} ) {
@@ -1838,6 +1847,7 @@ sub batch_import {
$sep_char = ',';
$fixedlength_format = '';
$row_callback = '';
+ $hash_callback = '';
@fields = @{ $param->{'fields'} };
} else {
@@ -2063,6 +2073,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 c2e0955f7..7edf6f173 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -227,6 +227,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 );
}
@@ -1166,6 +1172,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 2e2a664df..11bc184d9 100644
--- a/FS/FS/payby.pm
+++ b/FS/FS/payby.pm
@@ -239,6 +239,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 b2d542190..2018874e7 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 05a6c4fd8..ee0154db5 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 7711773d8..2981da46d 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>
+