diff options
| -rw-r--r-- | FS/FS/Cron/pay_batch.pm | 2 | ||||
| -rw-r--r-- | FS/FS/Schema.pm | 1 | ||||
| -rw-r--r-- | FS/FS/cust_main.pm | 5 | ||||
| -rw-r--r-- | FS/FS/cust_main/Billing_Batch.pm | 5 | ||||
| -rw-r--r-- | FS/FS/pay_batch.pm | 133 | ||||
| -rw-r--r-- | FS/FS/pay_batch/RBC.pm | 23 | ||||
| -rwxr-xr-x | FS/bin/freeside-eftca-upload | 12 | ||||
| -rwxr-xr-x | FS/bin/freeside-paymentech-upload | 7 | ||||
| -rwxr-xr-x | FS/bin/freeside-rbc-upload | 6 | ||||
| -rwxr-xr-x | httemplate/edit/process/cust_refund.cgi | 2 | ||||
| -rw-r--r-- | httemplate/misc/download-batch.cgi | 3 | ||||
| -rw-r--r-- | httemplate/search/elements/cust_pay_batch_top.html | 26 | ||||
| -rwxr-xr-x | httemplate/search/pay_batch.cgi | 8 | ||||
| -rw-r--r-- | httemplate/view/cust_main/payment_history/payment.html | 1 |
14 files changed, 197 insertions, 37 deletions
diff --git a/FS/FS/Cron/pay_batch.pm b/FS/FS/Cron/pay_batch.pm index c9039b8d8..faa9cd9ce 100644 --- a/FS/FS/Cron/pay_batch.pm +++ b/FS/FS/Cron/pay_batch.pm @@ -67,7 +67,7 @@ sub pay_batch_submit { my ($gateway, $payby, $agentnum) = @$config; if ( $gateway->batch_processor->can('default_transport') ) { - my $search = { status => 'O', payby => $payby }; + my $search = { status => 'O', payby => $payby, type => 'DEBIT' }; $search->{agentnum} = $agentnum if $agentnum; foreach my $pay_batch ( qsearch('pay_batch', $search) ) { diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index a804b12a0..b043afc15 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -1881,6 +1881,7 @@ sub tables_hashref { 'upload', @date_type, '', '', 'title', 'varchar', 'NULL',255, '', '', 'processor_id', 'varchar', 'NULL',255, '', '', + 'type', 'char', '', 6, 'DEBIT', '', # DEBIT/CREDIT ], 'primary_key' => 'batchnum', 'unique' => [], diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index ff75eb63d..03300a075 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -2775,6 +2775,10 @@ sub batch_card { ); } + my $paycode = $options{paycode} || ''; + my $batch_type = "DEBIT"; + $batch_type = "CREDIT" if $paycode eq 'C'; + my $oldAutoCommit = $FS::UID::AutoCommit; local $FS::UID::AutoCommit = 0; my $dbh = dbh; @@ -2787,6 +2791,7 @@ sub batch_card { my %pay_batch = ( 'status' => 'O', 'payby' => FS::payby->payby2payment($payby), + 'type' => $batch_type, ); $pay_batch{agentnum} = $self->agentnum if $conf->exists('batch-spoolagent'); diff --git a/FS/FS/cust_main/Billing_Batch.pm b/FS/FS/cust_main/Billing_Batch.pm index 0e713e937..c1bb35f04 100644 --- a/FS/FS/cust_main/Billing_Batch.pm +++ b/FS/FS/cust_main/Billing_Batch.pm @@ -83,6 +83,10 @@ sub batch_card { ); } + my $paycode= $options{paycode} || ''; + my $batch_type = "DEBIT"; + $batch_type = "CREDIT" if $paycode eq 'C'; + my $oldAutoCommit = $FS::UID::AutoCommit; local $FS::UID::AutoCommit = 0; my $dbh = dbh; @@ -95,6 +99,7 @@ sub batch_card { my %pay_batch = ( 'status' => 'O', 'payby' => FS::payby->payby2payment($payby), + 'type' => $batch_type, ); $pay_batch{agentnum} = $self->agentnum if $conf->exists('batch-spoolagent'); diff --git a/FS/FS/pay_batch.pm b/FS/FS/pay_batch.pm index 1c0a28ab6..c3cd0de2d 100644 --- a/FS/FS/pay_batch.pm +++ b/FS/FS/pay_batch.pm @@ -58,6 +58,10 @@ from FS::Record. The following fields are currently supported: =item title - unique batch identifier +=item processor_id - + +=item type - batch type payents (DEBIT), or refunds (CREDIT) + For incoming batches, the combination of 'title', 'payby', and 'agentnum' must be unique. @@ -1148,7 +1152,136 @@ sub manual_approve { return; } +=item batch_download_formats + +returns a hash of batch download formats. + +my %download_formats = FS::pay_batch::batch_download_formats; + +=cut + +sub batch_download_formats { + + my @formats = ( + '' => + 'Default batch mode', + 'NACHA' => + '94 byte NACHA', + 'csv-td_canada_trust-merchant_pc_batch' => + 'CSV file for TD Canada Trust Merchant PC Batch', + 'csv-chase_canada-E-xactBatch' => + 'CSV file for Chase Canada E-xactBatch', + 'PAP' => + '80 byte file for TD Canada Trust PAP Batch', + 'BoM' => + 'Bank of Montreal ECA batch', + 'ach-spiritone' => + 'Spiritone ACH batch', + 'paymentech' => + 'XML file for Chase Paymentech', + 'RBC' => + 'Royal Bank of Canada PDS batch', + 'td_eft1464' => + '1464 byte file for TD Commercial Banking EFT', + 'eft_canada' => + 'EFT Canada CSV batch', + 'CIBC' => + '80 byte file for Canadian Imperial Bank of Commerce', + # insert new batch formats here + ); + +} + +=item batch_download_formats + +returns a hash of batch download formats. + +my %download_formats = FS::pay_batch::batch_download_formats; + +=cut + +sub can_handle_electronic_refunds { + + my $self = shift; + my $format = shift; + my $conf = new FS::Conf; + + tie my %download_formats, 'Tie::IxHash', batch_download_formats; + + my %paybatch_mods = ( + 'NACHA' => 'nacha', + 'csv-td_canada_trust-merchant_pc_batch' => 'td_canada_trust', + 'csv-chase_canada-E-xactBatch' => 'chase-canada', + 'PAP' => 'PAP', + 'BoM' => 'BoM', + 'ach-spiritone' => 'ach_spiritone', + 'paymentech' => 'paymentech', + 'RBC' => 'RBC', + 'td_eft1464' => 'td_eft1464', + 'eft_canada' => 'eft_canada', + 'CIBC' => 'CIBC', + ); + + %download_formats = ( $format => $download_formats{$format}, ) if $format; + + foreach my $key (keys %download_formats) { + my $mod = "FS::pay_batch::".$paybatch_mods{$key}; + if ($mod->can('can_handle_credits')) { + return '1' if $conf->exists('batchconfig-'.$key); + } + } + + return; + +} + +use FS::upgrade_journal; sub _upgrade_data { + + # check if there are any pending batch refunds and no download format configured + # that allows electronic refunds. + unless ( FS::upgrade_journal->is_done('removed_refunds_nodownload_format') ) { + + ## get a list of all refunds in batches. + my $extrasql = " LEFT JOIN pay_batch USING ( batchnum ) WHERE cust_pay_batch.paycode = 'C' AND pay_batch.download IS NULL"; + + my @batch_refunds = qsearch({ + 'table' => 'cust_pay_batch', + 'select' => 'cust_pay_batch.*', + 'extra_sql' => $extrasql, + }); + + warn "found ".scalar @batch_refunds." batch refunds.\n"; + warn "Searching for their cust refunds...\n" if (scalar @batch_refunds > 0); + my ($delete_cust_refund_error, $delete_cust_pay_batch_error); + + ## find the cust_pay_refund for all those + foreach (@batch_refunds) { + my $extra_batch_refund_sql = " WHERE custnum = '".$_->{Hash}->{custnum}."' AND refund = '".$_->{Hash}->{amount}."' ORDER BY _date DESC LIMIT 1"; + my $cust_refund = qsearchs({ + 'table' => 'cust_refund', + 'extra_sql' => $extra_batch_refund_sql, + }); + + warn "found cust refund number ".$cust_refund->{Hash}->{refundnum}.", now to delete it.\n" if $cust_refund; + + ## delete the cust_pay_refund + $delete_cust_refund_error = $cust_refund->delete if $cust_refund; + warn "could not delete cust refund $delete_cust_refund_error\n" if $delete_cust_refund_error; + + ## delete the refund from the batch. + unless ($delete_cust_refund_error) { + $delete_cust_pay_batch_error = $_->unbatch_and_delete; + warn "could not delete cust refund $delete_cust_pay_batch_error\n" if $delete_cust_pay_batch_error; + } + + if ($delete_cust_refund_error || $delete_cust_pay_batch_error) { die "Could no delete cust_pay_batch refund\n"; } + else { warn "cust refund ".$cust_refund->{Hash}->{refundnum}." deleted\n"; } + } + + FS::upgrade_journal->set_done('removed_refunds_nodownload_format'); + } + # Set up configuration for gateways that have a Business::BatchPayment # module. diff --git a/FS/FS/pay_batch/RBC.pm b/FS/FS/pay_batch/RBC.pm index 6a2354211..00fe9a126 100644 --- a/FS/FS/pay_batch/RBC.pm +++ b/FS/FS/pay_batch/RBC.pm @@ -94,22 +94,17 @@ $name = 'RBC'; }, 'begin_condition' => sub { my $hash = shift; - # Debit Detail Record - if ($hash->{recordtype} eq '1') { + # Detail Record + if ($hash->{recordtype} eq '1' || $hash->{recordtype} eq '2') { $declined = {}; $totaloffset = 0; return 1; - # Credit Detail Record, will immediately trigger end condition & error - } elsif ($hash->{recordtype} eq '2') { - return 1; } else { return 0; } }, 'end_hook' => sub { my( $hash, $total, $line ) = @_; - return "Can't process Credit Detail Record, aborting import" - if ($hash->{'recordtype'} eq '2'); $total += $totaloffset; $total = sprintf("%.2f", $total); # We assume here that this is an 'All Records' or 'Input Records' report. @@ -120,8 +115,7 @@ $name = 'RBC'; }, 'end_condition' => sub { my $hash = shift; - return ($hash->{recordtype} eq '4') # Client Trailer Record - || ($hash->{recordtype} eq '2'); # Credit Detail Record, will throw error in end_hook + return ($hash->{recordtype} eq '4'); # Client Trailer Record }, 'skip_condition' => sub { my $hash = shift; @@ -154,7 +148,8 @@ $name = 'RBC'; my $pay_batch = shift; my $mode = $testmode ? 'TEST' : 'PROD'; my $filenum = $testmode ? 'TEST' : sprintf("%04u", $pay_batch->batchnum); - '$$AAPASTD0152['.$mode.'[NL$$'."\n". + my $qualifier = $pay_batch->type eq 'CREDIT' ? 'D' : 'A'; + '$$AAP'.$qualifier.'STD0152['.$mode.'[NL$$'."\n". '000001'. 'A'. 'HDR'. @@ -221,13 +216,15 @@ $name = 'RBC'; }, footer => sub { my ($pay_batch, $batchcount, $batchtotal) = @_; + + my $batch_info = '0' x 20 . sprintf("%06u", $batchcount) . sprintf("%014.0f", $batchtotal*100); + $batch_info = sprintf("%06u", $batchcount) . sprintf("%014.0f", $batchtotal*100) . '0' x 20 if ($pay_batch->type eq 'CREDIT'); + sprintf("%06u", $i + 1). 'Z'. 'TRL'. sprintf("%10s", $client_num). - '0' x 20 . - sprintf("%06u", $batchcount). - sprintf("%014.0f", $batchtotal*100). + $batch_info. '00' . '000000' . # total number of customer information records ' ' x 84 diff --git a/FS/bin/freeside-eftca-upload b/FS/bin/freeside-eftca-upload index 55e97744f..788de16d3 100755 --- a/FS/bin/freeside-eftca-upload +++ b/FS/bin/freeside-eftca-upload @@ -32,8 +32,12 @@ my @batches; if($opt_a) { local $@; + + my %criteria= ( 'status' => 'O', 'payby' => 'CHEK' ); + $criteria{'type'} = 'DEBIT' unless FS::pay_batch->can_handle_electronic_refunds('eft_canada'); + eval { - @batches = qsearch('pay_batch', { 'status' => 'O', 'payby' => 'CHEK' }) + @batches = qsearch('pay_batch', \%criteria) }; log_error_and_die ("Fatal database error: $@") if $@; @@ -51,6 +55,12 @@ else { if $@; log_error_and_die( "Can't find payment batch '$batchnum'\n" ) if !@batches; + + if ($batches[0]->type eq "CREDIT") { + warn "running credit\n"; + log_error_and_die( "Batch number $batchnum is a credit (batch refund) batch, and this format can not handle batch refunds.\n" ) + unless FS::pay_batch->can_handle_electronic_refunds('eft_canada'); + } } my $conf = new FS::Conf; diff --git a/FS/bin/freeside-paymentech-upload b/FS/bin/freeside-paymentech-upload index 137c38f1b..8e504d259 100755 --- a/FS/bin/freeside-paymentech-upload +++ b/FS/bin/freeside-paymentech-upload @@ -41,6 +41,7 @@ my @batches; if($opt_a) { my %criteria = (status => 'O'); $criteria{'payby'} = uc($opt_p) if $opt_p; + $criteria{'type'} = 'DEBIT' unless FS::pay_batch->can_handle_electronic_refunds('paymentech'); @batches = qsearch('pay_batch', \%criteria); log_and_die("No open batches found".($opt_p ? " of type '$opt_p'" : '').".\n") if !@batches; @@ -50,6 +51,12 @@ else { log_and_die("batchnum not passed\n".&usage) if !$batchnum; @batches = qsearchs('pay_batch', { batchnum => $batchnum } ); log_and_die("Can't find payment batch '$batchnum'\n") if !@batches; + + if ($batches[0]->type eq "CREDIT") { + warn "running credit\n"; + log_and_die( "Batch number $batchnum is a credit (batch refund) batch, and this format can not handle batch refunds.\n" ) + unless FS::pay_batch->can_handle_electronic_refunds('paymentech'); + } } my $conf = new FS::Conf; diff --git a/FS/bin/freeside-rbc-upload b/FS/bin/freeside-rbc-upload index 52501028c..5be31aeee 100755 --- a/FS/bin/freeside-rbc-upload +++ b/FS/bin/freeside-rbc-upload @@ -33,6 +33,7 @@ my @batches; if($opt_a) { my %criteria = (status => 'O'); $criteria{'payby'} = uc($opt_p) if $opt_p; + $criteria{'type'} = 'DEBIT' unless FS::pay_batch->can_handle_electronic_refunds('RBC'); @batches = qsearch('pay_batch', \%criteria); die "No open batches found".($opt_p ? " of type '$opt_p'" : '').".\n" if !@batches; @@ -42,6 +43,11 @@ else { die &usage if !$batchnum; @batches = qsearchs('pay_batch', { batchnum => $batchnum } ); die "Can't find payment batch '$batchnum'\n" if !@batches; + if ($batches[0]->type eq "CREDIT") { + warn "running credit\n"; + log_and_die( "Batch number $batchnum is a credit (batch refund) batch, and this format can not handle batch refunds.\n" ) + unless FS::pay_batch->can_handle_electronic_refunds('RBC'); + } } my $conf = new FS::Conf; diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi index 33bc886ba..3a175ea35 100755 --- a/httemplate/edit/process/cust_refund.cgi +++ b/httemplate/edit/process/cust_refund.cgi @@ -39,6 +39,8 @@ $cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum"; my ($reasonnum, $error) = $m->comp('/misc/process/elements/reason'); $cgi->param('reasonnum', $reasonnum) unless $error; +$error = "No batch download format configured that allows electronic refunds" unless (FS::pay_batch->can_handle_electronic_refunds && !$error); + if ( $error ) { # do nothing } elsif ( $payby =~ /^(CARD|CHEK)$/ ) { diff --git a/httemplate/misc/download-batch.cgi b/httemplate/misc/download-batch.cgi index 7b56f2aa1..5db563a43 100644 --- a/httemplate/misc/download-batch.cgi +++ b/httemplate/misc/download-batch.cgi @@ -4,6 +4,7 @@ http_header('Content-Type' => 'text/plain' ); # not necessarily correct... my $batchnum; + if ( $cgi->param('batchnum') =~ /^(\d+)$/ ) { $batchnum = $1; } else { @@ -29,7 +30,7 @@ die "Batch not found: '$batchnum'" if !$pay_batch; if ($pay_batch->{Hash}->{arecredits}) { my $export_format = "FS::pay_batch::".$opt{'format'}; - die "This format can not handle refunds." unless $export_format->can('can_handle_credits'); + die "You are trying to download a credit (batch refund) batch and The format ".$opt{'format'}." can not handle refunds.\n" unless $export_format->can('can_handle_credits'); } </%init> diff --git a/httemplate/search/elements/cust_pay_batch_top.html b/httemplate/search/elements/cust_pay_batch_top.html index 626d7c3ea..eee81dd5b 100644 --- a/httemplate/search/elements/cust_pay_batch_top.html +++ b/httemplate/search/elements/cust_pay_batch_top.html @@ -135,23 +135,7 @@ my $batchnum = $pay_batch->batchnum; my $fixed = $conf->config("batch-fixed_format-$payby"); -tie my %download_formats, 'Tie::IxHash', ( - '' => 'Default batch mode', - 'NACHA' => '94 byte NACHA', - 'csv-td_canada_trust-merchant_pc_batch' => - 'CSV file for TD Canada Trust Merchant PC Batch', - 'csv-chase_canada-E-xactBatch' => - 'CSV file for Chase Canada E-xactBatch', - 'PAP' => '80 byte file for TD Canada Trust PAP Batch', - 'BoM' => 'Bank of Montreal ECA batch', - 'ach-spiritone' => 'Spiritone ACH batch', - 'paymentech' => 'XML file for Chase Paymentech', - 'RBC' => 'Royal Bank of Canada PDS batch', - 'td_eft1464' => '1464 byte file for TD Commercial Banking EFT', - 'eft_canada' => 'EFT Canada CSV batch', - 'CIBC' => '80 byte file for Canadian Imperial Bank of Commerce', -# insert new batch formats here -); +tie my %download_formats, 'Tie::IxHash', FS::pay_batch::batch_download_formats; tie my %upload_formats, 'Tie::IxHash', ( %download_formats, @@ -160,7 +144,13 @@ tie my %upload_formats, 'Tie::IxHash', ( 'td_eftret' => 'TD EFT Returned Items', ); delete $upload_formats{'td_eft1464'}; -$upload_formats{'PAP'} = '264 byte results for TD Canada Trust PAP Batch', +$upload_formats{'PAP'} = '264 byte results for TD Canada Trust PAP Batch'; + +if ($pay_batch->type eq "CREDIT") { + foreach my $key (keys %download_formats) { + delete $download_formats{$key} unless FS::pay_batch->can_handle_electronic_refunds($key); + } +} my %statustext = ( 'O' => 'open', 'I' => 'in transit', 'R' => 'resolved' ); diff --git a/httemplate/search/pay_batch.cgi b/httemplate/search/pay_batch.cgi index 40df5aa56..8fe435132 100755 --- a/httemplate/search/pay_batch.cgi +++ b/httemplate/search/pay_batch.cgi @@ -33,9 +33,11 @@ ], 'align' => 'rcllrrrrc', 'fields' => [ 'batchnum', - sub { - FS::payby->shortname(shift->payby); - }, + sub { + my $self = shift; + my $type = $self->type eq 'CREDIT' ? 'CREDIT' : ''; + $type ." " . FS::payby->shortname($self->payby); + }, sub { my $self = shift; my $_date = $self->download; diff --git a/httemplate/view/cust_main/payment_history/payment.html b/httemplate/view/cust_main/payment_history/payment.html index cc5789b75..3fe588b52 100644 --- a/httemplate/view/cust_main/payment_history/payment.html +++ b/httemplate/view/cust_main/payment_history/payment.html @@ -177,6 +177,7 @@ if ( $cust_pay->closed !~ /^Y/i qq! TITLE="! . $refundtitle . '">' . emt('refund') . '</A>)'; } +$refund = '' if ($cust_pay->batchnum && !FS::pay_batch->can_handle_electronic_refunds); my $void = ''; my $voidmsg = $cust_pay->payby =~ /^(CARD|CHEK)$/ |
