summaryrefslogtreecommitdiff
path: root/FS/FS
diff options
context:
space:
mode:
authorivan <ivan>2005-08-17 22:23:46 +0000
committerivan <ivan>2005-08-17 22:23:46 +0000
commitfeef0e4c2b4bd6b776b25f5a1bd6fdbf63fd08b2 (patch)
treeba578a825368c92a499f09c2278accf6da1509f8 /FS/FS
parent7af914081daa5896f1b0d69de0f48af6978f1fe4 (diff)
infrastructure for easier schema changes, and: add payment_gateway, payment_gateway_option and agent_payment_gateway tables, add paystart_month, paystart_year, payissue and payip fields to cust_main, add preliminary gateway and gateway override editing to web UI, use payment gateway override when processing payments (card type, not taxclass yet)
Diffstat (limited to 'FS/FS')
-rw-r--r--FS/FS/Record.pm80
-rw-r--r--FS/FS/Schema.pm1247
-rw-r--r--FS/FS/cust_main.pm175
-rw-r--r--FS/FS/option_Common.pm295
-rw-r--r--FS/FS/part_export.pm140
-rw-r--r--FS/FS/part_export_option.pm2
-rw-r--r--FS/FS/part_pkg.pm6
-rw-r--r--FS/FS/payment_gateway.pm147
-rw-r--r--FS/FS/payment_gateway_option.pm126
9 files changed, 1985 insertions, 233 deletions
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index f806e4f88..9cff57936 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -1,9 +1,8 @@
package FS::Record;
use strict;
-use vars qw( $dbdef_file $dbdef $setup_hack $AUTOLOAD @ISA @EXPORT_OK $DEBUG
- $me %dbdef_cache %virtual_fields_cache $nowarn_identical );
-use subs qw(reload_dbdef);
+use vars qw( $AUTOLOAD @ISA @EXPORT_OK $DEBUG
+ $me %virtual_fields_cache $nowarn_identical );
use Exporter;
use Carp qw(carp cluck croak confess);
use File::CounterFile;
@@ -11,6 +10,7 @@ use Locale::Country;
use DBI qw(:sql_types);
use DBIx::DBSchema 0.25;
use FS::UID qw(dbh getotaker datasrc driver_name);
+use FS::Schema qw(dbdef);
use FS::SearchCache;
use FS::Msgcat qw(gettext);
use FS::Conf;
@@ -20,6 +20,8 @@ use FS::part_virtual_field;
use Tie::IxHash;
@ISA = qw(Exporter);
+
+#export dbdef for now... everything else expects to find it here
@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch);
$DEBUG = 0;
@@ -33,13 +35,10 @@ my $rsa_loaded;
my $rsa_encrypt;
my $rsa_decrypt;
-#ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::Record'} = sub {
+FS::UID->install_callback( sub {
$conf = new FS::Conf;
$File::CounterFile::DEFAULT_DIR = "/usr/local/etc/freeside/counters.". datasrc;
- $dbdef_file = "/usr/local/etc/freeside/dbdef.". datasrc;
- &reload_dbdef unless $setup_hack; #$setup_hack needed now?
-};
+} );
=head1 NAME
@@ -48,7 +47,7 @@ FS::Record - Database record objects
=head1 SYNOPSIS
use FS::Record;
- use FS::Record qw(dbh fields qsearch qsearchs dbdef);
+ use FS::Record qw(dbh fields qsearch qsearchs);
$record = new FS::Record 'table', \%hash;
$record = new FS::Record 'table', { 'column' => 'value', ... };
@@ -94,10 +93,6 @@ FS::Record - Database record objects
$error = $record->ut_anything('column');
$error = $record->ut_name('column');
- $dbdef = reload_dbdef;
- $dbdef = reload_dbdef "/non/standard/filename";
- $dbdef = dbdef;
-
$quoted_value = _quote($value,'table','field');
#deprecated
@@ -218,7 +213,7 @@ sub qsearch {
my $dbh = dbh;
my $table = $cache ? $cache->table : $stable;
- my $dbdef_table = $dbdef->table($table)
+ my $dbdef_table = dbdef->table($table)
or die "No schema for table $table found - ".
"do you need to create it or run dbdef-create?";
my $pkey = $dbdef_table->primary_key;
@@ -254,7 +249,7 @@ sub qsearch {
if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) {
if ( $op eq '=' ) {
if ( driver_name eq 'Pg' ) {
- my $type = $dbdef->table($table)->column($column)->type;
+ my $type = dbdef->table($table)->column($column)->type;
if ( $type =~ /(int|serial)/i ) {
qq-( $column IS NULL )-;
} else {
@@ -265,7 +260,7 @@ sub qsearch {
}
} elsif ( $op eq '!=' ) {
if ( driver_name eq 'Pg' ) {
- my $type = $dbdef->table($table)->column($column)->type;
+ my $type = dbdef->table($table)->column($column)->type;
if ( $type =~ /(int|serial)/i ) {
qq-( $column IS NOT NULL )-;
} else {
@@ -335,7 +330,7 @@ sub qsearch {
grep defined( $record->{$_} ) && $record->{$_} ne '', @real_fields
) {
if ( $record->{$field} =~ /^\d+(\.\d+)?$/
- && $dbdef->table($table)->column($field)->type =~ /(int|serial)/i
+ && dbdef->table($table)->column($field)->type =~ /(int|serial)/i
) {
$sth->bind_param($bind++, $record->{$field}, { TYPE => SQL_INTEGER } );
} else {
@@ -446,7 +441,7 @@ sub by_key {
my $table = $class->table
or croak "No table for $class found";
- my $dbdef_table = $dbdef->table($table)
+ my $dbdef_table = dbdef->table($table)
or die "No schema for table $table found - ".
"do you need to create it or run dbdef-create?";
my $pkey = $dbdef_table->primary_key
@@ -520,7 +515,7 @@ Returns the DBIx::DBSchema::Table object for the table.
sub dbdef_table {
my($self)=@_;
my($table)=$self->table;
- $dbdef->table($table);
+ dbdef->table($table);
}
=item get, getfield COLUMN
@@ -787,7 +782,7 @@ sub insert {
my $h_sth;
- if ( defined $dbdef->table('h_'. $table) ) {
+ if ( defined dbdef->table('h_'. $table) ) {
my $h_statement = $self->_h_statement('insert');
warn "[debug]$me $h_statement\n" if $DEBUG > 2;
$h_sth = dbh->prepare($h_statement) or do {
@@ -848,7 +843,7 @@ sub delete {
my $sth = dbh->prepare($statement) or return dbh->errstr;
my $h_sth;
- if ( defined $dbdef->table('h_'. $self->table) ) {
+ if ( defined dbdef->table('h_'. $self->table) ) {
my $h_statement = $self->_h_statement('delete');
warn "[debug]$me $h_statement\n" if $DEBUG > 2;
$h_sth = dbh->prepare($h_statement) or return dbh->errstr;
@@ -992,7 +987,7 @@ sub replace {
my $sth = dbh->prepare($statement) or return dbh->errstr;
my $h_old_sth;
- if ( defined $dbdef->table('h_'. $old->table) ) {
+ if ( defined dbdef->table('h_'. $old->table) ) {
my $h_old_statement = $old->_h_statement('replace_old');
warn "[debug]$me $h_old_statement\n" if $DEBUG > 2;
$h_old_sth = dbh->prepare($h_old_statement) or return dbh->errstr;
@@ -1001,7 +996,7 @@ sub replace {
}
my $h_new_sth;
- if ( defined $dbdef->table('h_'. $new->table) ) {
+ if ( defined dbdef->table('h_'. $new->table) ) {
my $h_new_statement = $new->_h_statement('replace_new');
warn "[debug]$me $h_new_statement\n" if $DEBUG > 2;
$h_new_sth = dbh->prepare($h_new_statement) or return dbh->errstr;
@@ -1552,9 +1547,9 @@ sub virtual_fields {
my $table;
$table = $self->table or confess "virtual_fields called on non-table";
- confess "Unknown table $table" unless $dbdef->table($table);
+ confess "Unknown table $table" unless dbdef->table($table);
- return () unless $self->dbdef->table('part_virtual_field');
+ return () unless dbdef->table('part_virtual_field');
unless ( $virtual_fields_cache{$table} ) {
my $query = 'SELECT name from part_virtual_field ' .
@@ -1622,40 +1617,11 @@ fields() and other subroutines elsewhere in FS::Record.
sub real_fields {
my $table = shift;
- my($table_obj) = $dbdef->table($table);
+ my($table_obj) = dbdef->table($table);
confess "Unknown table $table" unless $table_obj;
$table_obj->columns;
}
-=item reload_dbdef([FILENAME])
-
-Load a database definition (see L<DBIx::DBSchema>), optionally from a
-non-default filename. This command is executed at startup unless
-I<$FS::Record::setup_hack> is true. Returns a DBIx::DBSchema object.
-
-=cut
-
-sub reload_dbdef {
- my $file = shift || $dbdef_file;
-
- unless ( exists $dbdef_cache{$file} ) {
- warn "[debug]$me loading dbdef for $file\n" if $DEBUG;
- $dbdef_cache{$file} = DBIx::DBSchema->load( $file )
- or die "can't load database schema from $file";
- } else {
- warn "[debug]$me re-using cached dbdef for $file\n" if $DEBUG;
- }
- $dbdef = $dbdef_cache{$file};
-}
-
-=item dbdef
-
-Returns the current database definition. See L<DBIx::DBSchema>.
-
-=cut
-
-sub dbdef { $dbdef; }
-
=item _quote VALUE, TABLE, COLUMN
This is an internal function used to construct SQL statements. It returns
@@ -1666,7 +1632,7 @@ type (see L<DBIx::DBSchema::Column>) does not end in `char' or `binary'.
sub _quote {
my($value, $table, $column) = @_;
- my $column_obj = $dbdef->table($table)->column($column);
+ my $column_obj = dbdef->table($table)->column($column);
my $column_type = $column_obj->type;
my $nullable = $column_obj->null;
@@ -1701,7 +1667,7 @@ sub vfieldpart_hashref {
my $self = shift;
my $table = $self->table;
- return {} unless $self->dbdef->table('part_virtual_field');
+ return {} unless dbdef->table('part_virtual_field');
my $dbh = dbh;
my $statement = "SELECT vfieldpart, name FROM part_virtual_field WHERE ".
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
new file mode 100644
index 000000000..09e16fe75
--- /dev/null
+++ b/FS/FS/Schema.pm
@@ -0,0 +1,1247 @@
+package FS::Schema;
+
+use vars qw(@ISA @EXPORT_OK $DEBUG $setup_hack %dbdef_cache);
+use subs qw(reload_dbdef);
+use Exporter;
+use DBIx::DBSchema 0.25;
+use DBIx::DBSchema::Table;
+use DBIx::DBSchema::Column;
+use DBIx::DBSchema::ColGroup::Unique;
+use DBIx::DBSchema::ColGroup::Index;
+use FS::UID qw(datasrc);
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw( dbdef dbdef_dist reload_dbdef );
+
+$DEBUG = 0;
+$me = '[FS::Schema]';
+
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ #$conf = new FS::Conf;
+ &reload_dbdef("/usr/local/etc/freeside/dbdef.". datasrc)
+ unless $setup_hack; #$setup_hack needed now?
+} );
+
+=head1 NAME
+
+FS::Schema - Freeside database schema
+
+=head1 SYNOPSYS
+
+ use FS::Schema qw(dbdef dbdef_dist reload_dbdef);
+
+ $dbdef = reload_dbdef;
+ $dbdef = reload_dbdef "/non/standard/filename";
+ $dbdef = dbdef;
+ $dbdef_dist = dbdef_dist;
+
+=head1 DESCRIPTION
+
+This class represents the database schema.
+
+=head1 METHODS
+
+=over 4
+
+=item reload_dbdef([FILENAME])
+
+Load a database definition (see L<DBIx::DBSchema>), optionally from a
+non-default filename. This command is executed at startup unless
+I<$FS::Schema::setup_hack> is true. Returns a DBIx::DBSchema object.
+
+=cut
+
+sub reload_dbdef {
+ my $file = shift;
+
+ unless ( exists $dbdef_cache{$file} ) {
+ warn "[debug]$me loading dbdef for $file\n" if $DEBUG;
+ $dbdef_cache{$file} = DBIx::DBSchema->load( $file )
+ or die "can't load database schema from $file";
+ } else {
+ warn "[debug]$me re-using cached dbdef for $file\n" if $DEBUG;
+ }
+ $dbdef = $dbdef_cache{$file};
+}
+
+=item dbdef
+
+Returns the current database definition (represents the current database,
+assuming it is up-to-date). See L<DBIx::DBSchema>.
+
+=cut
+
+sub dbdef { $dbdef; }
+
+=item dbdef_dist [ OPTION => VALUE ... ]
+
+Returns the current canoical database definition as defined in this file.
+
+=cut
+
+sub dbdef_dist {
+
+ ###
+ # create a dbdef object from the old data structure
+ ###
+
+ my $tables_hashref = tables_hashref();
+
+ #turn it into objects
+ my $dbdef = new DBIx::DBSchema map {
+ my @columns;
+ while (@{$tables_hashref->{$_}{'columns'}}) {
+ my($name, $type, $null, $length) =
+ splice @{$tables_hashref->{$_}{'columns'}}, 0, 4;
+ push @columns, new DBIx::DBSchema::Column ( $name,$type,$null,$length );
+ }
+ DBIx::DBSchema::Table->new(
+ $_,
+ $tables_hashref->{$_}{'primary_key'},
+ DBIx::DBSchema::ColGroup::Unique->new($tables_hashref->{$_}{'unique'}),
+ DBIx::DBSchema::ColGroup::Index->new($tables_hashref->{$_}{'index'}),
+ @columns,
+ );
+ } keys %$tables_hashref;
+
+ if ( $DEBUG ) {
+ warn "[debug]$me initial dbdef_dist created ($dbdef) with tables:\n";
+ warn "[debug]$me $_\n" foreach $dbdef->tables;
+ }
+
+ my $cust_main = $dbdef->table('cust_main');
+ #unless ($ship) { #remove ship_ from cust_main
+ # $cust_main->delcolumn($_) foreach ( grep /^ship_/, $cust_main->columns );
+ #} else { #add indices
+ push @{$cust_main->index->lol_ref},
+ map { [ "ship_$_" ] } qw( last company daytime night fax );
+ #}
+
+ #add radius attributes to svc_acct
+ #
+ #my($svc_acct)=$dbdef->table('svc_acct');
+ #
+ #my($attribute);
+ #foreach $attribute (@attributes) {
+ # $svc_acct->addcolumn ( new DBIx::DBSchema::Column (
+ # 'radius_'. $attribute,
+ # 'varchar',
+ # 'NULL',
+ # $char_d,
+ # ));
+ #}
+ #
+ #foreach $attribute (@check_attributes) {
+ # $svc_acct->addcolumn( new DBIx::DBSchema::Column (
+ # 'rc_'. $attribute,
+ # 'varchar',
+ # 'NULL',
+ # $char_d,
+ # ));
+ #}
+
+ #create history tables (false laziness w/create-history-tables)
+ foreach my $table (
+ grep { ! /^clientapi_session/ }
+ grep { ! /^h_/ }
+ $dbdef->tables
+ ) {
+ my $tableobj = $dbdef->table($table)
+ or die "unknown table $table";
+
+ die "unique->lol_ref undefined for $table"
+ unless defined $tableobj->unique->lol_ref;
+ die "index->lol_ref undefined for $table"
+ unless defined $tableobj->index->lol_ref;
+
+ my $h_tableobj = DBIx::DBSchema::Table->new( {
+ name => "h_$table",
+ primary_key => 'historynum',
+ unique => DBIx::DBSchema::ColGroup::Unique->new( [] ),
+ 'index' => DBIx::DBSchema::ColGroup::Index->new( [
+ @{$tableobj->unique->lol_ref},
+ @{$tableobj->index->lol_ref}
+ ] ),
+ columns => [
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'historynum',
+ 'type' => 'serial',
+ 'null' => 'NOT NULL',
+ 'length' => '',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'history_date',
+ 'type' => 'int',
+ 'null' => 'NULL',
+ 'length' => '',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'history_user',
+ 'type' => 'varchar',
+ 'null' => 'NOT NULL',
+ 'length' => '80',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ DBIx::DBSchema::Column->new( {
+ 'name' => 'history_action',
+ 'type' => 'varchar',
+ 'null' => 'NOT NULL',
+ 'length' => '80',
+ 'default' => '',
+ 'local' => '',
+ } ),
+ map {
+ my $column = $tableobj->column($_);
+
+ #clone so as to not disturb the original
+ $column = DBIx::DBSchema::Column->new( {
+ map { $_ => $column->$_() }
+ qw( name type null length default local )
+ } );
+
+ if ( $column->type eq 'serial' ) {
+ $column->type('int');
+ $column->null('NULL');
+ }
+ #$column->default('')
+ # if $column->default =~ /^nextval\(/i;
+ #( my $local = $column->local ) =~ s/AUTO_INCREMENT//i;
+ #$column->local($local);
+ $column;
+ } $tableobj->columns
+ ],
+ } );
+ $dbdef->addtable($h_tableobj);
+ }
+
+ $dbdef;
+
+}
+
+sub tables_hashref {
+
+ my($char_d) = 80; #default maxlength for text fields
+
+ #my(@date_type) = ( 'timestamp', '', '' );
+ my(@date_type) = ( 'int', 'NULL', '' );
+ my(@perl_type) = ( 'text', 'NULL', '' );
+ my @money_type = ( 'decimal', '', '10,2' );
+
+ return {
+
+ 'agent' => {
+ 'columns' => [
+ 'agentnum', 'serial', '', '',
+ 'agent', 'varchar', '', $char_d,
+ 'typenum', 'int', '', '',
+ 'freq', 'int', 'NULL', '',
+ 'prog', @perl_type,
+ 'disabled', 'char', 'NULL', 1,
+ 'username', 'varchar', 'NULL', $char_d,
+ '_password','varchar', 'NULL', $char_d,
+ ],
+ 'primary_key' => 'agentnum',
+ 'unique' => [],
+ 'index' => [ ['typenum'], ['disabled'] ],
+ },
+
+ 'agent_type' => {
+ 'columns' => [
+ 'typenum', 'serial', '', '',
+ 'atype', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'typenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'type_pkgs' => {
+ 'columns' => [
+ 'typepkgnum', 'serial', '', '',
+ 'typenum', 'int', '', '',
+ 'pkgpart', 'int', '', '',
+ ],
+ 'primary_key' => 'typepkgnum',
+ 'unique' => [ ['typenum', 'pkgpart'] ],
+ 'index' => [ ['typenum'] ],
+ },
+
+ 'cust_bill' => {
+ 'columns' => [
+ 'invnum', 'serial', '', '',
+ 'custnum', 'int', '', '',
+ '_date', @date_type,
+ 'charged', @money_type,
+ 'printed', 'int', '', '',
+ 'closed', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'invnum',
+ 'unique' => [],
+ 'index' => [ ['custnum'], ['_date'] ],
+ },
+
+ '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'] ],
+ },
+
+ 'part_bill_event' => {
+ 'columns' => [
+ 'eventpart', 'serial', '', '',
+ 'payby', 'char', '', 4,
+ 'event', 'varchar', '', $char_d,
+ 'eventcode', @perl_type,
+ 'seconds', 'int', 'NULL', '',
+ 'weight', 'int', '', '',
+ 'plan', 'varchar', 'NULL', $char_d,
+ 'plandata', 'text', 'NULL', '',
+ 'disabled', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'eventpart',
+ 'unique' => [],
+ 'index' => [ ['payby'], ['disabled'], ],
+ },
+
+ 'cust_bill_pkg' => {
+ 'columns' => [
+ 'billpkgnum', 'serial', '', '',
+ 'pkgnum', 'int', '', '',
+ 'invnum', 'int', '', '',
+ 'setup', @money_type,
+ 'recur', @money_type,
+ 'sdate', @date_type,
+ 'edate', @date_type,
+ 'itemdesc', 'varchar', 'NULL', $char_d,
+ ],
+ 'primary_key' => 'billpkgnum',
+ 'unique' => [],
+ 'index' => [ ['invnum'] ],
+ },
+
+ 'cust_bill_pkg_detail' => {
+ 'columns' => [
+ 'detailnum', 'serial', '', '',
+ 'pkgnum', 'int', '', '',
+ 'invnum', 'int', '', '',
+ 'detail', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'detailnum',
+ 'unique' => [],
+ 'index' => [ [ 'pkgnum', 'invnum' ] ],
+ },
+
+ 'cust_credit' => {
+ 'columns' => [
+ 'crednum', 'serial', '', '',
+ 'custnum', 'int', '', '',
+ '_date', @date_type,
+ 'amount', @money_type,
+ 'otaker', 'varchar', '', 32,
+ 'reason', 'text', 'NULL', '',
+ 'closed', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'crednum',
+ 'unique' => [],
+ 'index' => [ ['custnum'] ],
+ },
+
+ 'cust_credit_bill' => {
+ 'columns' => [
+ 'creditbillnum', 'serial', '', '',
+ 'crednum', 'int', '', '',
+ 'invnum', 'int', '', '',
+ '_date', @date_type,
+ 'amount', @money_type,
+ ],
+ 'primary_key' => 'creditbillnum',
+ 'unique' => [],
+ 'index' => [ ['crednum'], ['invnum'] ],
+ },
+
+ 'cust_main' => {
+ 'columns' => [
+ 'custnum', 'serial', '', '',
+ 'agentnum', 'int', '', '',
+# 'titlenum', 'int', 'NULL', '',
+ 'last', 'varchar', '', $char_d,
+# 'middle', 'varchar', 'NULL', $char_d,
+ 'first', 'varchar', '', $char_d,
+ 'ss', 'varchar', 'NULL', 11,
+ 'company', 'varchar', 'NULL', $char_d,
+ 'address1', 'varchar', '', $char_d,
+ 'address2', 'varchar', 'NULL', $char_d,
+ 'city', 'varchar', '', $char_d,
+ 'county', 'varchar', 'NULL', $char_d,
+ 'state', 'varchar', 'NULL', $char_d,
+ 'zip', 'varchar', 'NULL', 10,
+ 'country', 'char', '', 2,
+ 'daytime', 'varchar', 'NULL', 20,
+ 'night', 'varchar', 'NULL', 20,
+ 'fax', 'varchar', 'NULL', 12,
+ 'ship_last', 'varchar', 'NULL', $char_d,
+# 'ship_middle', 'varchar', 'NULL', $char_d,
+ 'ship_first', 'varchar', 'NULL', $char_d,
+ 'ship_company', 'varchar', 'NULL', $char_d,
+ 'ship_address1', 'varchar', 'NULL', $char_d,
+ 'ship_address2', 'varchar', 'NULL', $char_d,
+ 'ship_city', 'varchar', 'NULL', $char_d,
+ 'ship_county', 'varchar', 'NULL', $char_d,
+ 'ship_state', 'varchar', 'NULL', $char_d,
+ 'ship_zip', 'varchar', 'NULL', 10,
+ 'ship_country', 'char', 'NULL', 2,
+ 'ship_daytime', 'varchar', 'NULL', 20,
+ 'ship_night', 'varchar', 'NULL', 20,
+ 'ship_fax', 'varchar', 'NULL', 12,
+ 'payby', 'char', '', 4,
+ 'payinfo', 'varchar', 'NULL', 512,
+ 'paycvv', 'varchar', 'NULL', 512,
+ 'paymask', 'varchar', 'NULL', $char_d,
+ #'paydate', @date_type,
+ 'paydate', 'varchar', 'NULL', 10,
+ 'paystart_month', 'int', 'NULL', '',
+ 'paystart_year', 'int', 'NULL', '',
+ 'payissue', 'varchar', 'NULL', 2,
+ 'payname', 'varchar', 'NULL', $char_d,
+ 'payip', 'varchar', 'NULL', 15,
+ 'tax', 'char', 'NULL', 1,
+ 'otaker', 'varchar', '', 32,
+ 'refnum', 'int', '', '',
+ 'referral_custnum', 'int', 'NULL', '',
+ 'comments', 'text', 'NULL', '',
+ ],
+ 'primary_key' => 'custnum',
+ 'unique' => [],
+ #'index' => [ ['last'], ['company'] ],
+ 'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ],
+ [ 'daytime' ], [ 'night' ], [ 'fax' ], [ 'refnum' ],
+ ],
+ },
+
+ 'cust_main_invoice' => {
+ 'columns' => [
+ 'destnum', 'serial', '', '',
+ 'custnum', 'int', '', '',
+ 'dest', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'destnum',
+ 'unique' => [],
+ 'index' => [ ['custnum'], ],
+ },
+
+ 'cust_main_county' => { #county+state+country are checked off the
+ #cust_main_county for validation and to provide
+ # a tax rate.
+ 'columns' => [
+ 'taxnum', 'serial', '', '',
+ 'state', 'varchar', 'NULL', $char_d,
+ 'county', 'varchar', 'NULL', $char_d,
+ 'country', 'char', '', 2,
+ 'taxclass', 'varchar', 'NULL', $char_d,
+ 'exempt_amount', @money_type,
+ 'tax', 'real', '', '', #tax %
+ 'taxname', 'varchar', 'NULL', $char_d,
+ 'setuptax', 'char', 'NULL', 1, # Y = setup tax exempt
+ 'recurtax', 'char', 'NULL', 1, # Y = recur tax exempt
+ ],
+ 'primary_key' => 'taxnum',
+ 'unique' => [],
+ # 'unique' => [ ['taxnum'], ['state', 'county'] ],
+ 'index' => [],
+ },
+
+ 'cust_pay' => {
+ 'columns' => [
+ 'paynum', 'serial', '', '',
+ #now cust_bill_pay #'invnum', 'int', '', '',
+ 'custnum', 'int', '', '',
+ 'paid', @money_type,
+ '_date', @date_type,
+ 'payby', 'char', '', 4, # CARD/BILL/COMP, should be index into
+ # payment type table.
+ 'payinfo', 'varchar', 'NULL', $char_d, #see cust_main above
+ 'paybatch', 'varchar', 'NULL', $char_d, #for auditing purposes.
+ 'closed', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'paynum',
+ 'unique' => [],
+ 'index' => [ [ 'custnum' ], [ 'paybatch' ], [ 'payby' ], [ '_date' ] ],
+ },
+
+ 'cust_pay_void' => {
+ 'columns' => [
+ 'paynum', 'int', '', '',
+ 'custnum', 'int', '', '',
+ 'paid', @money_type,
+ '_date', @date_type,
+ 'payby', 'char', '', 4, # CARD/BILL/COMP, should be index into
+ # payment type table.
+ 'payinfo', 'varchar', 'NULL', $char_d, #see cust_main above
+ 'paybatch', 'varchar', 'NULL', $char_d, #for auditing purposes.
+ 'closed', 'char', 'NULL', 1,
+ 'void_date', @date_type,
+ 'reason', 'varchar', 'NULL', $char_d,
+ 'otaker', 'varchar', '', 32,
+ ],
+ 'primary_key' => 'paynum',
+ 'unique' => [],
+ 'index' => [ [ 'custnum' ] ],
+ },
+
+ 'cust_bill_pay' => {
+ 'columns' => [
+ 'billpaynum', 'serial', '', '',
+ 'invnum', 'int', '', '',
+ 'paynum', 'int', '', '',
+ 'amount', @money_type,
+ '_date', @date_type
+ ],
+ 'primary_key' => 'billpaynum',
+ 'unique' => [],
+ 'index' => [ [ 'paynum' ], [ 'invnum' ] ],
+ },
+
+ 'cust_pay_batch' => { #what's this used for again? list of customers
+ #in current CARD batch? (necessarily CARD?)
+ 'columns' => [
+ 'paybatchnum', 'serial', '', '',
+ 'invnum', 'int', '', '',
+ 'custnum', 'int', '', '',
+ 'last', 'varchar', '', $char_d,
+ 'first', 'varchar', '', $char_d,
+ 'address1', 'varchar', '', $char_d,
+ 'address2', 'varchar', 'NULL', $char_d,
+ 'city', 'varchar', '', $char_d,
+ 'state', 'varchar', 'NULL', $char_d,
+ 'zip', 'varchar', 'NULL', 10,
+ 'country', 'char', '', 2,
+# 'trancode', 'int', '', '',
+ 'cardnum', 'varchar', '', 16,
+ #'exp', @date_type,
+ 'exp', 'varchar', '', 11,
+ 'payname', 'varchar', 'NULL', $char_d,
+ 'amount', @money_type,
+ ],
+ 'primary_key' => 'paybatchnum',
+ 'unique' => [],
+ 'index' => [ ['invnum'], ['custnum'] ],
+ },
+
+ 'cust_pkg' => {
+ 'columns' => [
+ 'pkgnum', 'serial', '', '',
+ 'custnum', 'int', '', '',
+ 'pkgpart', 'int', '', '',
+ 'otaker', 'varchar', '', 32,
+ 'setup', @date_type,
+ 'bill', @date_type,
+ 'last_bill', @date_type,
+ 'susp', @date_type,
+ 'cancel', @date_type,
+ 'expire', @date_type,
+ 'manual_flag', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'pkgnum',
+ 'unique' => [],
+ 'index' => [ ['custnum'] ],
+ },
+
+ 'cust_refund' => {
+ 'columns' => [
+ 'refundnum', 'serial', '', '',
+ #now cust_credit_refund #'crednum', 'int', '', '',
+ 'custnum', 'int', '', '',
+ '_date', @date_type,
+ 'refund', @money_type,
+ 'otaker', 'varchar', '', 32,
+ 'reason', 'varchar', '', $char_d,
+ 'payby', 'char', '', 4, # CARD/BILL/COMP, should be index
+ # into payment type table.
+ 'payinfo', 'varchar', 'NULL', $char_d, #see cust_main above
+ 'paybatch', 'varchar', 'NULL', $char_d,
+ 'closed', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'refundnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'cust_credit_refund' => {
+ 'columns' => [
+ 'creditrefundnum', 'serial', '', '',
+ 'crednum', 'int', '', '',
+ 'refundnum', 'int', '', '',
+ 'amount', @money_type,
+ '_date', @date_type
+ ],
+ 'primary_key' => 'creditrefundnum',
+ 'unique' => [],
+ 'index' => [ [ 'crednum', 'refundnum' ] ],
+ },
+
+
+ 'cust_svc' => {
+ 'columns' => [
+ 'svcnum', 'serial', '', '',
+ 'pkgnum', 'int', 'NULL', '',
+ 'svcpart', 'int', '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [ ['svcnum'], ['pkgnum'], ['svcpart'] ],
+ },
+
+ 'part_pkg' => {
+ 'columns' => [
+ 'pkgpart', 'serial', '', '',
+ 'pkg', 'varchar', '', $char_d,
+ 'comment', 'varchar', '', $char_d,
+ 'promo_code', 'varchar', 'NULL', $char_d,
+ 'setup', @perl_type,
+ 'freq', 'varchar', '', $char_d, #billing frequency
+ 'recur', @perl_type,
+ 'setuptax', 'char', 'NULL', 1,
+ 'recurtax', 'char', 'NULL', 1,
+ 'plan', 'varchar', 'NULL', $char_d,
+ 'plandata', 'text', 'NULL', '',
+ 'disabled', 'char', 'NULL', 1,
+ 'taxclass', 'varchar', 'NULL', $char_d,
+ ],
+ 'primary_key' => 'pkgpart',
+ 'unique' => [],
+ 'index' => [ [ 'promo_code' ], [ 'disabled' ] ],
+ },
+
+# 'part_title' => {
+# 'columns' => [
+# 'titlenum', 'int', '', '',
+# 'title', 'varchar', '', $char_d,
+# ],
+# 'primary_key' => 'titlenum',
+# 'unique' => [ [] ],
+# 'index' => [ [] ],
+# },
+
+ 'pkg_svc' => {
+ 'columns' => [
+ 'pkgsvcnum', 'serial', '', '',
+ 'pkgpart', 'int', '', '',
+ 'svcpart', 'int', '', '',
+ 'quantity', 'int', '', '',
+ 'primary_svc','char', 'NULL', 1,
+ ],
+ 'primary_key' => 'pkgsvcnum',
+ 'unique' => [ ['pkgpart', 'svcpart'] ],
+ 'index' => [ ['pkgpart'] ],
+ },
+
+ 'part_referral' => {
+ 'columns' => [
+ 'refnum', 'serial', '', '',
+ 'referral', 'varchar', '', $char_d,
+ 'disabled', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'refnum',
+ 'unique' => [],
+ 'index' => [ ['disabled'] ],
+ },
+
+ 'part_svc' => {
+ 'columns' => [
+ 'svcpart', 'serial', '', '',
+ 'svc', 'varchar', '', $char_d,
+ 'svcdb', 'varchar', '', $char_d,
+ 'disabled', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'svcpart',
+ 'unique' => [],
+ 'index' => [ [ 'disabled' ] ],
+ },
+
+ 'part_svc_column' => {
+ 'columns' => [
+ 'columnnum', 'serial', '', '',
+ 'svcpart', 'int', '', '',
+ 'columnname', 'varchar', '', 64,
+ 'columnvalue', 'varchar', 'NULL', $char_d,
+ 'columnflag', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'columnnum',
+ 'unique' => [ [ 'svcpart', 'columnname' ] ],
+ 'index' => [ [ 'svcpart' ] ],
+ },
+
+ #(this should be renamed to part_pop)
+ 'svc_acct_pop' => {
+ 'columns' => [
+ 'popnum', 'serial', '', '',
+ 'city', 'varchar', '', $char_d,
+ 'state', 'varchar', '', $char_d,
+ 'ac', 'char', '', 3,
+ 'exch', 'char', '', 3,
+ 'loc', 'char', 'NULL', 4, #NULL for legacy purposes
+ ],
+ 'primary_key' => 'popnum',
+ 'unique' => [],
+ 'index' => [ [ 'state' ] ],
+ },
+
+ 'part_pop_local' => {
+ 'columns' => [
+ 'localnum', 'serial', '', '',
+ 'popnum', 'int', '', '',
+ 'city', 'varchar', 'NULL', $char_d,
+ 'state', 'char', 'NULL', 2,
+ 'npa', 'char', '', 3,
+ 'nxx', 'char', '', 3,
+ ],
+ 'primary_key' => 'localnum',
+ 'unique' => [],
+ 'index' => [ [ 'npa', 'nxx' ], [ 'popnum' ] ],
+ },
+
+ 'svc_acct' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '',
+ 'username', 'varchar', '', $username_len, #unique (& remove dup code)
+ '_password', 'varchar', '', 72, #13 for encryped pw's plus ' *SUSPENDED* (md5 passwords can be 34, blowfish 60)
+ 'sec_phrase', 'varchar', 'NULL', $char_d,
+ 'popnum', 'int', 'NULL', '',
+ 'uid', 'int', 'NULL', '',
+ 'gid', 'int', 'NULL', '',
+ 'finger', 'varchar', 'NULL', $char_d,
+ 'dir', 'varchar', 'NULL', $char_d,
+ 'shell', 'varchar', 'NULL', $char_d,
+ 'quota', 'varchar', 'NULL', $char_d,
+ 'slipip', 'varchar', 'NULL', 15, #four TINYINTs, bah.
+ 'seconds', 'int', 'NULL', '', #uhhhh
+ 'domsvc', 'int', '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ #'unique' => [ [ 'username', 'domsvc' ] ],
+ 'unique' => [],
+ 'index' => [ ['username'], ['domsvc'] ],
+ },
+
+ #'svc_charge' => {
+ # 'columns' => [
+ # 'svcnum', 'int', '', '',
+ # 'amount', @money_type,
+ # ],
+ # 'primary_key' => 'svcnum',
+ # 'unique' => [ [] ],
+ # 'index' => [ [] ],
+ #},
+
+ 'svc_domain' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '',
+ 'domain', 'varchar', '', $char_d,
+ 'catchall', 'int', 'NULL', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [ ['domain'] ],
+ 'index' => [],
+ },
+
+ 'domain_record' => {
+ 'columns' => [
+ 'recnum', 'serial', '', '',
+ 'svcnum', 'int', '', '',
+ #'reczone', 'varchar', '', $char_d,
+ 'reczone', 'varchar', '', 255,
+ 'recaf', 'char', '', 2,
+ 'rectype', 'varchar', '', 5,
+ #'recdata', 'varchar', '', $char_d,
+ 'recdata', 'varchar', '', 255,
+ ],
+ 'primary_key' => 'recnum',
+ 'unique' => [],
+ 'index' => [ ['svcnum'] ],
+ },
+
+ 'svc_forward' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '',
+ 'srcsvc', 'int', 'NULL', '',
+ 'src', 'varchar', 'NULL', 255,
+ 'dstsvc', 'int', 'NULL', '',
+ 'dst', 'varchar', 'NULL', 255,
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [ ['srcsvc'], ['dstsvc'] ],
+ },
+
+ 'svc_www' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '',
+ 'recnum', 'int', '', '',
+ 'usersvc', 'int', '', '',
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ #'svc_wo' => {
+ # 'columns' => [
+ # 'svcnum', 'int', '', '',
+ # 'svcnum', 'int', '', '',
+ # 'svcnum', 'int', '', '',
+ # 'worker', 'varchar', '', $char_d,
+ # '_date', @date_type,
+ # ],
+ # 'primary_key' => 'svcnum',
+ # 'unique' => [ [] ],
+ # 'index' => [ [] ],
+ #},
+
+ 'prepay_credit' => {
+ 'columns' => [
+ 'prepaynum', 'serial', '', '',
+ 'identifier', 'varchar', '', $char_d,
+ 'amount', @money_type,
+ 'seconds', 'int', 'NULL', '',
+ 'agentnum', 'int', 'NULL', '',
+ ],
+ 'primary_key' => 'prepaynum',
+ 'unique' => [ ['identifier'] ],
+ 'index' => [],
+ },
+
+ 'port' => {
+ 'columns' => [
+ 'portnum', 'serial', '', '',
+ 'ip', 'varchar', 'NULL', 15,
+ 'nasport', 'int', 'NULL', '',
+ 'nasnum', 'int', '', '',
+ ],
+ 'primary_key' => 'portnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'nas' => {
+ 'columns' => [
+ 'nasnum', 'serial', '', '',
+ 'nas', 'varchar', '', $char_d,
+ 'nasip', 'varchar', '', 15,
+ 'nasfqdn', 'varchar', '', $char_d,
+ 'last', 'int', '', '',
+ ],
+ 'primary_key' => 'nasnum',
+ 'unique' => [ [ 'nas' ], [ 'nasip' ] ],
+ 'index' => [ [ 'last' ] ],
+ },
+
+ 'session' => {
+ 'columns' => [
+ 'sessionnum', 'serial', '', '',
+ 'portnum', 'int', '', '',
+ 'svcnum', 'int', '', '',
+ 'login', @date_type,
+ 'logout', @date_type,
+ ],
+ 'primary_key' => 'sessionnum',
+ 'unique' => [],
+ 'index' => [ [ 'portnum' ] ],
+ },
+
+ 'queue' => {
+ 'columns' => [
+ 'jobnum', 'serial', '', '',
+ 'job', 'text', '', '',
+ '_date', 'int', '', '',
+ 'status', 'varchar', '', $char_d,
+ 'statustext', 'text', 'NULL', '',
+ 'svcnum', 'int', 'NULL', '',
+ ],
+ 'primary_key' => 'jobnum',
+ 'unique' => [],
+ 'index' => [ [ 'svcnum' ], [ 'status' ] ],
+ },
+
+ 'queue_arg' => {
+ 'columns' => [
+ 'argnum', 'serial', '', '',
+ 'jobnum', 'int', '', '',
+ 'arg', 'text', 'NULL', '',
+ ],
+ 'primary_key' => 'argnum',
+ 'unique' => [],
+ 'index' => [ [ 'jobnum' ] ],
+ },
+
+ 'queue_depend' => {
+ 'columns' => [
+ 'dependnum', 'serial', '', '',
+ 'jobnum', 'int', '', '',
+ 'depend_jobnum', 'int', '', '',
+ ],
+ 'primary_key' => 'dependnum',
+ 'unique' => [],
+ 'index' => [ [ 'jobnum' ], [ 'depend_jobnum' ] ],
+ },
+
+ 'export_svc' => {
+ 'columns' => [
+ 'exportsvcnum' => 'serial', '', '',
+ 'exportnum' => 'int', '', '',
+ 'svcpart' => 'int', '', '',
+ ],
+ 'primary_key' => 'exportsvcnum',
+ 'unique' => [ [ 'exportnum', 'svcpart' ] ],
+ 'index' => [ [ 'exportnum' ], [ 'svcpart' ] ],
+ },
+
+ 'part_export' => {
+ 'columns' => [
+ 'exportnum', 'serial', '', '',
+ #'svcpart', 'int', '', '',
+ 'machine', 'varchar', '', $char_d,
+ 'exporttype', 'varchar', '', $char_d,
+ 'nodomain', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'exportnum',
+ 'unique' => [],
+ 'index' => [ [ 'machine' ], [ 'exporttype' ] ],
+ },
+
+ 'part_export_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '',
+ 'exportnum', 'int', '', '',
+ 'optionname', 'varchar', '', $char_d,
+ 'optionvalue', 'text', 'NULL', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'exportnum' ], [ 'optionname' ] ],
+ },
+
+ 'radius_usergroup' => {
+ 'columns' => [
+ 'usergroupnum', 'serial', '', '',
+ 'svcnum', 'int', '', '',
+ 'groupname', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'usergroupnum',
+ 'unique' => [],
+ 'index' => [ [ 'svcnum' ], [ 'groupname' ] ],
+ },
+
+ 'msgcat' => {
+ 'columns' => [
+ 'msgnum', 'serial', '', '',
+ 'msgcode', 'varchar', '', $char_d,
+ 'locale', 'varchar', '', 16,
+ 'msg', 'text', '', '',
+ ],
+ 'primary_key' => 'msgnum',
+ 'unique' => [ [ 'msgcode', 'locale' ] ],
+ 'index' => [],
+ },
+
+ 'cust_tax_exempt' => {
+ 'columns' => [
+ 'exemptnum', 'serial', '', '',
+ 'custnum', 'int', '', '',
+ 'taxnum', 'int', '', '',
+ 'year', 'int', '', '',
+ 'month', 'int', '', '',
+ 'amount', @money_type,
+ ],
+ 'primary_key' => 'exemptnum',
+ 'unique' => [ [ 'custnum', 'taxnum', 'year', 'month' ] ],
+ 'index' => [],
+ },
+
+ 'router' => {
+ 'columns' => [
+ 'routernum', 'serial', '', '',
+ 'routername', 'varchar', '', $char_d,
+ 'svcnum', 'int', 'NULL', '',
+ ],
+ 'primary_key' => 'routernum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'part_svc_router' => {
+ 'columns' => [
+ 'svcrouternum', 'serial', '', '',
+ 'svcpart', 'int', '', '',
+ 'routernum', 'int', '', '',
+ ],
+ 'primary_key' => 'svcrouternum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'addr_block' => {
+ 'columns' => [
+ 'blocknum', 'serial', '', '',
+ 'routernum', 'int', '', '',
+ 'ip_gateway', 'varchar', '', 15,
+ 'ip_netmask', 'int', '', '',
+ ],
+ 'primary_key' => 'blocknum',
+ 'unique' => [ [ 'blocknum', 'routernum' ] ],
+ 'index' => [],
+ },
+
+ 'svc_broadband' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '',
+ 'blocknum', 'int', '', '',
+ 'speed_up', 'int', '', '',
+ 'speed_down', 'int', '', '',
+ 'ip_addr', 'varchar', '', 15,
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'part_virtual_field' => {
+ 'columns' => [
+ 'vfieldpart', 'int', '', '',
+ 'dbtable', 'varchar', '', 32,
+ 'name', 'varchar', '', 32,
+ 'check_block', 'text', 'NULL', '',
+ 'length', 'int', 'NULL', '',
+ 'list_source', 'text', 'NULL', '',
+ 'label', 'varchar', 'NULL', 80,
+ ],
+ 'primary_key' => 'vfieldpart',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'virtual_field' => {
+ 'columns' => [
+ 'vfieldnum', 'serial', '', '',
+ 'recnum', 'int', '', '',
+ 'vfieldpart', 'int', '', '',
+ 'value', 'varchar', '', 128,
+ ],
+ 'primary_key' => 'vfieldnum',
+ 'unique' => [ [ 'vfieldpart', 'recnum' ] ],
+ 'index' => [],
+ },
+
+ 'acct_snarf' => {
+ 'columns' => [
+ 'snarfnum', 'int', '', '',
+ 'svcnum', 'int', '', '',
+ 'machine', 'varchar', '', 255,
+ 'protocol', 'varchar', '', $char_d,
+ 'username', 'varchar', '', $char_d,
+ '_password', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'snarfnum',
+ 'unique' => [],
+ 'index' => [ [ 'svcnum' ] ],
+ },
+
+ 'svc_external' => {
+ 'columns' => [
+ 'svcnum', 'int', '', '',
+ 'id', 'int', 'NULL', '',
+ 'title', 'varchar', 'NULL', $char_d,
+ ],
+ 'primary_key' => 'svcnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'cust_pay_refund' => {
+ 'columns' => [
+ 'payrefundnum', 'serial', '', '',
+ 'paynum', 'int', '', '',
+ 'refundnum', 'int', '', '',
+ '_date', @date_type,
+ 'amount', @money_type,
+ ],
+ 'primary_key' => 'payrefundnum',
+ 'unique' => [],
+ 'index' => [ ['paynum'], ['refundnum'] ],
+ },
+
+ 'part_pkg_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '',
+ 'pkgpart', 'int', '', '',
+ 'optionname', 'varchar', '', $char_d,
+ 'optionvalue', 'text', 'NULL', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'pkgpart' ], [ 'optionname' ] ],
+ },
+
+ 'rate' => {
+ 'columns' => [
+ 'ratenum', 'serial', '', '',
+ 'ratename', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'ratenum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'rate_detail' => {
+ 'columns' => [
+ 'ratedetailnum', 'serial', '', '',
+ 'ratenum', 'int', '', '',
+ 'orig_regionnum', 'int', 'NULL', '',
+ 'dest_regionnum', 'int', '', '',
+ 'min_included', 'int', '', '',
+ 'min_charge', @money_type,
+ 'sec_granularity', 'int', '', '',
+ #time period (link to table of periods)?
+ ],
+ 'primary_key' => 'ratedetailnum',
+ 'unique' => [ [ 'ratenum', 'orig_regionnum', 'dest_regionnum' ] ],
+ 'index' => [],
+ },
+
+ 'rate_region' => {
+ 'columns' => [
+ 'regionnum', 'serial', '', '',
+ 'regionname', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'regionnum',
+ 'unique' => [],
+ 'index' => [],
+ },
+
+ 'rate_prefix' => {
+ 'columns' => [
+ 'prefixnum', 'serial', '', '',
+ 'regionnum', 'int', '', '',,
+ 'countrycode', 'varchar', '', 3,
+ 'npa', 'varchar', 'NULL', 6,
+ 'nxx', 'varchar', 'NULL', 3,
+ ],
+ 'primary_key' => 'prefixnum',
+ 'unique' => [],
+ 'index' => [ [ 'countrycode' ], [ 'regionnum' ] ],
+ },
+
+ 'reg_code' => {
+ 'columns' => [
+ 'codenum', 'serial', '', '',
+ 'code', 'varchar', '', $char_d,
+ 'agentnum', 'int', '', '',
+ ],
+ 'primary_key' => 'codenum',
+ 'unique' => [ [ 'agentnum', 'code' ] ],
+ 'index' => [ [ 'agentnum' ] ],
+ },
+
+ 'reg_code_pkg' => {
+ 'columns' => [
+ 'codepkgnum', 'serial', '', '',
+ 'codenum', 'int', '', '',
+ 'pkgpart', 'int', '', '',
+ ],
+ 'primary_key' => 'codepkgnum',
+ 'unique' => [ [ 'codenum', 'pkgpart' ] ],
+ 'index' => [ [ 'codenum' ] ],
+ },
+
+ 'clientapi_session' => {
+ 'columns' => [
+ 'sessionnum', 'serial', '', '',
+ 'sessionid', 'varchar', '', $char_d,
+ 'namespace', 'varchar', '', $char_d,
+ ],
+ 'primary_key' => 'sessionnum',
+ 'unique' => [ [ 'sessionid', 'namespace' ] ],
+ 'index' => [],
+ },
+
+ 'clientapi_session_field' => {
+ 'columns' => [
+ 'fieldnum', 'serial', '', '',
+ 'sessionnum', 'int', '', '',
+ 'fieldname', 'varchar', '', $char_d,
+ 'fieldvalue', 'text', 'NULL', '',
+ ],
+ 'primary_key' => 'fieldnum',
+ 'unique' => [ [ 'sessionnum', 'fieldname' ] ],
+ 'index' => [],
+ },
+
+ 'payment_gateway' => {
+ 'columns' => [
+ 'gatewaynum', 'serial', '', '',
+ 'gateway_module', 'varchar', '', $char_d,
+ 'gateway_username', 'varchar', 'NULL', $char_d,
+ 'gateway_password', 'varchar', 'NULL', $char_d,
+ 'gateway_action', 'varchar', 'NULL', $char_d,
+ 'disabled', 'char', 'NULL', 1,
+ ],
+ 'primary_key' => 'gatewaynum',
+ 'unique' => [],
+ 'index' => [ [ 'disabled' ] ],
+ },
+
+ 'payment_gateway_option' => {
+ 'columns' => [
+ 'optionnum', 'serial', '', '',
+ 'gatewaynum', 'int', '', '',
+ 'optionname', 'varchar', '', $char_d,
+ 'optionvalue', 'text', 'NULL', '',
+ ],
+ 'primary_key' => 'optionnum',
+ 'unique' => [],
+ 'index' => [ [ 'gatewaynum' ], [ 'optionname' ] ],
+ },
+
+ 'agent_payment_gateway' => {
+ 'columns' => [
+ 'agentgatewaynum', 'serial', '', '',
+ 'agentnum', 'int', '', '',
+ 'gatewaynum', 'int', '', '',
+ 'cardtype', 'varchar', 'NULL', $char_d,
+ 'taxclass', 'varchar', 'NULL', $char_d,
+ ],
+ 'primary_key' => 'agentgatewaynum',
+ 'unique' => [],
+ 'index' => [ [ 'agentnum', 'cardtype' ], ],
+ },
+
+ };
+
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<DBIx::DBSchema>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 5d68dddb1..a1df5602b 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -256,13 +256,18 @@ sub paymask {
return $paymask;
}
+=item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
+=item paystart_month - start date month (maestro/solo cards only)
+=item paystart_year - start date year (maestro/solo cards only)
-=item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
+=item payissue - issue number (maestro/solo cards only)
=item payname - name on card or billing name
+=item payip - IP address from which payment information was received
+
=item tax - tax exempt, empty or `Y'
=item otaker - order taker (assigned automatically, see L<FS::UID>)
@@ -1099,6 +1104,19 @@ sub check {
$self->payby =~ /^(CARD|DCRD|CHEK|DCHK|LECB|BILL|COMP|PREPAY)$/
or return "Illegal payby: ". $self->payby;
+ $error = $self->ut_numbern('paystart_month')
+ || $self->ut_numbern('paystart_year')
+ || $self->ut_numbern('payissue')
+ ;
+ return $error if $error;
+
+ if ( $self->payip eq '' ) {
+ $self->payip('');
+ } else {
+ $error = $self->ut_ip('payip');
+ return $error if $error;
+ }
+
# If it is encrypted and the private key is not availaible then we can't
# check the credit card.
@@ -1110,7 +1128,7 @@ sub check {
$self->payby($1);
- if ( $check_payinfo && ($self->payby eq 'CARD' || $self->payby eq 'DCRD')) {
+ if ( $check_payinfo && $self->payby =~ /^(CARD|DCRD)$/ ) {
my $payinfo = $self->payinfo;
$payinfo =~ s/\D//g;
@@ -1138,7 +1156,31 @@ sub check {
}
}
- } elsif ($check_payinfo && ( $self->payby eq 'CHEK' || $self->payby eq 'DCHK' )) {
+ my $cardtype = cardtype($payinfo);
+ if ( $cardtype =~ /^(Switch|Solo)$/i ) {
+
+ return "Start date or issue number is required for $cardtype cards"
+ unless $self->paystart_month && $self->paystart_year or $self->payissue;
+
+ return "Start month must be between 1 and 12"
+ if $self->paystart_month
+ and $self->paystart_month < 1 || $self->paystart_month > 12;
+
+ return "Start year must be 1990 or later"
+ if $self->paystart_year
+ and $self->paystart_year < 1990;
+
+ return "Issue number must be beween 1 and 99"
+ if $self->payissue
+ and $self->payissue < 1 || $self->payissue > 99;
+
+ } else {
+ $self->paystart_month('');
+ $self->paystart_year('');
+ $self->payissue('');
+ }
+
+ } elsif ( $check_payinfo && $self->payby =~ /^(CHEK|DCHK)$/ ) {
my $payinfo = $self->payinfo;
$payinfo =~ s/[^\d\@]//g;
@@ -2036,25 +2078,77 @@ sub realtime_bop {
$options{'description'} ||= 'Internet services';
- #pre-requisites
- die "Real-time processing not enabled\n"
- unless $conf->exists('business-onlinepayment');
eval "use Business::OnlinePayment";
die $@ if $@;
- #load up config
- my $bop_config = 'business-onlinepayment';
- $bop_config .= '-ach'
- if $method eq 'ECHECK' && $conf->exists($bop_config. '-ach');
- my ( $processor, $login, $password, $action, @bop_options ) =
- $conf->config($bop_config);
- $action ||= 'normal authorization';
- pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/;
- die "No real-time processor is enabled - ".
- "did you set the business-onlinepayment configuration value?\n"
- unless $processor;
+ my $payinfo = exists($options{'payinfo'})
+ ? $options{'payinfo'}
+ : $self->payinfo;
- #massage data
+ ###
+ # select a gateway
+ ###
+
+ my $taxclass = ''; #XXX not yet
+
+ #look for an agent gateway override first
+ my $cardtype;
+ if ( $method eq 'CC' ) {
+ $cardtype = cardtype($payinfo);
+ } elsif ( $method eq 'ECHECK' ) {
+ $cardtype = 'ACH';
+ } else {
+ $cardtype = $method;
+ }
+
+ my $override =
+ qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => $cardtype,
+ taxclass => $taxclass, } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => '',
+ taxclass => $taxclass, } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => $cardtype,
+ taxclass => '', } )
+ || qsearchs('agent_payment_gateway', { agentnum => $self->agentnum,
+ cardtype => '',
+ taxclass => '', } );
+
+ my $payment_gateway = '';
+ my( $processor, $login, $password, $action, @bop_options );
+ if ( $override ) { #use a payment gateway override
+
+ $payment_gateway = $override->payment_gateway;
+
+ $processor = $payment_gateway->gateway_module;
+ $login = $payment_gateway->gateway_username;
+ $password = $payment_gateway->gateway_password;
+ $action = $payment_gateway->gateway_action;
+ @bop_options = $payment_gateway->options;
+
+ } else { #use the standard settings from the config
+
+ die "Real-time processing not enabled\n"
+ unless $conf->exists('business-onlinepayment');
+
+ #load up config
+ my $bop_config = 'business-onlinepayment';
+ $bop_config .= '-ach'
+ if $method eq 'ECHECK' && $conf->exists($bop_config. '-ach');
+ ( $processor, $login, $password, $action, @bop_options ) =
+ $conf->config($bop_config);
+ $action ||= 'normal authorization';
+ pop @bop_options if scalar(@bop_options) % 2 && $bop_options[-1] =~ /^\s*$/;
+ die "No real-time processor is enabled - ".
+ "did you set the business-onlinepayment configuration value?\n"
+ unless $processor;
+
+ }
+
+ ###
+ # massage data
+ ###
my $address = exists($options{'address1'})
? $options{'address1'}
@@ -2088,10 +2182,6 @@ sub realtime_bop {
? $conf->config('business-onlinepayment-email-override')
: $invoicing_list[0];
- my $payinfo = exists($options{'payinfo'})
- ? $options{'payinfo'}
- : $self->payinfo;
-
my %content = ();
if ( $method eq 'CC' ) {
@@ -2130,7 +2220,9 @@ sub realtime_bop {
$content{phone} = $payinfo;
}
- #transaction(s)
+ ###
+ # run transaction(s)
+ ###
my( $action1, $action2 ) = split(/\s*\,\s*/, $action );
@@ -2208,7 +2300,10 @@ sub realtime_bop {
}
- #remove paycvv after initial transaction
+ ###
+ # remove paycvv after initial transaction
+ ###
+
#false laziness w/misc/process/payment.cgi - check both to make sure working
# correctly
if ( defined $self->dbdef_table->column('paycvv')
@@ -2221,7 +2316,10 @@ sub realtime_bop {
}
}
- #result handling
+ ###
+ # result handling
+ ###
+
if ( $transaction->is_success() ) {
my %method2payby = (
@@ -2230,7 +2328,13 @@ sub realtime_bop {
'LEC' => 'LECB',
);
- my $paybatch = "$processor:". $transaction->authorization;
+ my $paybatch = '';
+ if ( $payment_gateway ) { # agent override
+ $paybatch = $payment_gateway->gatewaynum. '-';
+ }
+
+ $paybatch .= "$processor:". $transaction->authorization;
+
$paybatch .= ':'. $transaction->order_number
if $transaction->can('order_number')
&& length($transaction->order_number);
@@ -3278,17 +3382,10 @@ Returns an SQL expression identifying active cust_main records.
=cut
-my $recurring_sql = "
- '0' != ( select freq from part_pkg
- where cust_pkg.pkgpart = part_pkg.pkgpart )
-";
-
sub active_sql { "
0 < ( SELECT COUNT(*) FROM cust_pkg
WHERE cust_pkg.custnum = cust_main.custnum
- AND $recurring_sql
- AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
- AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
+ AND ". FS::cust_pkg->active_sql. "
)
"; }
@@ -3299,6 +3396,12 @@ Returns an SQL expression identifying suspended cust_main records.
=cut
+#my $recurring_sql = FS::cust_pkg->recurring_sql;
+my $recurring_sql = "
+ '0' != ( select freq from part_pkg
+ where cust_pkg.pkgpart = part_pkg.pkgpart )
+";
+
sub suspended_sql { susp_sql(@_); }
sub susp_sql { "
0 < ( SELECT COUNT(*) FROM cust_pkg
@@ -3308,9 +3411,7 @@ sub susp_sql { "
)
AND 0 = ( SELECT COUNT(*) FROM cust_pkg
WHERE cust_pkg.custnum = cust_main.custnum
- AND $recurring_sql
- AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
- AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ AND ". FS::cust_pkg->active_sql. "
)
"; }
diff --git a/FS/FS/option_Common.pm b/FS/FS/option_Common.pm
new file mode 100644
index 000000000..c57e55b61
--- /dev/null
+++ b/FS/FS/option_Common.pm
@@ -0,0 +1,295 @@
+package FS::option_Common;
+
+use strict;
+use vars qw( @ISA $DEBUG );
+use FS::Record qw( qsearch qsearchs dbh );
+
+@ISA = qw( FS::Record );
+
+$DEBUG = 0;
+
+=head1 NAME
+
+FS::option_Common - Base class for option sub-classes
+
+=head1 SYNOPSIS
+
+use FS::option_Common;
+
+@ISA = qw( FS::option_Common );
+
+=head1 DESCRIPTION
+
+FS::option_Common is intended as a base class for classes which have a
+simple one-to-many class associated with them, used to store a hash-like data
+structure of keys and values.
+
+=head1 METHODS
+
+=over 4
+
+=item insert [ HASHREF | OPTION => VALUE ... ]
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+If a list or hash reference of options is supplied, option records are also
+created.
+
+=cut
+
+#false laziness w/queue.pm
+sub insert {
+ my $self = shift;
+ my $options =
+ ( ref($_[0]) eq 'HASH' )
+ ? shift
+ : { @_ };
+ warn "FS::option_Common::insert called on $self with options ".
+ join(', ', map "$_ => ".$options->{$_}, keys %$options)
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ 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 $error = $self->SUPER::insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkey = $self->pkey;
+ my $option_table = $self->option_table;
+
+ foreach my $optionname ( keys %{$options} ) {
+ my $href = {
+ $pkey => $self->get($pkey),
+ 'optionname' => $optionname,
+ 'optionvalue' => $options->{$optionname},
+ };
+
+ #my $option_record = eval "new FS::$option_table \$href";
+ #if ( $@ ) {
+ # $dbh->rollback if $oldAutoCommit;
+ # return $@;
+ #}
+ my $option_record = $option_table->new($href);
+
+ $error = $option_record->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item delete
+
+Delete this record from the database. Any associated option records are also
+deleted.
+
+=cut
+
+#foreign keys would make this much less tedious... grr dumb mysql
+sub delete {
+ my $self = shift;
+ local $SIG{HUP} = 'IGNORE';
+ 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 $error = $self->SUPER::delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkey = $self->pkey;
+ my $option_table = $self->option_table;
+
+ foreach my $obj ( $self->option_objects ) {
+ my $error = $obj->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item replace [ HASHREF | OPTION => VALUE ... ]
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+If a list hash reference of options is supplied, part_export_option records are
+created or modified (see L<FS::part_export_option>).
+
+=cut
+
+sub replace {
+ my $self = shift;
+ my $old = shift;
+ my $options =
+ ( ref($_[0]) eq 'HASH' )
+ ? shift
+ : { @_ };
+ warn "FS::option_Common::insert called on $self with options ".
+ join(', ', map "$_ => ". $options->{$_}, keys %$options)
+ if $DEBUG;
+
+ local $SIG{HUP} = 'IGNORE';
+ 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 $error = $self->SUPER::replace($old);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkey = $self->pkey;
+ my $option_table = $self->option_table;
+
+ foreach my $optionname ( keys %{$options} ) {
+ my $old = qsearchs( $option_table, {
+ $pkey => $self->get($pkey),
+ 'optionname' => $optionname,
+ } );
+
+ my $href = {
+ $pkey => $self->get($pkey),
+ 'optionname' => $optionname,
+ 'optionvalue' => $options->{$optionname},
+ };
+
+ #my $new = eval "new FS::$option_table \$href";
+ #if ( $@ ) {
+ # $dbh->rollback if $oldAutoCommit;
+ # return $@;
+ #}
+ my $new = $option_table->new($href);
+
+ $new->optionnum($old->optionnum) if $old;
+ my $error = $old ? $new->replace($old) : $new->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ #remove extraneous old options
+ foreach my $opt (
+ grep { !exists $options->{$_->optionname} } $old->option_objects
+ ) {
+ my $error = $opt->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
+=item option_objects
+
+Returns all options as FS::I<tablename>_option objects.
+
+=cut
+
+sub option_objects {
+ my $self = shift;
+ my $pkey = $self->pkey;
+ my $option_table = $self->option_table;
+ qsearch($option_table, { $pkey => $self->get($pkey) } );
+}
+
+=item options
+
+Returns a list of option names and values suitable for assigning to a hash.
+
+=cut
+
+sub options {
+ my $self = shift;
+ map { $_->optionname => $_->optionvalue } $self->option_objects;
+}
+
+=item option OPTIONNAME
+
+Returns the option value for the given name, or the empty string.
+
+=cut
+
+sub option {
+ my $self = shift;
+ my $pkey = $self->pkey;
+ my $option_table = $self->option_table;
+ my $obj =
+ qsearchs($option_table, {
+ $pkey => $self->get($pkey),
+ optionname => shift,
+ } );
+ $obj ? $obj->optionvalue : '';
+}
+
+
+sub pkey {
+ my $self = shift;
+ my $pkey = $self->dbdef_table->primary_key;
+}
+
+sub option_table {
+ my $self = shift;
+ my $option_table = $self->table . '_option';
+ eval "use FS::$option_table";
+ die $@ if $@;
+ $option_table;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm
index 3f677b267..dce2d2a44 100644
--- a/FS/FS/part_export.pm
+++ b/FS/FS/part_export.pm
@@ -5,11 +5,12 @@ use vars qw( @ISA @EXPORT_OK $DEBUG %exports );
use Exporter;
use Tie::IxHash;
use FS::Record qw( qsearch qsearchs dbh );
+use FS::option_Common;
use FS::part_svc;
use FS::part_export_option;
use FS::export_svc;
-@ISA = qw(FS::Record);
+@ISA = qw( FS::option_Common );
@EXPORT_OK = qw(export_info);
$DEBUG = 0;
@@ -103,48 +104,6 @@ otherwise returns false.
If a hash reference of options is supplied, part_export_option records are
created (see L<FS::part_export_option>).
-=cut
-
-#false laziness w/queue.pm
-sub insert {
- my $self = shift;
- my $options = shift;
- local $SIG{HUP} = 'IGNORE';
- 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 $error = $self->SUPER::insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
-
- foreach my $optionname ( keys %{$options} ) {
- my $part_export_option = new FS::part_export_option ( {
- 'exportnum' => $self->exportnum,
- 'optionname' => $optionname,
- 'optionvalue' => $options->{$optionname},
- } );
- $error = $part_export_option->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
-
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-
- '';
-
-}
-
=item delete
Delete this record from the database.
@@ -171,14 +130,6 @@ sub delete {
return $error;
}
- foreach my $part_export_option ( $self->part_export_option ) {
- my $error = $part_export_option->delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
-
foreach my $export_svc ( $self->export_svc ) {
my $error = $export_svc->delete;
if ( $error ) {
@@ -193,72 +144,6 @@ sub delete {
}
-=item replace OLD_RECORD HASHREF
-
-Replaces the OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-If a hash reference of options is supplied, part_export_option records are
-created or modified (see L<FS::part_export_option>).
-
-=cut
-
-sub replace {
- my $self = shift;
- my $old = shift;
- my $options = shift;
- local $SIG{HUP} = 'IGNORE';
- 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 $error = $self->SUPER::replace($old);
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
-
- foreach my $optionname ( keys %{$options} ) {
- my $old = qsearchs( 'part_export_option', {
- 'exportnum' => $self->exportnum,
- 'optionname' => $optionname,
- } );
- my $new = new FS::part_export_option ( {
- 'exportnum' => $self->exportnum,
- 'optionname' => $optionname,
- 'optionvalue' => $options->{$optionname},
- } );
- $new->optionnum($old->optionnum) if $old;
- my $error = $old ? $new->replace($old) : $new->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
-
- #remove extraneous old options
- foreach my $opt (
- grep { !exists $options->{$_->optionname} } $old->part_export_option
- ) {
- my $error = $opt->delete;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
-
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
-
- '';
-
-};
-
=item check
Checks all fields to make sure this is a valid export. If there is
@@ -347,36 +232,17 @@ L<FS::part_export_option>).
sub part_export_option {
my $self = shift;
- qsearch('part_export_option', { 'exportnum' => $self->exportnum } );
+ $self->option_objects;
}
=item options
Returns a list of option names and values suitable for assigning to a hash.
-=cut
-
-sub options {
- my $self = shift;
- map { $_->optionname => $_->optionvalue } $self->part_export_option;
-}
-
=item option OPTIONNAME
Returns the option value for the given name, or the empty string.
-=cut
-
-sub option {
- my $self = shift;
- my $part_export_option =
- qsearchs('part_export_option', {
- exportnum => $self->exportnum,
- optionname => shift,
- } );
- $part_export_option ? $part_export_option->optionvalue : '';
-}
-
=item _rebless
Reblesses the object into the FS::part_export::EXPORTTYPE class, where
diff --git a/FS/FS/part_export_option.pm b/FS/FS/part_export_option.pm
index 33b5e5a67..e75940429 100644
--- a/FS/FS/part_export_option.pm
+++ b/FS/FS/part_export_option.pm
@@ -104,7 +104,7 @@ sub check {
my $error =
$self->ut_numbern('optionnum')
- || $self->ut_number('exportnum')
+ || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
|| $self->ut_alpha('optionname')
|| $self->ut_anything('optionvalue')
;
diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm
index fb08ddd91..73e469cf8 100644
--- a/FS/FS/part_pkg.pm
+++ b/FS/FS/part_pkg.pm
@@ -13,7 +13,9 @@ use FS::agent_type;
use FS::type_pkgs;
use FS::part_pkg_option;
-@ISA = qw( FS::Record );
+@ISA = qw( FS::Record ); # FS::option_Common ); # this can use option_Common
+ # when all the plandata bs is
+ # gone
$DEBUG = 0;
@@ -755,6 +757,8 @@ The delete method is unimplemented.
setup and recur semantics are not yet defined (and are implemented in
FS::cust_bill. hmm.).
+plandata should go
+
=head1 SEE ALSO
L<FS::Record>, L<FS::cust_pkg>, L<FS::type_pkgs>, L<FS::pkg_svc>, L<Safe>.
diff --git a/FS/FS/payment_gateway.pm b/FS/FS/payment_gateway.pm
new file mode 100644
index 000000000..a5cdd9d8d
--- /dev/null
+++ b/FS/FS/payment_gateway.pm
@@ -0,0 +1,147 @@
+package FS::payment_gateway;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+use FS::option_Common;
+
+@ISA = qw( FS::option_Common );
+
+=head1 NAME
+
+FS::payment_gateway - Object methods for payment_gateway records
+
+=head1 SYNOPSIS
+
+ use FS::payment_gateway;
+
+ $record = new FS::payment_gateway \%hash;
+ $record = new FS::payment_gateway { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::payment_gateway object represents an payment gateway.
+FS::payment_gateway inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item gatewaynum - primary key
+
+=item gateway_module - Business::OnlinePayment:: module name
+
+=item gateway_username - payment gateway username
+
+=item gateway_password - payment gateway password
+
+=item gateway_action - optional action or actions (multiple actions are separated with `,': for example: `Authorization Only, Post Authorization'). Defaults to `Normal Authorization'.
+
+=item disabled - Disabled flag, empty or 'Y'
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new payment gateway. To add the payment gateway 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 { 'payment_gateway'; }
+
+=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 payment gateway. 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('gatewaynum')
+ || $self->ut_alpha('gateway_module')
+ || $self->ut_textn('gateway_username')
+ || $self->ut_anything('gateway_password')
+ || $self->ut_enum('disabled', [ '', 'Y' ] )
+ #|| $self->ut_textn('gateway_action')
+ ;
+ return $error if $error;
+
+ if ( $self->gateway_action ) {
+ my @actions = split(/,\s*/, $self->gateway_action);
+ $self->gateway_action(
+ join( ',', map { /^(Normal Authorization|Authorization Only|Credit|Post Authorization)$/
+ or return "Unknown action $_";
+ $1
+ }
+ @actions
+ )
+ );
+ } else {
+ $self->gateway_action('Normal Authorization');
+ }
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/payment_gateway_option.pm b/FS/FS/payment_gateway_option.pm
new file mode 100644
index 000000000..057602291
--- /dev/null
+++ b/FS/FS/payment_gateway_option.pm
@@ -0,0 +1,126 @@
+package FS::payment_gateway_option;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record qw( qsearch qsearchs );
+
+@ISA = qw(FS::Record);
+
+=head1 NAME
+
+FS::payment_gateway_option - Object methods for payment_gateway_option records
+
+=head1 SYNOPSIS
+
+ use FS::payment_gateway_option;
+
+ $record = new FS::payment_gateway_option \%hash;
+ $record = new FS::payment_gateway_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::payment_gateway_option object represents an option key and value for
+a payment gateway. FS::payment_gateway_option inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item optionnum - primary key
+
+=item gatewaynum -
+
+=item optionname -
+
+=item optionvalue -
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new option. To add the option 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 { 'payment_gateway_option'; }
+
+=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 option. 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('optionnum')
+ || $self->ut_foreign_key('gatewaynum', 'payment_gateway', 'gatewaynum')
+ || $self->ut_text('optionname')
+ || $self->ut_textn('optionvalue')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+