package FS::cust_payby;
use base qw( FS::payinfo_Mixin FS::cust_main_Mixin FS::Record );
+use feature 'state';
use strict;
use Scalar::Util qw( blessed );
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- my $error = $self->check_payinfo_cardtype if $self->payby =~/^(CARD|DCRD)$/;
- $self->SUPER::insert unless $error;
-
+ my $error = $self->check_payinfo_cardtype
+ || $self->SUPER::insert;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
if ( !$ignore_invalid_card &&
$check_payinfo && $self->payby =~ /^(CARD|DCRD)$/ ) {
- my $payinfo = $self->payinfo;
- $payinfo =~ s/\D//g;
- $payinfo =~ /^(\d{13,19}|\d{8,9})$/
- or return gettext('invalid_card'); #. ": ". $self->payinfo;
- $payinfo = $1;
- $self->payinfo($payinfo);
- validate($payinfo)
- or return gettext('invalid_card'); # . ": ". $self->payinfo;
+ unless ( $self->tokenized ) {
+ my $payinfo = $self->payinfo;
+ $payinfo =~ s/\D//g;
+ $payinfo =~ /^(\d{13,19}|\d{8,9})$/
+ or return gettext('invalid_card'); #. ": ". $self->payinfo;
+ $payinfo = $1;
+ $self->payinfo($payinfo);
+ validate($payinfo)
+ or return gettext('invalid_card'); # . ": ". $self->payinfo;
+ }
# see parallel checks in check_payinfo_cardtype & payinfo_Mixin::payinfo_check
my $cardtype = $self->paycardtype;
return '' if $ignore_cardtype;
- return '' unless $self->payby =~ /^(CARD|CHEK)$/;
+ return '' unless $self->payby =~ /^(CARD|DCRD)$/;
my $payinfo = $self->payinfo;
$payinfo =~ s/\D//g;
=item realtime_bop
-Runs a L<realtime_bop|FS::cust_main::Billing_Realtime::realtime_bop> transaction on this card
+Runs a L<FS::cust_main::Billing_Realtime/realtime_bop> transaction on this card
=cut
=item tokenize
-Runs a L<realtime_tokenize|FS::cust_main::Billing_Realtime::realtime_tokenize> transaction on this card
+Runs a L<FS::cust_main::Billing_Realtime/realtime_tokenize> transaction on this card
=cut
=back
+=item has_autobill_cards
+
+Returns the number of unexpired cards configured for autobill
+
=cut
+sub has_autobill_cards {
+ scalar FS::Record::qsearch({
+ table => 'cust_payby',
+ addl_from => 'JOIN cust_main USING (custnum)',
+ order_by => 'LIMIT 1',
+ hashref => {
+ paydate => { op => '>', value => DateTime->now->ymd },
+ weight => { op => '>', value => 0 },
+ },
+ extra_sql =>
+ "AND cust_payby.payby IN ('CARD', 'DCRD') ".
+ 'AND '.
+ $FS::CurrentUser::CurrentUser->agentnums_sql( table => 'cust_main' ),
+ });
+}
+
+=item has_autobill_checks
+
+Returns the number of check accounts configured for autobill
+
+=cut
+
+sub has_autobill_checks {
+ scalar FS::Record::qsearch({
+ table => 'cust_payby',
+ addl_from => 'JOIN cust_main USING (custnum)',
+ order_by => 'LIMIT 1',
+ hashref => {
+ weight => { op => '>', value => 0 },
+ },
+ extra_sql =>
+ "AND cust_payby.payby IN ('CHEK','DCHEK','DCHK') ".
+ 'AND '.
+ $FS::CurrentUser::CurrentUser->agentnums_sql( table => 'cust_main' ),
+ });
+}
+
+=item future_autobill_report_title
+
+Determine if the future_autobill report should be available.
+If so, return a dynamic title for it
+
+=cut
+
+sub future_autobill_report_title {
+ # Perhaps this function belongs somewhere else
+ state $title;
+ return $title if defined $title;
+
+ # Report incompatible with tax engines
+ return $title = '' if FS::TaxEngine->new->info->{batch};
+
+ my $has_cards = has_autobill_cards();
+ my $has_checks = has_autobill_checks();
+ my $_title = 'Future %s transactions';
+
+ if ( $has_cards && $has_checks ) {
+ $title = sprintf $_title, 'credit card and electronic check';
+ } elsif ( $has_cards ) {
+ $title = sprintf $_title, 'credit card';
+ } elsif ( $has_checks ) {
+ $title = sprintf $_title, 'electronic check';
+ } else {
+ $title = '';
+ }
+
+ $title;
+}
+
sub _upgrade_data {
my $class = shift;