summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS.pm4
-rw-r--r--FS/FS/API.pm41
-rw-r--r--FS/FS/AccessRight.pm1
-rw-r--r--FS/FS/Conf.pm5
-rw-r--r--FS/FS/Cron/bill.pm2
-rw-r--r--FS/FS/Cron/breakage.pm2
-rw-r--r--FS/FS/Mason.pm1
-rw-r--r--FS/FS/Schema.pm52
-rw-r--r--FS/FS/Setup.pm12
-rw-r--r--FS/FS/Upgrade.pm3
-rw-r--r--FS/FS/cust_bill.pm48
-rw-r--r--FS/FS/cust_bill/Search.pm12
-rw-r--r--FS/FS/cust_bill_event.pm378
-rw-r--r--FS/FS/cust_event.pm4
-rw-r--r--FS/FS/cust_main/API.pm6
-rw-r--r--FS/FS/cust_main/Billing.pm2
-rw-r--r--FS/FS/cust_main/Billing_Realtime.pm30
-rw-r--r--FS/FS/cust_pay.pm5
-rw-r--r--FS/FS/cust_pay_batch.pm37
-rw-r--r--FS/FS/cust_payby.pm21
-rw-r--r--FS/FS/part_bill_event.pm368
-rw-r--r--FS/FS/part_event.pm16
-rw-r--r--FS/FS/part_event/Action/realtime_auto.pm41
-rw-r--r--FS/FS/pay_batch/RBC.pm2
-rw-r--r--FS/FS/tax_rate.pm3
-rw-r--r--FS/MANIFEST4
-rwxr-xr-xFS/bin/freeside-daily11
-rwxr-xr-xFS/bin/freeside-monthly8
-rwxr-xr-xFS/bin/freeside-vitelity-cdrimport132
-rwxr-xr-xFS/bin/freeside-voipinnovations-cdrimport129
-rw-r--r--FS/t/cust_bill_event.t5
-rw-r--r--FS/t/part_bill_event.t5
32 files changed, 391 insertions, 999 deletions
diff --git a/FS/FS.pm b/FS/FS.pm
index 33105ba46..04311e57d 100644
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -408,10 +408,6 @@ L<FS::cust_bill_pkg_detail> - Invoice line item detail class
L<FS::legacy_cust_bill> - Legacy data invoice class
-L<FS::part_bill_event> - (Old) Invoice event definition class
-
-L<FS::cust_bill_event> - (Old) Completed invoice event class
-
L<FS::part_event> - (New) Billing event definition class
L<FS::part_event_option> - (New) Billing event option class
diff --git a/FS/FS/API.pm b/FS/FS/API.pm
index c49fb205a..f848361ac 100644
--- a/FS/FS/API.pm
+++ b/FS/FS/API.pm
@@ -365,26 +365,6 @@ comma-separated list of email addresses for email invoices. The special value 'P
postal_invoicing
Set to 1 to enable postal invoicing
-=item payby
-
-CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY
-
-=item payinfo
-
-Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
-
-=item paycvv
-
-Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
-
-=item paydate
-
-Expiration date for CARD/DCRD
-
-=item payname
-
-Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
-
=item referral_custnum
Referring customer number
@@ -505,27 +485,6 @@ addition to email addresses),
postal_invoicing
Set to 1 to enable postal invoicing
-=item payby
-
-CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY
-
-=item payinfo
-
-Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid
-"pin" for PREPAY, purchase order number for BILL
-
-=item paycvv
-
-Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
-
-=item paydate
-
-Expiration date for CARD/DCRD
-
-=item payname
-
-Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
-
=item referral_custnum
Referring customer number
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index f395dead3..82423d8db 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -53,7 +53,6 @@ assigned to users and/or groups.
# 'billing' => [
# '_desc' => 'Access to billing configuration',
# 'payment_gateway' => {},
-# 'part_bill_event' => {},
# 'prepay_credit' => {},
# 'rate' => {},
# 'cust_main_county' => {},
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index 091070ec5..bb04d9427 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -2147,11 +2147,6 @@ and customer address. Include units.',
},
{
- 'key' => 'safe-part_bill_event',
- 'section' => 'UI',
- 'description' => 'Validates invoice event expressions against a preset list. Useful for webdemos, annoying to powerusers.',
- 'type' => 'checkbox',
- },
{
'key' => 'show_ship_company',
diff --git a/FS/FS/Cron/bill.pm b/FS/FS/Cron/bill.pm
index 98f1c2e6f..a82265452 100644
--- a/FS/FS/Cron/bill.pm
+++ b/FS/FS/Cron/bill.pm
@@ -190,8 +190,6 @@ sub bill_where {
push @search, "( cust_main.archived != 'Y' OR archived IS NULL )"; #disable?
- push @search, "cust_main.payby = '". $opt{'p'}. "'"
- if $opt{'p'};
push @search, "cust_main.agentnum IN ( ". $opt{'a'}. " ) "
if $opt{'a'};
diff --git a/FS/FS/Cron/breakage.pm b/FS/FS/Cron/breakage.pm
index 6dd904d6a..56a9df436 100644
--- a/FS/FS/Cron/breakage.pm
+++ b/FS/FS/Cron/breakage.pm
@@ -47,7 +47,7 @@ sub reconcile_breakage {
my @customers = qsearch({
'table' => 'cust_main',
'hashref' => { 'agentnum' => $agent->agentnum,
- 'payby' => { op=>'!=', value=>'COMP', },
+ 'complimentary' => { op=>'!=', value=>'Y', },
},
'extra_sql' => $extra_sql,
});
diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 37e3ad243..81671ffad 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -180,7 +180,6 @@ if ( -e $addl_handler_use_file ) {
use FS::cust_pay_refund;
use FS::cust_svc;
use FS::nas;
- use FS::part_bill_event;
use FS::part_event;
use FS::part_event_condition;
use FS::part_pkg;
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 5333b1af0..db2d3cf71 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -794,58 +794,6 @@ sub tables_hashref {
],
},
- #old "invoice" events, deprecated
- 'cust_bill_event' => {
- 'columns' => [
- 'eventnum', 'serial', '', '', '', '',
- 'invnum', 'int', '', '', '', '',
- 'eventpart', 'int', '', '', '', '',
- '_date', @date_type, '', '',
- 'status', 'varchar', '', $char_d, '', '',
- 'statustext', 'text', 'NULL', '', '', '',
- ],
- 'primary_key' => 'eventnum',
- #no... there are retries now #'unique' => [ [ 'eventpart', 'invnum' ] ],
- 'unique' => [],
- 'index' => [ ['invnum'], ['status'], ['eventpart'],
- ['statustext'], ['_date'],
- ],
- 'foreign_keys' => [
- { columns => [ 'invnum' ],
- table => 'cust_bill',
- },
- { columns => [ 'eventpart' ],
- table => 'part_bill_event',
- },
- ],
- },
-
- #old "invoice" events, deprecated
- 'part_bill_event' => {
- 'columns' => [
- 'eventpart', 'serial', '', '', '', '',
- 'freq', 'varchar', 'NULL', $char_d, '', '',
- 'payby', 'char', '', 4, '', '',
- 'event', 'varchar', '', $char_d, '', '',
- 'eventcode', @perl_type, '', '',
- 'seconds', 'int', 'NULL', '', '', '',
- 'weight', 'int', '', '', '', '',
- 'plan', 'varchar', 'NULL', $char_d, '', '',
- 'plandata', 'text', 'NULL', '', '', '',
- 'reason', 'int', 'NULL', '', '', '',
- 'disabled', 'char', 'NULL', 1, '', '',
- ],
- 'primary_key' => 'eventpart',
- 'unique' => [],
- 'index' => [ ['payby'], ['disabled'], ],
- 'foreign_keys' => [
- { columns => [ 'reason' ],
- table => 'reason',
- references => [ 'reasonnum' ],
- },
- ],
- },
-
'part_event' => {
'columns' => [
'eventpart', 'serial', '', '', '', '',
diff --git a/FS/FS/Setup.pm b/FS/FS/Setup.pm
index 5528c8988..f26e50ea9 100644
--- a/FS/FS/Setup.pm
+++ b/FS/FS/Setup.pm
@@ -363,13 +363,11 @@ sub initial_data {
#with billing type Complimentary. Leave the First package dropdown set to
#(none).
'cust_main' => [
- { 'agentnum' => 1, #XXX
- 'refnum' => 1, #XXX
- 'first' => 'System',
- 'last' => 'Accounts',
- 'payby' => 'COMP',
- 'payinfo' => 'system', #or something
- 'paydate' => '1/2037',
+ { 'agentnum' => 1, #XXX
+ 'refnum' => 1, #XXX
+ 'first' => 'System',
+ 'last' => 'Accounts',
+ 'complimentary' => 'Y',
'bill_location' => $cust_location,
'ship_location' => $cust_location,
},
diff --git a/FS/FS/Upgrade.pm b/FS/FS/Upgrade.pm
index 263be80b1..35a1e19c3 100644
--- a/FS/FS/Upgrade.pm
+++ b/FS/FS/Upgrade.pm
@@ -312,6 +312,9 @@ sub upgrade_data {
#payby conditions to new ones
'part_event_condition' => [],
+ #payby actions to new ones
+ 'part_event' => [],
+
#cust_main (remove paycvv from history, locations, cust_payby, etc)
'cust_main' => [],
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index 068d0d1d3..8d6966161 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -6,6 +6,7 @@ use base qw( FS::cust_bill::Search FS::Template_Mixin
use strict;
use vars qw( $DEBUG $me );
# but NOT $conf
+use Carp;
use Fcntl qw(:flock); #for spool_csv
use Cwd;
use List::Util qw(min max sum);
@@ -26,11 +27,9 @@ use FS::cust_pay;
use FS::cust_pkg;
use FS::cust_credit_bill;
use FS::pay_batch;
-use FS::cust_bill_event;
use FS::cust_event;
use FS::part_pkg;
use FS::cust_bill_pay;
-use FS::part_bill_event;
use FS::payby;
use FS::bill_batch;
use FS::cust_bill_batch;
@@ -285,7 +284,6 @@ sub delete {
my $dbh = dbh;
foreach my $table (qw(
- cust_bill_event
cust_event
cust_credit_bill
cust_bill_pay
@@ -565,32 +563,6 @@ sub open_cust_bill_pkg {
@open;
}
-=item cust_bill_event
-
-Returns the completed invoice events (deprecated, old-style events - see L<FS::cust_bill_event>) for this invoice.
-
-=cut
-
-sub cust_bill_event {
- my $self = shift;
- qsearch( 'cust_bill_event', { 'invnum' => $self->invnum } );
-}
-
-=item num_cust_bill_event
-
-Returns the number of completed invoice events (deprecated, old-style events - see L<FS::cust_bill_event>) for this invoice.
-
-=cut
-
-sub num_cust_bill_event {
- my $self = shift;
- my $sql =
- "SELECT COUNT(*) FROM cust_bill_event WHERE invnum = ?";
- my $sth = dbh->prepare($sql) or die dbh->errstr. " preparing $sql";
- $sth->execute($self->invnum) or die $sth->errstr. " executing $sql";
- $sth->fetchrow_arrayref->[0];
-}
-
=item cust_event
Returns the new-style customer billing events (see L<FS::cust_event>) for this invoice.
@@ -1982,24 +1954,8 @@ sub print_csv {
}
-=item comp
-
-Pays this invoice with a compliemntary payment. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
sub comp {
- my $self = shift;
- my $cust_pay = new FS::cust_pay ( {
- 'invnum' => $self->invnum,
- 'paid' => $self->owed,
- '_date' => '',
- 'payby' => 'COMP',
- 'payinfo' => $self->cust_main->payinfo,
- 'paybatch' => '',
- } );
- $cust_pay->insert;
+ croak 'cust_bill->comp is deprecated (COMP payments are deprecated)';
}
=item realtime_card
diff --git a/FS/FS/cust_bill/Search.pm b/FS/FS/cust_bill/Search.pm
index 1fc818d35..2a67529c1 100644
--- a/FS/FS/cust_bill/Search.pm
+++ b/FS/FS/cust_bill/Search.pm
@@ -128,10 +128,6 @@ Return only invoices belonging to that customer.
Limit to that customer class (single value or arrayref).
-=item payby
-
-Limit to customers with that payment method (single value or arrayref).
-
=item refnum
Limit to customers with that advertising source.
@@ -194,14 +190,6 @@ sub search_sql_where {
}
- #payby
- if ( $param->{payby} ) {
- my $payby = $param->{payby};
- $payby = [ $payby ] unless ref $payby;
- my $payby_in = join(',', map {dbh->quote($_)} @$payby);
- push @search, "cust_main.payby IN($payby_in)" if length($payby_in);
- }
-
#_date
if ( $param->{_date} ) {
my($beginning, $ending) = @{$param->{_date}};
diff --git a/FS/FS/cust_bill_event.pm b/FS/FS/cust_bill_event.pm
deleted file mode 100644
index adaa13eee..000000000
--- a/FS/FS/cust_bill_event.pm
+++ /dev/null
@@ -1,378 +0,0 @@
-package FS::cust_bill_event;
-
-use strict;
-use vars qw( @ISA $DEBUG );
-use FS::Record qw( qsearch qsearchs );
-use FS::cust_main_Mixin;
-use FS::cust_bill;
-use FS::part_bill_event;
-
-@ISA = qw(FS::cust_main_Mixin FS::Record);
-
-$DEBUG = 0;
-
-=head1 NAME
-
-FS::cust_bill_event - Object methods for cust_bill_event records
-
-=head1 SYNOPSIS
-
- use FS::cust_bill_event;
-
- $record = new FS::cust_bill_event \%hash;
- $record = new FS::cust_bill_event { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_event object represents an complete invoice event.
-FS::cust_bill_event inherits from FS::Record. The following fields are
-currently supported:
-
-=over 4
-
-=item eventnum
-
-Primary key
-
-=item invnum
-
-Invoice (see L<FS::cust_bill>)
-
-=item eventpart
-
-Event definition (see L<FS::part_bill_event>)
-
-=item _date
-
-Specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
-L<Time::Local> and L<Date::Parse> for conversion functions.
-
-=item status
-
-Event status: B<done> or B<failed>
-
-=item statustext
-
-Additional status detail (i.e. error message)
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new completed invoice event. To add the compelted invoice event to
-the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'cust_bill_event'; }
-
-sub cust_linked { $_[0]->cust_main_custnum || $_[0]->custnum }
-sub cust_unlinked_msg {
- my $self = shift;
- "WARNING: can't find cust_main.custnum ". $self->custnum.
- ' (cust_bill.invnum '. $self->invnum. ')';
-}
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid completed invoice event. If
-there is an error, returns the error, otherwise returns false. Called by the
-insert and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
- my $self = shift;
-
- my $error = $self->ut_numbern('eventnum')
- || $self->ut_number('invnum')
- || $self->ut_number('eventpart')
- || $self->ut_number('_date')
- || $self->ut_enum('status', [qw( done failed )])
- || $self->ut_anything('statustext')
- ;
-
- return "Unknown eventpart ". $self->eventpart
- unless my $part_bill_event =
- qsearchs( 'part_bill_event' ,{ 'eventpart' => $self->eventpart } );
-
- return "Unknown invnum ". $self->invnum
- unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
-
- $self->SUPER::check;
-}
-
-=item part_bill_event
-
-Returns the invoice event definition (see L<FS::part_bill_event>) for this
-completed invoice event.
-
-=cut
-
-sub part_bill_event {
- my $self = shift;
- qsearchs( 'part_bill_event', { 'eventpart' => $self->eventpart } );
-}
-
-=item cust_bill
-
-Returns the invoice (see L<FS::cust_bill>) for this completed invoice event.
-
-=cut
-
-sub cust_bill {
- my $self = shift;
- qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
-}
-
-=item retry
-
-Changes the status of this event from B<done> to B<failed>, allowing it to be
-retried.
-
-=cut
-
-sub retry {
- my $self = shift;
- return '' unless $self->status eq 'done';
- my $old = ref($self)->new( { $self->hash } );
- $self->status('failed');
- $self->replace($old);
-}
-
-=item retryable
-
-Changes the statustext of this event to B<retriable>, rendering it
-retriable (should retry be called).
-
-=cut
-
-sub retriable {
- my $self = shift;
- return '' unless $self->status eq 'done';
- my $old = ref($self)->new( { $self->hash } );
- $self->statustext('retriable');
- $self->replace($old);
-}
-
-=item search_sql_where HASHREF
-
-Class method which returns an SQL WHERE fragment to search for parameters
-specified in HASHREF. Valid parameters are
-
-=over 4
-
-=item agentnum
-
-=item beginning
-
-An epoch date setting a lower bound for _date values
-
-=item ending
-
-An epoch date setting a upper bound for _date values
-
-=item failed
-
-Limits the search to failed events if true
-
-=item payby
-
-Requires that the search be JOIN'd to part_bill_event # Bug?
-
-=item invnum
-
-=item currentuser
-
-Specifies the user for agent virtualization
-
-=back
-
-=cut
-
-sub search_sql_where {
- my ($class, $params) = @_;
- my @search = ();
-
- push @search, "agentnum = ". $params->{agentnum} if $params->{agentnum};
-
- push @search, "cust_bill_event._date >= ". $params->{beginning}
- if $params->{beginning};
- push @search, "cust_bill_event._date <= ". $params->{ending}
- if $params->{ending};
-
- push @search, "statustext != ''",
- "statustext IS NOT NULL",
- "statustext != 'N/A'"
- if $params->{failed};
-
- push @search, "part_bill_event.payby = '". $params->{payby}. "'"
- if $params->{payby};
-
- push @search, "cust_bill_event.invnum = '". $params->{invnum}. "'"
- if $params->{invnum};
-
- my $currentuser = $params->{currentuser} || $params->{CurrentUser};
- if ($currentuser) {
- my $access_user = qsearchs('access_user', { username => $currentuser });
- if ($access_user) {
- push @search, $access_user->agentnums_sql;
- }else{
- push @search, "1=0";
- }
- }else{
- push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
- }
-
- join(' AND ', @search );
-
-}
-
-=back
-
-=head1 SUBROUTINES
-
-=over 4
-
-=item reprint
-
-=cut
-
-sub process_reprint {
- process_re_X('print', @_);
-}
-
-=item reemail
-
-=cut
-
-sub process_reemail {
- process_re_X('email', @_);
-}
-
-=item refax
-
-=cut
-
-sub process_refax {
- process_re_X('fax', @_);
-}
-
-use Data::Dumper;
-sub process_re_X {
- my( $method, $job ) = ( shift, shift );
-
- my $param = shift;
- warn Dumper($param) if $DEBUG;
-
- re_X(
- $method,
- $param,
- $job,
- );
-
-}
-
-sub re_X {
- my($method, $param, $job) = @_;
-
- my $where = FS::cust_bill_event->search_sql_where($param);
- $where = " WHERE plan LIKE 'send%'". ( $where ? " AND $where" : "" );
-
- my $from = 'LEFT JOIN part_bill_event USING ( eventpart )'.
- 'LEFT JOIN cust_bill USING ( invnum )'.
- 'LEFT JOIN cust_main USING ( custnum )';
-
- my @cust_bill_event = qsearch( 'cust_bill_event', {}, '', $where, '', $from );
-
- my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
- foreach my $cust_bill_event ( @cust_bill_event ) {
-
- $cust_bill_event->cust_bill->$method(
- $cust_bill_event->part_bill_event->templatename
- );
-
- if ( $job ) { #progressbar foo
- $num++;
- if ( time - $min_sec > $last ) {
- my $error = $job->update_statustext(
- int( 100 * $num / scalar(@cust_bill_event) )
- );
- die $error if $error;
- $last = time;
- }
- }
-
- }
-
- #this doesn't work, but it would be nice
- #if ( $job ) { #progressbar foo
- # my $error = $job->update_statustext(
- # scalar(@cust_bill_event). " invoices re-${method}ed"
- # );
- # die $error if $error;
- #}
-
-}
-
-=back
-
-=head1 BUGS
-
-Far too early in the morning.
-
-=head1 SEE ALSO
-
-L<FS::part_bill_event>, L<FS::cust_bill>, L<FS::Record>, schema.html from the
-base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/cust_event.pm b/FS/FS/cust_event.pm
index d7a35a7a9..f299f9377 100644
--- a/FS/FS/cust_event.pm
+++ b/FS/FS/cust_event.pm
@@ -337,10 +337,6 @@ specified in HASHREF. Valid parameters are
=item ending
-=item payby
-
-=item
-
=back
=cut
diff --git a/FS/FS/cust_main/API.pm b/FS/FS/cust_main/API.pm
index 158b5cf58..2d6da9ed6 100644
--- a/FS/FS/cust_main/API.pm
+++ b/FS/FS/cust_main/API.pm
@@ -21,8 +21,7 @@ use vars qw(
first last company daytime night fax mobile
);
# locale
-# payby payinfo payname paystart_month paystart_year payissue payip
-# ss paytype paystate stateid stateid_state
+# ss stateid stateid_state
@location_editable_fields = qw(
address1 address2 city county state zip country
);
@@ -106,14 +105,12 @@ sub API_insert {
#same for refnum like signup_server-default_refnum?
my $cust_main = new FS::cust_main ( { # $class->new( {
- 'payby' => 'BILL',
'tagnum' => [ FS::part_tag->default_tags ],
map { $_ => $opt{$_} } qw(
agentnum salesnum refnum agent_custid referral_custnum
last first company
daytime night fax mobile
- payby payinfo paydate paycvv payname
),
} );
@@ -176,7 +173,6 @@ sub API_update {
agentnum salesnum refnum agent_custid referral_custnum
last first company
daytime night fax mobile
- payby payinfo paydate paycvv payname
),
my @invoicing_list;
diff --git a/FS/FS/cust_main/Billing.pm b/FS/FS/cust_main/Billing.pm
index 9e2082fc3..b3d4e705f 100644
--- a/FS/FS/cust_main/Billing.pm
+++ b/FS/FS/cust_main/Billing.pm
@@ -390,7 +390,7 @@ terms or the default terms are used.
sub bill {
my( $self, %options ) = @_;
- return '' if $self->payby eq 'COMP';
+ return '' if $self->complimentary eq 'Y';
local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG;
my $log = FS::Log->new('FS::cust_main::Billing::bill');
diff --git a/FS/FS/cust_main/Billing_Realtime.pm b/FS/FS/cust_main/Billing_Realtime.pm
index 20698fbca..1f6a9e91a 100644
--- a/FS/FS/cust_main/Billing_Realtime.pm
+++ b/FS/FS/cust_main/Billing_Realtime.pm
@@ -45,6 +45,36 @@ These methods are available on FS::cust_main objects.
=over 4
+=item realtime_cust_payby
+
+=cut
+
+sub realtime_cust_payby {
+ my( $self, %options ) = @_;
+
+ local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG;
+
+ $options{amount} = $self->balance unless exists( $options{amount} );
+
+ my @cust_payby = qsearch({
+ 'table' => 'cust_payby',
+ 'hashref' => { 'custnum' => $self->custnum, },
+ 'extra_sql' => " AND payby IN ( 'CARD', 'CHEK' ) ",
+ 'order_by' => 'ORDER BY weight ASC',
+ });
+
+ my $error;
+ foreach my $cust_payby (@cust_payby) {
+ $error = $cust_payby->realtime_bop( %options, );
+ last unless $error;
+ }
+
+ #XXX what about the earlier errors?
+
+ $error;
+
+}
+
=item realtime_collect [ OPTION => VALUE ... ]
Attempt to collect the customer's current balance with a realtime credit
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index 139d2ffe5..e8f9aee8e 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -978,7 +978,6 @@ sub _upgrade_data { #class method
$cust_pay->set('otaker', 'legacy');
}
- delete $FS::payby::hash{'COMP'}->{cust_pay}; #quelle kludge
my $error = $cust_pay->replace;
if ( $error ) {
@@ -987,8 +986,6 @@ sub _upgrade_data { #class method
next;
}
- $FS::payby::hash{'COMP'}->{cust_pay} = ''; #restore it
-
$count++;
if ( $DEBUG > 1 && $lastprog + 30 < time ) {
warn "$me $count/$total (".sprintf('%.2f',100*$count/$total). '%)'."\n";
@@ -1042,9 +1039,7 @@ sub _upgrade_data { #class method
# otaker->usernum upgrade
###
- delete $FS::payby::hash{'COMP'}->{cust_pay}; #quelle kludge
$class->_upgrade_otaker(%opt);
- $FS::payby::hash{'COMP'}->{cust_pay} = ''; #restore it
# if we do this anywhere else, it should become an FS::Upgrade method
my $num_to_upgrade = $class->count('paybatch is not null');
diff --git a/FS/FS/cust_pay_batch.pm b/FS/FS/cust_pay_batch.pm
index d4d40b580..a4b4957aa 100644
--- a/FS/FS/cust_pay_batch.pm
+++ b/FS/FS/cust_pay_batch.pm
@@ -49,7 +49,7 @@ following fields are currently supported:
=item batchnum - indentifies group in batch
-=item payby - CARD/CHEK/LECB/BILL/COMP
+=item payby - CARD/CHEK
=item payinfo
@@ -154,7 +154,7 @@ sub check {
if ( $self->exp eq '' ) {
return "Expiration date required"
- unless $self->payby =~ /^(CHEK|DCHK|LECB|WEST)$/;
+ unless $self->payby =~ /^(CHEK|DCHK|WEST)$/;
$self->exp('');
} else {
if ( $self->exp =~ /^(\d{4})[\/\-](\d{1,2})[\/\-](\d{1,2})$/ ) {
@@ -246,39 +246,6 @@ sub retriable {
confess "deprecated method cust_pay_batch->retriable called; try removing ".
"the once condition and adding an every condition?";
- my $self = shift;
-
- local $SIG{HUP} = 'IGNORE'; #Hmm
- 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 $cust_bill = qsearchs('cust_bill', { 'invnum' => $self->invnum } )
- or return "event $self->eventnum references nonexistant invoice $self->invnum";
-
- warn "cust_pay_batch->retriable working with self of " . $self->paybatchnum . " and invnum of " . $self->invnum;
- my @cust_bill_event =
- sort { $a->part_bill_event->seconds <=> $b->part_bill_event->seconds }
- grep {
- $_->part_bill_event->eventcode =~ /\$cust_bill->batch_card/
- && $_->status eq 'done'
- && ! $_->statustext
- }
- $cust_bill->cust_bill_event;
- # complain loudly if scalar(@cust_bill_event) > 1 ?
- my $error = $cust_bill_event[0]->retriable;
- if ($error ) {
- # gah, even with transactions.
- $dbh->commit if $oldAutoCommit; #well.
- return "error marking invoice event retriable: $error";
- }
- '';
}
=item approve OPTIONS
diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm
index a65a17133..b1a7ddb56 100644
--- a/FS/FS/cust_payby.pm
+++ b/FS/FS/cust_payby.pm
@@ -427,12 +427,16 @@ sub check {
}
- if ( $self->paydate eq '' || $self->paydate eq '-' ) {
- return "Expiration date required"
- # shouldn't payinfo_check do this?
- unless $self->payby =~ /^(CHEK|DCHK)$/;
+ if ( $self->payby =~ /^(CHEK|DCHK)$/ ) {
+
$self->paydate('');
- } else {
+
+ } elsif ( $self->payby =~ /^(CARD|DCRD)$/ ) {
+
+ # shouldn't payinfo_check do this?
+ return "Expiration date required"
+ if $self->paydate eq '' || $self->paydate eq '-';
+
my( $m, $y );
if ( $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/ ) {
( $m, $y ) = ( $1, length($2) == 4 ? $2 : "20$2" );
@@ -451,6 +455,7 @@ sub check {
#&&
!$ignore_expired_card
&& ( $y<$nowy || ( $y==$nowy && $1<$nowm ) );
+
}
if ( $self->payname eq '' && $self->payby !~ /^(CHEK|DCHK)$/ &&
@@ -560,7 +565,7 @@ Returns the field names used in the web interface (including some pseudo-fields)
sub cgi_cust_payby_fields {
#my $class = shift;
[qw( payby payinfo paydate_month paydate_year paycvv payname weight
- payinfo1 payinfo2 payinfo3 paytype paystate )];
+ payinfo1 payinfo2 payinfo3 paytype paystate payname_CHEK )];
}
=item cgi_hash_callback HASHREF
@@ -582,7 +587,7 @@ sub cgi_hash_callback {
if ( $hashref->{payby} =~ /^(CHEK|DCHK)$/ ) {
- unless ( grep $hashref->{$_}, qw( payinfo1 payinfo2 payinfo3 payname ) ) {
+ unless ( grep $hashref->{$_}, qw(payinfo1 payinfo2 payinfo3 payname_CHEK)) {
%$hashref = ();
return;
}
@@ -592,7 +597,7 @@ sub cgi_hash_callback {
if $conf->config('echeck-country') eq 'CA';
$hashref->{payinfo} .= $hashref->{'payinfo2'};
- $hashref->{payname} .= $hashref->{'payname_CHEK'};
+ $hashref->{payname} = $hashref->{'payname_CHEK'};
} elsif ( $hashref->{payby} =~ /^(CARD|DCRD)$/ ) {
diff --git a/FS/FS/part_bill_event.pm b/FS/FS/part_bill_event.pm
deleted file mode 100644
index 4e7aa52f2..000000000
--- a/FS/FS/part_bill_event.pm
+++ /dev/null
@@ -1,368 +0,0 @@
-package FS::part_bill_event;
-
-use strict;
-use vars qw( @ISA $DEBUG @EXPORT_OK );
-use Carp qw(cluck confess);
-use FS::Record qw( dbh qsearch qsearchs );
-use FS::Conf;
-use FS::cust_main;
-use FS::cust_bill;
-
-@ISA = qw( FS::Record );
-@EXPORT_OK = qw( due_events );
-$DEBUG = 0;
-
-=head1 NAME
-
-FS::part_bill_event - Object methods for part_bill_event records
-
-=head1 SYNOPSIS
-
- use FS::part_bill_event;
-
- $record = new FS::part_bill_event \%hash;
- $record = new FS::part_bill_event { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->do_event( $direct_object );
-
- @events = due_events ( { 'record' => $event_triggering_record,
- 'payby' => $payby,
- 'event_time => $_date,
- 'extra_sql => $extra } );
-
-=head1 DESCRIPTION
-
-An FS::part_bill_event object represents a deprecated, old-style invoice event
-definition - a callback which is triggered when an invoice is a certain amount
-of time overdue. FS::part_bill_event inherits from FS::Record. The following
-fields are currently supported:
-
-=over 4
-
-=item eventpart - primary key
-
-=item payby - CARD, DCRD, CHEK, DCHK, LECB, BILL, or COMP
-
-=item event - event name
-
-=item eventcode - event action
-
-=item seconds - how long after the invoice date events of this type are triggered
-
-=item weight - ordering for events with identical seconds
-
-=item plan - eventcode plan
-
-=item plandata - additional plan data
-
-=item reason - an associated reason for this event to fire
-
-=item disabled - Disabled flag, empty or `Y'
-
-=back
-
-=head1 NOTE
-
-Old-style invoice events are only useful for legacy migrations - if you are
-looking for current events see L<FS::part_event>.
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new invoice event definition. To add the invoice event definition to
-the database, see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'part_bill_event'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=cut
-
-# the insert method can be inherited from FS::Record
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# the delete method can be inherited from FS::Record
-
-=item replace OLD_RECORD
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=cut
-
-# the replace method can be inherited from FS::Record
-
-=item check
-
-Checks all fields to make sure this is a valid invoice event definition. If
-there is an error, returns the error, otherwise returns false. Called by the
-insert and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
- my $self = shift;
-
- $self->weight(0) unless $self->weight;
-
- my $conf = new FS::Conf;
- if ( $conf->exists('safe-part_bill_event') ) {
- my $error = $self->ut_anything('eventcode');
- return $error if $error;
-
- my $c = $self->eventcode;
-
- #yay, these regexen will go away with the event refactor
-
- $c =~ /^\s*\$cust_main\->(suspend|cancel|invoicing_list_addpost|bill|collect)\(\);\s*("";)?\s*$/
-
- or $c =~ /^\s*\$cust_bill\->(comp|realtime_(card|ach|lec)|batch_card|send)\((%options)*\);\s*$/
-
- or $c =~ /^\s*\$cust_bill\->send(_if_newest)?\(\'[\w\-\s]+\'\s*(,\s*(\d+|\[\s*\d+(,\s*\d+)*\s*\])\s*,\s*'[\w\@\.\-\+]*'\s*)?\);\s*$/
-
-# or $c =~ /^\s*\$cust_main\->apply_payments; \$cust_main->apply_credits; "";\s*$/
- or $c =~ /^\s*\$cust_main\->apply_payments_and_credits; "";\s*$/
-
- or $c =~ /^\s*\$cust_main\->charge\( \s*\d*\.?\d*\s*,\s*\'[\w \!\@\#\$\%\&\(\)\-\+\;\:\"\,\.\?\/]*\'\s*\);\s*$/
-
- or $c =~ /^\s*\$cust_main\->suspend_(if|unless)_pkgpart\([\d\,\s]*\);\s*$/
-
- or $c =~ /^\s*\$cust_bill\->cust_suspend_if_balance_over\([\d\.\s]*\);\s*$/
-
- or do {
- #log
- return "illegal eventcode: $c";
- };
-
- }
-
- my $error = $self->ut_numbern('eventpart')
- || $self->ut_enum('payby', [qw( CARD DCLN DCRD CHEK DCHK LECB BILL COMP )] )
- || $self->ut_text('event')
- || $self->ut_anything('eventcode')
- || $self->ut_number('seconds')
- || $self->ut_enum('disabled', [ '', 'Y' ] )
- || $self->ut_number('weight')
- || $self->ut_textn('plan')
- || $self->ut_anything('plandata')
- || $self->ut_numbern('reason')
- ;
- #|| $self->ut_snumber('seconds')
- return $error if $error;
-
- #quelle kludge
- if ( $self->plandata =~ /^(agent_)?templatename\s+(.*)$/m ) {
- my $name= $2;
-
- foreach my $file (qw( template
- latex latexnotes latexreturnaddress latexfooter
- latexsmallfooter
- html htmlnotes htmlreturnaddress htmlfooter
- ))
- {
- unless ( $conf->exists("invoice_${file}_$name") ) {
- $conf->set(
- "invoice_${file}_$name" =>
- join("\n", $conf->config("invoice_$file") )
- );
- }
- }
- }
-
- if ($self->reason){
- my $reasonr = qsearchs('reason', {'reasonnum' => $self->reason});
- return "Unknown reason" unless $reasonr;
- }
-
- $self->SUPER::check;
-}
-
-=item templatename
-
-Returns the alternate invoice template name, if any, or false if there is
-no alternate template for this invoice event.
-
-=cut
-
-sub templatename {
- my $self = shift;
- if ( $self->plan =~ /^send_(alternate|agent)$/
- && $self->plandata =~ /^(agent_)?templatename (.*)$/m
- )
- {
- $2;
- } else {
- '';
- }
-}
-
-=item due_events
-
-Returns the list of events due, if any, or false if there is none.
-Requires record and payby, but event_time and extra_sql are optional.
-
-=cut
-
-sub due_events {
- my ($record, $payby, $event_time, $extra_sql) = @_;
-
- #cluck "DEPRECATED: FS::part_bill_event::due_events called on $record";
- confess "DEPRECATED: FS::part_bill_event::due_events called on $record";
-
- my $interval = 0;
- if ($record->_date){
- $event_time = time unless $event_time;
- $interval = $event_time - $record->_date;
- }
- sort { $a->seconds <=> $b->seconds
- || $a->weight <=> $b->weight
- || $a->eventpart <=> $b->eventpart }
- grep { ref($record) ne 'FS::cust_bill' || $_->eventcode !~ /honor_dundate/
- || $event_time > $record->cust_main->dundate
- }
- grep { $_->seconds <= ( $interval )
- && ! qsearch( 'cust_bill_event', {
- 'invnum' => $record->get($record->dbdef_table->primary_key),
- 'eventpart' => $_->eventpart,
- 'status' => 'done',
- } )
- }
- qsearch( {
- 'table' => 'part_bill_event',
- 'hashref' => { 'payby' => $payby,
- 'disabled' => '', },
- 'extra_sql' => $extra_sql,
- } );
-
-
-}
-
-=item do_event
-
-Performs the event and returns any errors that occur.
-Requires a record on which to perform the event.
-Should only be performed inside a transaction.
-
-=cut
-
-sub do_event {
- my ($self, $object, %options) = @_;
-
- #cluck "DEPRECATED: FS::part_bill_event::do_event called on $self";
- confess "DEPRECATED: FS::part_bill_event::do_event called on $self";
-
- warn " calling event (". $self->eventcode. ") for " . $object->table . " " ,
- $object->get($object->dbdef_table->primary_key) . "\n" if $DEBUG > 1;
- my $oldAutoCommit = $FS::UID::AutoCommit;
- local $FS::UID::AutoCommit = 0;
-
- # for "callback" -- heh
- my $cust_main = $object->cust_main;
- my $cust_bill;
- if ($object->table eq 'cust_bill'){
- $cust_bill = $object;
- }
- my $cust_pay_batch;
- if ($object->table eq 'cust_pay_batch'){
- $cust_pay_batch = $object;
- }
-
- my $error;
- {
- local $SIG{__DIE__}; # don't want Mason __DIE__ handler active
- $error = eval $self->eventcode;
- }
-
- my $status = '';
- my $statustext = '';
- if ( $@ ) {
- $status = 'failed';
- $statustext = $@;
- } elsif ( $error ) {
- $status = 'done';
- $statustext = $error;
- } else {
- $status = 'done';
- }
-
- #add cust_bill_event
- my $cust_bill_event = new FS::cust_bill_event {
-# 'invnum' => $object->get($object->dbdef_table->primary_key),
- 'invnum' => $object->invnum,
- 'eventpart' => $self->eventpart,
- '_date' => time,
- 'status' => $status,
- 'statustext' => $statustext,
- };
- $error = $cust_bill_event->insert;
- if ( $error ) {
- my $e = 'WARNING: Event run but database not updated - '.
- 'error inserting cust_bill_event, invnum #'. $object->invnum .
- ', eventpart '. $self->eventpart.": $error";
- warn $e;
- return $e;
- }
- '';
-}
-
-=item reasontext
-
-Returns the text of any reason associated with this event.
-
-=cut
-
-sub reasontext {
- my $self = shift;
- my $r = qsearchs('reason', { 'reasonnum' => $self->reason });
- if ($r){
- $r->reason;
- }else{
- '';
- }
-}
-
-=back
-
-=head1 BUGS
-
-The whole "eventcode" idea is bunk. This should be refactored with subclasses
-like part_pkg/ and part_export/
-
-=head1 SEE ALSO
-
-L<FS::cust_bill>, L<FS::cust_bill_event>, L<FS::Record>, schema.html from the
-base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/part_event.pm b/FS/FS/part_event.pm
index e7acf5af2..d15f35b7d 100644
--- a/FS/FS/part_event.pm
+++ b/FS/FS/part_event.pm
@@ -604,6 +604,22 @@ sub process_initialize {
$part_event->initialize;
}
+sub _upgrade_data { #class method
+ my ($class, %opts) = @_;
+
+ foreach my $part_event (
+ qsearch('part_event', { 'action' => 'cust_bill_realtime_card' }),
+ qsearch('part_event', { 'action' => 'cust_bill_realtime_check' }),
+ ) {
+
+ $part_event->action('realtime_auto');
+ my $error = $part_event->replace;
+ die $error if $error;
+
+ }
+
+}
+
=back
=head1 SEE ALSO
diff --git a/FS/FS/part_event/Action/realtime_auto.pm b/FS/FS/part_event/Action/realtime_auto.pm
new file mode 100644
index 000000000..3902319d8
--- /dev/null
+++ b/FS/FS/part_event/Action/realtime_auto.pm
@@ -0,0 +1,41 @@
+package FS::part_event::Action::realtime_auto;
+
+use strict;
+use base qw( FS::part_event::Action );
+
+sub description {
+ #'Run card with a <a href="http://420.am/business-onlinepayment/">Business::OnlinePayment</a> realtime gateway';
+ 'Run card or check with a Business::OnlinePayment realtime gateway';
+}
+
+sub eventtable_hashref {
+ { 'cust_bill' => 1,
+ 'cust_main' => 1,
+ };
+}
+
+sub default_weight { 30; }
+
+sub do_action {
+ my( $self, $object ) = @_;
+
+ my $cust_main = $self->cust_main($object);
+
+ my %opt = ('cc_surcharge_from_event' => 1);
+
+ my $amount;
+ my $balance = $cust_main->balance;
+ if ( ref($object) eq 'FS::cust_main' ) {
+ $amount = $balance;
+ } elsif ( ref($object) eq 'FS::cust_bill' ) {
+ $amount = ( $balance < $object->owed ) ? $balance : $object->owed;
+ $opt{'invnum'} = $object->invnum;
+ } else {
+ die 'guru meditation #5454.au';
+ }
+
+ $cust_main->realtime_cust_payby( 'amount' => $amount, %opt, );
+
+}
+
+1;
diff --git a/FS/FS/pay_batch/RBC.pm b/FS/FS/pay_batch/RBC.pm
index a5c468367..4b11fdb89 100644
--- a/FS/FS/pay_batch/RBC.pm
+++ b/FS/FS/pay_batch/RBC.pm
@@ -108,7 +108,7 @@ $name = 'RBC';
sprintf("%3s",$trans_code).
sprintf("%10s",$client_num).
' '.
- sprintf("%-19s", $cust_pay_batch->cust_main->custnum).
+ sprintf("%-19s", $cust_pay_batch->paybatchnum).
'00'.
sprintf("%04s", $bankno).
sprintf("%05s", $branch).
diff --git a/FS/FS/tax_rate.pm b/FS/FS/tax_rate.pm
index ec3bb12b6..0047f9d5f 100644
--- a/FS/FS/tax_rate.pm
+++ b/FS/FS/tax_rate.pm
@@ -2055,9 +2055,6 @@ sub generate_liability_report {
join(';', map { "$_=". uri_escape($t->$_) } @params);
my $itemdesc_loc =
- # " payby != 'COMP' ". # breaks the entire report under 4.x
- # # and unnecessary since COMP accounts don't
- # # get taxes calculated in the first place
" ( itemdesc = ? OR ? = '' AND itemdesc IS NULL ) ".
"AND ". FS::tax_rate_location->location_sql( map { $_ => $t->$_ }
@taxparams
diff --git a/FS/MANIFEST b/FS/MANIFEST
index b7d347b98..0542dfde2 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -86,7 +86,6 @@ FS/cust_main_Mixin.pm
FS/cust_main_county.pm
FS/cust_main_invoice.pm
FS/cust_pay.pm
-FS/cust_bill_event.pm
FS/cust_bill_pay.pm
FS/cust_pay_batch.pm
FS/cust_pay_refund.pm
@@ -110,7 +109,6 @@ FS/h_svc_domain.pm
FS/h_svc_external.pm
FS/h_svc_forward.pm
FS/h_svc_www.pm
-FS/part_bill_event.pm
FS/payinfo_Mixin.pm
FS/export_svc.pm
FS/export_device.pm
@@ -246,7 +244,6 @@ t/UID.t
t/Msgcat.t
t/SearchCache.t
t/cust_bill.t
-t/cust_bill_event.t
t/cust_bill_pay.t
t/cust_bill_pkg.t
t/cust_bill_pkg_detail.t
@@ -280,7 +277,6 @@ t/cust_tax_exempt.t
t/cust_tax_exempt_pkg.t
t/domain_record.t
t/nas.t
-t/part_bill_event.t
t/export_svc.t
t/export_device.t
t/part_export.t
diff --git a/FS/bin/freeside-daily b/FS/bin/freeside-daily
index f14e2b337..af48ec0cb 100755
--- a/FS/bin/freeside-daily
+++ b/FS/bin/freeside-daily
@@ -12,6 +12,11 @@ getopts("p:a:d:vl:sy:nmrkg:o", \%opt);
my $user = shift or die &usage;
adminsuidsetup $user;
+
+die "The -p option has been removed in version 4 -- customers no longer have ".
+ "a single, specific payment type\n"
+ if $opt{'p'};
+
my $log = FS::Log->new('daily');
$log->info('start');
@@ -108,7 +113,7 @@ sub untaint_argv {
}
sub usage {
- die "Usage:\n\n freeside-daily [ -d 'date' ] [ -y days ] [ -p 'payby' ] [ -a agentnum,agentnum,... ] [ -s ] [ -v ] [ -l level ] [ -m ] [ -k ] user [ custnum custnum ... ]\n";
+ die "Usage:\n\n freeside-daily [ -d 'date' ] [ -y days ] [ -a agentnum,agentnum,... ] [ -s ] [ -v ] [ -l level ] [ -m ] [ -k ] user [ custnum custnum ... ]\n";
}
###
@@ -121,7 +126,7 @@ freeside-daily - Run daily billing and invoice collection events.
=head1 SYNOPSIS
- freeside-daily [ -d 'date' ] [ -y days ] [ -p 'payby' ] [ -a agentnum,agentnum,... ] [ -s ] [ -o ] [ -v ] [ -l level ] [ -m ] [ -r ] [ -k ] user [ custnum custnum ... ]
+ freeside-daily [ -d 'date' ] [ -y days ] [ -a agentnum,agentnum,... ] [ -s ] [ -o ] [ -v ] [ -l level ] [ -m ] [ -r ] [ -k ] user [ custnum custnum ... ]
=head1 DESCRIPTION
@@ -143,7 +148,7 @@ the bill and collect methods of a cust_main object. See L<FS::cust_main>.
with today's date, irregardless of the pretend date used to pre-generate
the invoices.
- -p: Only process customers with the specified payby (CARD, DCRD, CHEK, DCHK, BILL, COMP, LECB)
+ -p: Deprecated, will produce a fatal error (formerly was: Only process customers with the specified payby (CARD, DCRD, CHEK, DCHK, BILL, COMP, LECB))
-a: Only process customers with the specified agentnum. Multiple agentnums can be specified, separated with commas.
diff --git a/FS/bin/freeside-monthly b/FS/bin/freeside-monthly
index 431fbd86f..7be3776fc 100755
--- a/FS/bin/freeside-monthly
+++ b/FS/bin/freeside-monthly
@@ -12,6 +12,10 @@ getopts("p:a:d:vsy:m", \%opt);
my $user = shift or die &usage;
adminsuidsetup $user;
+die "The -p option has been removed in version 4 -- customers no longer have ".
+ "a single, specific payment type"
+ if $opt{'p'};
+
use FS::Cron::bill qw(bill);
bill(%opt, 'check_freq'=>'1m' );
@@ -45,7 +49,7 @@ freeside-monthly - Run monthly billing and invoice collection events.
=head1 SYNOPSIS
- freeside-monthly [ -d 'date' ] [ -y days ] [ -p 'payby' ] [ -a agentnum ] [ -s ] [ -v ] user [ custnum custnum ... ]
+ freeside-monthly [ -d 'date' ] [ -y days ] [ -a agentnum ] [ -s ] [ -v ] user [ custnum custnum ... ]
=head1 DESCRIPTION
@@ -64,7 +68,7 @@ the bill and collect methods of a cust_main object. See L<FS::cust_main>.
"pretend date" 15 days from whatever was specified by the -d switch
(or now, if no -d switch was given).
- -p: Only process customers with the specified payby (CARD, DCRD, CHEK, DCHK, BILL, COMP, LECB)
+ -p: Deprecated, will produce a fatal error (formerly was: Only process customers with the specified payby (CARD, DCRD, CHEK, DCHK, BILL, COMP, LECB))
-a: Only process customers with the specified agentnum
diff --git a/FS/bin/freeside-vitelity-cdrimport b/FS/bin/freeside-vitelity-cdrimport
new file mode 100755
index 000000000..83bbdd96e
--- /dev/null
+++ b/FS/bin/freeside-vitelity-cdrimport
@@ -0,0 +1,132 @@
+#!/usr/bin/perl
+
+=pod
+
+freeside-vitelity-cdrimport [ -v ] [ -k ]
+ -s date -e date
+ username
+ [ exportnum ]
+
+Download CDRs using the Vitelity API.
+
+-v: Be verbose.
+
+-k: Keep the .csv file for debugging purposes, instead of deleting it.
+
+-s date: Import only records on or after 'date' Now required as the Vitelity
+API has changed.
+
+-e date: Import only records before 'date'. Now required as the Vitelity API
+has changed.
+
+username: a Freeside user
+
+exportnum: Run only for that export. If not specified, this will run for
+all Vitelity exports.
+
+=cut
+
+use strict;
+use FS::UID qw(adminsuidsetup dbh);
+use FS::Record qw(qsearchs qsearch);
+use FS::cdr;
+use FS::part_export;
+use Getopt::Std;
+use File::Temp;
+use Date::Format 'time2str';
+use Date::Parse 'str2time';
+
+my %opt;
+getopts('vks:e:', \%opt);
+
+my $user = shift or die &usage;
+my $exportnum = shift;
+my $dbh = adminsuidsetup $user;
+
+my $start = $opt{'s'} ? str2time($opt{'s'}) : die &usage('-s is now required');
+my $end = $opt{'e'} ? str2time($opt{'e'}) : die &usage('-e is now required');
+
+local $FS::UID::AutoCommit = 0;
+
+my @part_exports;
+if ( $exportnum ) {
+ @part_exports = ( qsearchs('part_export', { 'exportnum' => $exportnum }) )
+ or die "exportnum $exportnum not found\n";
+}
+else {
+ @part_exports = qsearch('part_export', { 'exporttype' => 'vitelity' })
+ or die "no Vitelity exports found\n";
+}
+
+foreach my $export (@part_exports) {
+ my $exportnum = $export->exportnum;
+ print "Processing exportnum $exportnum.\n" if $opt{'v'};
+ $export->isa('FS::part_export::vitelity')
+ or die "exportnum $exportnum is not a Vitelity export\n";
+
+ my $dir = $FS::UID::cache_dir . "/cache.". $FS::UID::datasrc;
+ my $temp = new File::Temp ( TEMPLATE => 'download.XXXXXXXX',
+ SUFFIX => '.csv',
+ DIR => $dir,
+ UNLINK => !$opt{'k'} )
+ or die "can't open temporary file to store download: $!\n";
+ print "Downloading to ".$temp->filename."\n" if $opt{'v'};
+
+ print "Sending API request..." if $opt{'v'};
+
+ my $s = time2str('%m-%d-%Y', $start);
+ my $e = time2str('%m-%d-%Y', $end);
+
+ my @records = eval { $export->vitelity_command('getcdr',
+ 'startdate' => $s,
+ 'enddate' => $e,
+ );
+ };
+ if ( $@ ) {
+ print "download error: $@\n";
+ exit(-1);
+ }
+
+ print "received ".scalar(@records)." records\n" if $opt{'v'};
+ if ( !@records ) {
+ print "No records to process.\n" if $opt{'v'};
+ exit(1);
+ }
+
+ print $temp "Date,Source,Destination,Seconds,CallerID,Disposition,Cost\n";
+
+ while (my $rec = shift @records) {
+ print $temp $rec, "\n";
+ }
+ close $temp;
+
+ my $format = 'vitelity';
+ my $batchname = "vitelity-$exportnum-".time2str('%Y/%m/%d-%T',time);
+
+ print "Importing batch..." if $opt{'v'};
+ my $error = FS::cdr::batch_import( {
+ 'file' => $temp->filename,
+ 'format' => $format,
+ 'batch_namevalue' => $batchname,
+ } );
+
+ if ( $error ) {
+ $dbh->rollback;
+ print "error: $error";
+ exit(-2);
+ }
+}
+$dbh->commit;
+print "done.\n";
+exit(0);
+
+sub usage {
+ my $err = @_ ? shift."\n\n" : '';
+$err."Usage:
+freeside-vitelity-cdrimport [ -v ] [ -k ]
+ -s date -e date
+ username
+ [ exportnum ]
+";
+}
+
diff --git a/FS/bin/freeside-voipinnovations-cdrimport b/FS/bin/freeside-voipinnovations-cdrimport
new file mode 100755
index 000000000..484b330f6
--- /dev/null
+++ b/FS/bin/freeside-voipinnovations-cdrimport
@@ -0,0 +1,129 @@
+#!/usr/bin/perl
+
+use strict;
+use Getopt::Std;
+use Date::Format;
+use File::Temp 'tempdir';
+use Net::FTP;
+use FS::UID qw(adminsuidsetup datasrc dbh);
+use FS::cdr;
+use FS::cdr_batch;
+use FS::Record qw(qsearch qsearchs);
+use Date::Format 'time2str';
+use Date::Parse 'str2time';
+
+
+###
+# parse command line
+###
+
+use vars qw( $opt_d $opt_v $opt_c $opt_s $opt_e $opt_a );
+getopts('dvc:s:e:a');
+
+my ($user, $login, $password) = @ARGV;
+$user and $login and $password or die &usage;
+
+my $dbh = adminsuidsetup $user;
+$FS::UID::AutoCommit = 0;
+
+# index already-downloaded batches
+my @previous = qsearch({
+ 'table' => 'cdr_batch',
+ 'hashref' => { 'cdrbatch' => {op=>'like', value=>'voip_innovations%'} },
+ 'order_by' => 'ORDER BY cdrbatch DESC',
+});
+my %exists = map {$_->cdrbatch => 1} @previous;
+
+my $tempdir = tempdir( CLEANUP => !$opt_v );
+
+my $format = 'voip_innovations';
+my $hostname = 'cdrs.globalpopsvoip.com';
+
+my $ftp = Net::FTP->new($hostname, Debug => $opt_d)
+ or die "Can't connect to $hostname: $@\n";
+
+$ftp->login($login, $password)
+ or die "Login failed: ".$ftp->message."\n";
+
+###
+# get the file list
+###
+
+warn "Retrieving directory listing\n" if $opt_v;
+
+$ftp->cwd('/');
+my @dirs = $ftp->ls();
+warn scalar(@dirs)." directories found.\n" if $opt_v;
+# apply date range
+if ( $opt_a ) {
+ my $most_recent = $previous[0];
+ if ($most_recent) {
+ if ($most_recent->cdrbatch =~ /^voip_innovations-(\d+)/) {
+ my $date = $1;
+ warn "limiting to dates > $date (from most recent batch)\n" if $opt_v;
+ @dirs = grep {$_ > $date} @dirs;
+ }
+ } # else download them all
+}
+if ( $opt_s ) {
+ # start date
+ # normalize date format
+ $opt_s = time2str('%Y%m%d', str2time($opt_s)) if $opt_s =~ /\D/;
+ warn "limiting to dates > $opt_s\n" if $opt_v;
+ @dirs = grep {$_ > $opt_s} @dirs;
+}
+if ( $opt_e ) {
+ # end date
+ $opt_e = time2str('%Y%m%d', str2time($opt_e)) if $opt_e =~ /\D/;
+ warn "limiting to dates < $opt_e\n" if $opt_v;
+ @dirs = grep {$_ < $opt_e} @dirs;
+}
+warn scalar(@dirs) ." to be downloaded\n" if $opt_v;
+foreach my $dir (@dirs) {
+ $ftp->cwd($dir);
+ foreach my $file ($ftp->ls) {
+ warn "downloading $file\n" if $opt_v;
+ $ftp->get($file, "$tempdir/$file");
+ warn "processing $file\n" if $opt_v;
+
+ # "voip_innovations-20130628/20130628_20130628.CDR"
+ my $batchname = "$format-$dir/$file";
+ if ($exists{$batchname}) {
+ warn "already imported $file\n";
+ next;
+ }
+ my $import_options = {
+ 'file' => "$tempdir/$file",
+ 'format' => $format,
+ 'batch_namevalue' => $batchname,
+ 'empty_ok' => 1,
+ };
+ $import_options->{'cdrtypenum'} = $opt_c if $opt_c;
+
+ my $error = FS::cdr::batch_import($import_options);
+
+ if ( $error ) {
+ die "error processing $dir/$file: $error\n";
+ }
+ }
+ $ftp->cwd('..');
+}
+warn "finished\n";
+$dbh->commit;
+
+###
+# subs
+###
+
+sub usage {
+ "Usage: \n freeside-voip_innovations-cdrimport user login password\n [ options ]
+ Options:
+ -v: be verbose
+ -d: enable FTP debugging (very noisy)
+ -c num: apply a cdrtypenum to the imported CDRs
+ -s date: start date
+ -e date: end date
+ -a: automatically choose start date from most recently downloaded batch
+ ";
+}
+
diff --git a/FS/t/cust_bill_event.t b/FS/t/cust_bill_event.t
deleted file mode 100644
index 0e2ca3e24..000000000
--- a/FS/t/cust_bill_event.t
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::cust_bill_event;
-$loaded=1;
-print "ok 1\n";
diff --git a/FS/t/part_bill_event.t b/FS/t/part_bill_event.t
deleted file mode 100644
index 5626a9f97..000000000
--- a/FS/t/part_bill_event.t
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::part_bill_event;
-$loaded=1;
-print "ok 1\n";