From feef0e4c2b4bd6b776b25f5a1bd6fdbf63fd08b2 Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 17 Aug 2005 22:23:46 +0000 Subject: [PATCH] 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) --- FS/FS.pm | 4 +- FS/FS/Record.pm | 80 +- FS/FS/Schema.pm | 1247 ++++++++++++++++++++++++++++ FS/FS/cust_main.pm | 175 +++- FS/FS/option_Common.pm | 295 +++++++ FS/FS/part_export.pm | 140 +--- FS/FS/part_export_option.pm | 2 +- FS/FS/part_pkg.pm | 6 +- FS/FS/payment_gateway.pm | 147 ++++ FS/FS/payment_gateway_option.pm | 126 +++ FS/MANIFEST | 11 + FS/bin/freeside-setup | 1130 +------------------------ FS/bin/freeside-upgrade | 129 +++ FS/t/option_Common.t | 5 + FS/t/payment_gateway.t | 8 + FS/t/payment_gateway_option.t | 8 + README.1.5.8 | 6 + SCHEMA_CHANGE | 21 +- bin/generate-table-module | 87 ++ htetc/global.asa | 2 + htetc/handler.pl | 2 + httemplate/browse/agent.cgi | 25 + httemplate/browse/payment_gateway.html | 70 ++ httemplate/docs/upgrade10.html | 347 +------- httemplate/edit/agent_payment_gateway.html | 64 ++ httemplate/edit/payment_gateway.html | 108 +++ httemplate/index.html | 2 + 27 files changed, 2561 insertions(+), 1686 deletions(-) create mode 100644 FS/FS/Schema.pm create mode 100644 FS/FS/option_Common.pm create mode 100644 FS/FS/payment_gateway.pm create mode 100644 FS/FS/payment_gateway_option.pm create mode 100755 FS/bin/freeside-upgrade create mode 100644 FS/t/option_Common.t create mode 100644 FS/t/payment_gateway.t create mode 100644 FS/t/payment_gateway_option.t create mode 100644 README.1.5.8 create mode 100755 bin/generate-table-module create mode 100644 httemplate/browse/payment_gateway.html create mode 100644 httemplate/edit/agent_payment_gateway.html create mode 100644 httemplate/edit/payment_gateway.html diff --git a/FS/FS.pm b/FS/FS.pm index f5b2b96e4..f41245e22 100644 --- a/FS/FS.pm +++ b/FS/FS.pm @@ -237,11 +237,11 @@ The main documentation is in httemplate/docs. =head1 SUPPORT A mailing list for users is available. Send a blank message to - to subscribe. + to subscribe. A mailing list for developers is available. It is intended to be lower volume and higher SNR than the users list. Send a blank message to - to subscribe. + to subscribe. Commercial support is available; see . 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), 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. - -=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) 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), 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. + +=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 + +=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) @@ -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). + +=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_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 + +=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). -=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). - -=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). 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, L, L, L, L. 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 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, 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 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, schema.html from the base documentation. + +=cut + +1; + diff --git a/FS/MANIFEST b/FS/MANIFEST index fa2cef097..50bc7b5db 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -92,6 +92,8 @@ FS/part_export/http.pm FS/part_export/infostreet.pm FS/part_export/ldap.pm FS/part_export/null.pm +FS/part_export/radiator.pm +FS/part_export/router.pm FS/part_export/shellcommands.pm FS/part_export/shellcommands_withdomain.pm FS/part_export/sqlmail.pm @@ -221,6 +223,7 @@ t/part_export-ldap.t t/part_export-null.t t/part_export-passwdfile.t t/part_export-postfix.t +t/part_export-radiator.t t/part_export-router.t t/part_export-shellcommands.t t/part_export-shellcommands_withdomain.t @@ -276,3 +279,11 @@ t/msgcat.t t/raddb.t t/clientapi_session.t t/clientapi_session_field.t +FS/payment_gateway.pm +t/payment_gateway.t +FS/payment_gateway_option.pm +t/payment_gateway_option.t +FS/option_Common.pm +t/option_Common.t +FS/agent_payment_gateway.pm +t/agent_payment_gateway.t diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup index a2ef31eb1..14c5a84ab 100755 --- a/FS/bin/freeside-setup +++ b/FS/bin/freeside-setup @@ -1,29 +1,24 @@ #!/usr/bin/perl -Tw #to delay loading dbdef until we're ready -BEGIN { $FS::Record::setup_hack = 1; } +BEGIN { $FS::Schema::setup_hack = 1; } use strict; use vars qw($opt_s); use Getopt::Std; use Locale::Country; use Locale::SubCountry; -use DBI; -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(adminsuidsetup datasrc checkeuid getsecrets); +use FS::Schema qw( dbdef_dist ); use FS::Record; use FS::cust_main_county; -use FS::raddb; +#use FS::raddb; use FS::part_bill_event; die "Not running uid freeside!" unless checkeuid(); -my %attrib2db = - map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib; +#my %attrib2db = +# map { lc($FS::raddb::attrib{$_}) => $_ } keys %FS::raddb::attrib; getopts("s"); my $user = shift or die &usage; @@ -75,158 +70,25 @@ my $username_len = 32; #usernamemax config file # $x =~ /^y/i; #} -my @check_attributes = (); #add later -my @attributes = (); #add later -my $ship = $opt_s; - -### - -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' ); +#my @check_attributes = (); #add later +#my @attributes = (); #add later +#my $ship = $opt_s; ### # create a dbdef object from the old data structure ### -my(%tables)=&tables_hash_hack; - -#turn it into objects -my($dbdef) = new DBIx::DBSchema ( map { - my(@columns); - while (@{$tables{$_}{'columns'}}) { - my($name,$type,$null,$length)=splice @{$tables{$_}{'columns'}}, 0, 4; - push @columns, new DBIx::DBSchema::Column ( $name,$type,$null,$length ); - } - DBIx::DBSchema::Table->new( - $_, - $tables{$_}{'primary_key'}, - DBIx::DBSchema::ColGroup::Unique->new($tables{$_}{'unique'}), - DBIx::DBSchema::ColGroup::Index->new($tables{$_}{'index'}), - @columns, - ); -} (keys %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 ) - } ); - - $column->type('int') - if $column->type eq 'serial'; - #$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); -} +my $dbdef = dbdef_dist; #important $dbdef->save($dbdef_file); -&FS::Record::reload_dbdef($dbdef_file); +&FS::Schema::reload_dbdef($dbdef_file); ### # create 'em ### -my($dbh)=adminsuidsetup $user; +my $dbh = adminsuidsetup $user; #create tables $|=1; @@ -301,974 +163,8 @@ $dbh->disconnect or die $dbh->errstr; #print "Freeside database initialized sucessfully\n"; sub usage { - die "Usage:\n freeside-setup [ -s ] user\n"; + die "Usage:\n freeside-setup user\n"; } -### -# Now it becomes an object. much better. -### -sub tables_hash_hack { - - #note that s/(date|change)/_$1/; to avoid keyword conflict. - #put a kludge in FS::Record to catch this or? (pry need some date-handling - #stuff anyway also) - - my(%tables)=( #yech.} - - '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, - 'payname', 'varchar', 'NULL', $char_d, - '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' => [], - }, - - ); - - %tables; - -} +1; diff --git a/FS/bin/freeside-upgrade b/FS/bin/freeside-upgrade new file mode 100755 index 000000000..285ff7a0e --- /dev/null +++ b/FS/bin/freeside-upgrade @@ -0,0 +1,129 @@ +#!/usr/bin/perl -w + +use strict; +use vars qw($DEBUG $DRY_RUN); +use Term::ReadKey; +use DBIx::DBSchema 0.27; +use FS::UID qw(adminsuidsetup checkeuid datasrc ); #getsecrets); +use FS::Schema qw( dbdef dbdef_dist reload_dbdef ); + + +$DEBUG = 1; +$DRY_RUN = 0; + + +die "Not running uid freeside!" unless checkeuid(); + +my $user = shift or die &usage; +my $dbh = adminsuidsetup($user); + +#needs to match FS::Schema... +my $dbdef_file = "/usr/local/etc/freeside/dbdef.". datasrc; + +dbdef_create($dbh, $dbdef_file); +reload_dbdef($dbdef_file); + + +foreach my $table ( dbdef_dist->tables ) { + + if ( dbdef->table($table) ) { + + warn "$table exists\n" if $DEBUG > 1; + + foreach my $column ( dbdef_dist->table($table)->columns ) { + if ( dbdef->table($table)->column($column) ) { + warn " $table.$column exists\n" if $DEBUG > 2; + } else { + + if ( $DEBUG ) { + print STDERR "column $table.$column does not exist. create?"; + next unless yesno(); + } + + foreach my $statement ( + dbdef_dist->table($table)->column($column)->sql_add_column( $dbh ) + ) { + warn "$statement\n" if $DEBUG || $DRY_RUN; + unless ( $DRY_RUN ) { + $dbh->do( $statement) + or die "CREATE error: ". $dbh->errstr. "\nexecuting: $statement"; + } + } + + } + + } + + #should eventually check & create missing indices + + #should eventually drop columns not in dbdef_dist... + + } else { + + if ( $DEBUG ) { + print STDERR "table $table does not exist. create?"; + next unless yesno(); + } + + foreach my $statement ( + dbdef_dist->table($table)->sql_create_table( $dbh ) + ) { + warn "$statement\n" if $DEBUG || $DRY_RUN; + unless ( $DRY_RUN ) { + $dbh->do( $statement) + or die "CREATE error: ". $dbh->errstr. "\nexecuting: $statement"; + } + } + + } + +} + +# should eventually drop tables not in dbdef_dist too i guess... + +$dbh->commit or die $dbh->errstr; + +dbdef_create($dbh, $dbdef_file); + +$dbh->disconnect or die $dbh->errstr; + +### + +my $all = 0; +sub yesno { + print STDERR ' [yes/no/all] '; + if ( $all ) { + warn "yes\n"; + return 1; + } else { + while ( 1 ) { + ReadMode 4; + my $x = lc(ReadKey); + ReadMode 0; + if ( $x eq 'n' ) { + warn "no\n"; + return 0; + } elsif ( $x eq 'y' ) { + warn "yes\n"; + return 1; + } elsif ( $x eq 'a' ) { + warn "yes\n"; + $all = 1; + return 1; + } + } + } +} + +sub dbdef_create { # reverse engineer the schema from the DB and save to file + my( $dbh, $file ) = @_; + my $dbdef = new_native DBIx::DBSchema $dbh; + $dbdef->save($file); +} + +sub usage { + die "Usage:\n freeside-upgrade user\n"; +} + +1; + diff --git a/FS/t/option_Common.t b/FS/t/option_Common.t new file mode 100644 index 000000000..ad261415c --- /dev/null +++ b/FS/t/option_Common.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::option_Common; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/payment_gateway.t b/FS/t/payment_gateway.t new file mode 100644 index 000000000..b91d58244 --- /dev/null +++ b/FS/t/payment_gateway.t @@ -0,0 +1,8 @@ +BEGIN { $| = 1; print "1..1 +" } +END {print "not ok 1 +" unless $loaded;} +use FS::payment_gateway; +$loaded=1; +print "ok 1 +"; diff --git a/FS/t/payment_gateway_option.t b/FS/t/payment_gateway_option.t new file mode 100644 index 000000000..e52b6b487 --- /dev/null +++ b/FS/t/payment_gateway_option.t @@ -0,0 +1,8 @@ +BEGIN { $| = 1; print "1..1 +" } +END {print "not ok 1 +" unless $loaded;} +use FS::payment_gateway_option; +$loaded=1; +print "ok 1 +"; diff --git a/README.1.5.8 b/README.1.5.8 new file mode 100644 index 000000000..49c3bc0d5 --- /dev/null +++ b/README.1.5.8 @@ -0,0 +1,6 @@ + +install DBIx::DBSchema 0.27 + +install the new version +run "freeside-upgrade username" to uprade your database schema + diff --git a/SCHEMA_CHANGE b/SCHEMA_CHANGE index 063d01cb3..26ebeea76 100644 --- a/SCHEMA_CHANGE +++ b/SCHEMA_CHANGE @@ -1,7 +1,16 @@ -FS/bin/freeside-setup -httemplate/docs/upgrade10.html -README.1.5.X -httemplate/docs/schema.html -for new tables: edit FS/FS.pm, add a new FS/FS/table_name.pm - and FS/t/table_name.t, edit FS/MANIFEST +primarily: +- edit FS/FS/Schema.pm + +if the changes are something other than table and/or column additions: +- httemplate/docs/upgrade10.html +- README.1.5.X + +for new tables: +- run bin/generate-table-module tablename +- edit the resulting FS/FS/table.pm + +docs: +- sorta neglected: FS/FS.pm +- somehwat neglected: httemplate/docs/schema.html +- really neglected: httemplate/docs/schema.dia diff --git a/bin/generate-table-module b/bin/generate-table-module new file mode 100755 index 000000000..0baf23d09 --- /dev/null +++ b/bin/generate-table-module @@ -0,0 +1,87 @@ +#!/usr/bin/perl + +use FS::Schema qw( dbdef_dist ); + +my $table = shift; + +### +# add a new FS/FS/table.pm +### + +my %ut = ( #just guesses + 'int' => 'number', + 'number' => 'float', + 'varchar' => 'text', + 'text' => 'text', +); + +my $dbdef_table = dbdef_dist->table($table) + or die "define table in Schema.pm first"; +my $primary_key = $dbdef_table->primary_key; + +open(SRC,"FS/FS/$table.pm") or die $!; + +while (my $line = ) { + + $line =~ s/table_name/$table/g; + + if ( $line =~ /^=item\s+field\s+-\s+description\s*$/ ) { + + foreach my $column ( $dbdef_table->columns ) { + print DEST "=item $column - "; + print DEST "primary key" + if $column eq $primary_key; + print DEST "\n\n"; + } + next; + + } elsif ( $line=~ /^(\s*)\$self->ut_numbern\('primary_key'\)\s*/ ) { + + print DEST "$1\$self->ut_numbern('$primary_key')\n" + if $primary_key; + next; + + } elsif ( + $line =~ /^(\s*)\|\|\s+\$self->ut_number\('validate_other_fields'\)\s*/ + ) { + + foreach my $column ( grep { $_ ne $primary_key } $dbdef_table->columns ) { + my $ut = $ut{$dbdef_table->column($column)->type}; + $ut .= 'n' if $dbdef_table->column($column)->null; + print DEST "$1|| \$self->ut_$ut('$column')\n"; + } + next; + + } + + print DEST $line; +} + +close SRC; +close DEST; + +### +# add FS/t/table.t +### + +open(TEST,">FS/t/$table.t") or die $!; +print TEST <>FS/MANIFEST") or die $!; +print MANIFEST "FS/$table.pm\n", + "t/$table.t\n"; +close MANIFEST; + diff --git a/htetc/global.asa b/htetc/global.asa index 39ec898de..1979261ed 100644 --- a/htetc/global.asa +++ b/htetc/global.asa @@ -82,6 +82,8 @@ use FS::msgcat; use FS::rate; use FS::rate_region; use FS::rate_prefix; +use FS::payment_gateway; +use FS::agent_payment_gateway; sub Script_OnStart { $Response->AddHeader('Cache-control' => 'no-cache'); diff --git a/htetc/handler.pl b/htetc/handler.pl index ad671d6c7..397e4267a 100644 --- a/htetc/handler.pl +++ b/htetc/handler.pl @@ -165,6 +165,8 @@ sub handler use FS::rate; use FS::rate_region; use FS::rate_prefix; + use FS::payment_gateway; + use FS::agent_payment_gateway; use FS::XMLRPC; if ( %%%RT_ENABLED%%% ) { diff --git a/httemplate/browse/agent.cgi b/httemplate/browse/agent.cgi index fd360ab3c..e3c4022d4 100755 --- a/httemplate/browse/agent.cgi +++ b/httemplate/browse/agent.cgi @@ -36,6 +36,7 @@ full offerings (via their type).

Reports Registration codes Prepaid cards + Payment Gateway Overrides Freq. Prog. @@ -174,6 +175,30 @@ foreach my $agent ( sort {
Generate cards + + + <% foreach my $override ( + # sort { } want taxclass-full stuff first? and default cards (empty cardtype) + qsearch('agent_payment_gateway', { 'agentnum' => $agent->agentnum } ) + ) { + %> + + + + <% } %> + + + +
+ <%= $override->cardtype || 'Default' %> to <%= $override->payment_gateway->gateway_module %> (<%= $override->payment_gateway->gateway_username %>) + <%= $override->taxclass + ? ' for '. $override->taxclass. ' only' + : '' + %> + (delete) +
(add override)
+ + <%= $agent->freq %> <%= $agent->prog %> diff --git a/httemplate/browse/payment_gateway.html b/httemplate/browse/payment_gateway.html new file mode 100644 index 000000000..bb7f31514 --- /dev/null +++ b/httemplate/browse/payment_gateway.html @@ -0,0 +1,70 @@ +<% + + my %search; + if ( $cgi->param('showdisabled') ) { + %search = (); + } else { + %search = ( 'disabled' => '' ); + } + +%> +<%= header('Payment gateways', menubar( + 'Main Menu' => $p, + 'Agents' => $p. 'browse/agent.cgi', +)) %> + +Add a new payment gateway

+ +<%= $cgi->param('showdisabled') + ? do { $cgi->param('showdisabled', 0); + '( hide disabled gateways )'; } + : do { $cgi->param('showdisabled', 1); + '( show disabled gateways )'; } +%> + +<%= table() %> + + param('showdisabled') ? 1 : 2 %>># + Gateway + Username + Password + Action + Options + + +<% foreach my $payment_gateway ( qsearch( 'payment_gateway', \%search ) ) { %> + + + <%= $payment_gateway->gatewaynum %> + <% if ( !$cgi->param('showdisabled') ) { %> + <%= $payment_gateway->disabled ? 'DISABLED' : '' %> + <% } %> + <%= $payment_gateway->gateway_module %> + <%= !$payment_gateway->disabled + ? ' (disable)' + : '' + %> + + <%= $payment_gateway->gateway_username %> + - + <%= $payment_gateway->gateway_action %> + + + <% my %options = $payment_gateway->options; + foreach my $option ( keys %options ) { + %> + + + + + <% } %> +
<%= $option %>:<%= $options{$option} %>
+ + + +<% } %> + + + + + diff --git a/httemplate/docs/upgrade10.html b/httemplate/docs/upgrade10.html index 1faaedee0..e1eb76881 100644 --- a/httemplate/docs/upgrade10.html +++ b/httemplate/docs/upgrade10.html @@ -5,6 +5,8 @@ NOTE: Version numbering has been simplified. 1.5.7 is the version after 1.5.0pre6. It is still a development version - releases with odd numbered middle parts (NN in x.NN.x) are development versions, like Perl or Linux. +If migrating from 1.5.7, see README.1.5.8 instead + If migrating from 1.5.0pre6, see README.1.5.7 instead install DBD::Pg 1.32, 1.41 or later (not 1.40) (or, if you're using a Perl version before 5.6, you could try installing DBD::Pg 1.22 with this patch and commenting out the "use DBD::Pg 1.32" at the top of DBIx/DBSchema/DBD/Pg.pm) @@ -23,326 +25,15 @@ Spreadsheet::WriteExcel, IO-stringy (IO::Scalar), Frontier::RPC INSERT INTO msgcat ( msgnum, msgcode, locale, msg ) VALUES ( 20, 'svc_external-id', 'en_US', 'External ID' ); INSERT INTO msgcat ( msgnum, msgcode, locale, msg ) VALUES ( 21, 'svc_external-title', 'en_US', 'Title' ); -CREATE TABLE cust_bill_pkg_detail ( - detailnum serial, - pkgnum int NOT NULL, - invnum int NOT NULL, - detail varchar(80), - PRIMARY KEY (detailnum) -); -CREATE INDEX cust_bill_pkg_detail1 ON cust_bill_pkg_detail ( pkgnum, invnum ); - -CREATE TABLE part_virtual_field ( - vfieldpart int NOT NULL, - dbtable varchar(32) NOT NULL, - name varchar(32) NOT NULL, - check_block text, - list_source text, - length integer, - label varchar(80), - PRIMARY KEY (vfieldpart) -); - -CREATE TABLE virtual_field ( - recnum integer NOT NULL, - vfieldpart integer NOT NULL, - value varchar(128) NOT NULL, - PRIMARY KEY (vfieldpart, recnum) -); - -CREATE TABLE router ( - routernum serial, - routername varchar(80), - svcnum int, - PRIMARY KEY (routernum) -); - -CREATE TABLE part_svc_router ( - svcrouternum serial, - svcpart int NOT NULL, - routernum int NOT NULL, - PRIMARY KEY (svcrouternum) -); - -CREATE TABLE addr_block ( - blocknum serial, - routernum int NOT NULL, - ip_gateway varchar(15) NOT NULL, - ip_netmask int NOT NULL, - PRIMARY KEY (blocknum) -); -CREATE UNIQUE INDEX addr_block1 ON addr_block ( blocknum, routernum ); - -CREATE TABLE svc_broadband ( - svcnum int NOT NULL, - blocknum int NOT NULL, - speed_up int NOT NULL, - speed_down int NOT NULL, - ip_addr varchar(15), - PRIMARY KEY (svcnum) -); - -CREATE TABLE acct_snarf ( - snarfnum serial, - svcnum int NOT NULL, - machine varchar(255) NULL, - protocol varchar(80) NULL, - username varchar(80) NULL, - _password varchar(80) NULL, - PRIMARY KEY (snarfnum) -); -CREATE INDEX acct_snarf1 ON acct_snarf ( svcnum ); - -CREATE TABLE svc_external ( - svcnum int NOT NULL, - id int, - title varchar(80), - PRIMARY KEY (svcnum) -); - -ALTER TABLE part_pkg ADD COLUMN taxclass varchar(80) NULL; -ALTER TABLE h_part_pkg ADD COLUMN taxclass varchar(80) NULL; - -CREATE TABLE cust_pay_refund ( - payrefundnum serial NOT NULL, - paynum int NOT NULL, - refundnum int NOT NULL, - _date int NOT NULL, - amount decimal(10,2) NOT NULL, - PRIMARY KEY (payrefundnum) -); -CREATE INDEX cust_pay_refund1 ON cust_pay_refund(paynum); -CREATE INDEX cust_pay_refund2 ON cust_pay_refund(refundnum); - -CREATE TABLE cust_pay_void ( - paynum int NOT NULL, - custnum int NOT NULL, - paid decimal(10,2) NOT NULL, - _date int, - payby char(4) NOT NULL, - payinfo varchar(80), - paybatch varchar(80), - closed char(1), - void_date int, - reason varchar(80), - otaker varchar(32) NOT NULL, - PRIMARY KEY (paynum) -); -CREATE INDEX cust_pay_void1 ON cust_pay_void(custnum); - -CREATE TABLE part_pkg_option ( - optionnum serial, - pkgpart int not null, - optionname varchar(80) not null, - optionvalue text NULL, - PRIMARY KEY (optionnum) -); -CREATE INDEX part_pkg_option1 ON part_pkg_option ( pkgpart ); -CREATE INDEX part_pkg_option2 ON part_pkg_option ( optionname ); - -CREATE TABLE rate ( - ratenum serial NOT NULL, - ratename varchar(80) NOT NULL, - PRIMARY KEY (ratenum) -); - -CREATE TABLE rate_detail ( - ratedetailnum serial NOT NULL, - ratenum int NOT NULL, - orig_regionnum int NULL, - dest_regionnum int NOT NULL, - min_included int NOT NULL, - min_charge decimal(10,2) NOT NULL, - sec_granularity int NOT NULL, - PRIMARY KEY (ratedetailnum) -); -CREATE UNIQUE INDEX rate_detail1 ON rate_detail ( ratenum, orig_regionnum, dest_regionnum ); - -CREATE TABLE rate_region ( - regionnum serial NOT NULL, - regionname varchar(80) NOT NULL, - PRIMARY KEY (regionnum) -); - -CREATE TABLE rate_prefix ( - prefixnum serial NOT NULL, - regionnum int NOT NULL, - countrycode varchar(3) NOT NULL, - npa varchar(6) NULL, - nxx varchar(3) NULL, - PRIMARY KEY (prefixnum) -); -CREATE INDEX rate_prefix1 ON rate_prefix ( countrycode ); -CREATE INDEX rate_prefix2 ON rate_prefix ( regionnum ); - -CREATE TABLE reg_code ( - codenum serial NOT NULL, - code varchar(80) NOT NULL, - agentnum int NOT NULL, - PRIMARY KEY (codenum) -); -CREATE UNIQUE INDEX reg_code1 ON reg_code ( agentnum, code ); -CREATE INDEX reg_code2 ON reg_code ( agentnum ); - -CREATE TABLE reg_code_pkg ( - codepkgnum serial, - codenum int NOT NULL, - pkgpart int NOT NULL, - PRIMARY KEY (codepkgnum) -); -CREATE UNIQUE INDEX reg_code_pkg1 ON reg_code_pkg ( codenum, pkgpart ); -CREATE INDEX reg_code_pkg2 ON reg_code_pkg ( codenum ); - -CREATE TABLE clientapi_session ( - sessionnum serial NOT NULL, - sessionid varchar(80) NOT NULL, - namespace varchar(80) NOT NULL, - PRIMARY KEY (sessionnum) -); -CREATE UNIQUE INDEX clientapi_session1 ON clientapi_session ( sessionid, namespace ); - -CREATE TABLE clientapi_session_field ( - fieldnum serial NOT NULL, - sessionnum int NOT NULL, - fieldname varchar(80) NOT NULL, - fieldvalue text NULL, - PRIMARY KEY (fieldnum) -); -CREATE UNIQUE INDEX clientapi_session_field1 ON clientapi_session_field ( sessionnum, fieldname ); - DROP INDEX cust_bill_pkg1; -ALTER TABLE cust_bill_pkg ADD itemdesc varchar(80) NULL; -ALTER TABLE h_cust_bill_pkg ADD itemdesc varchar(80) NULL; -ALTER TABLE cust_main_county ADD taxname varchar(80) NULL; -ALTER TABLE h_cust_main_county ADD taxname varchar(80) NULL; -ALTER TABLE cust_main_county ADD setuptax char(1) NULL; -ALTER TABLE h_cust_main_county ADD setuptax char(1) NULL; -ALTER TABLE cust_main_county ADD recurtax char(1) NULL; -ALTER TABLE h_cust_main_county ADD recurtax char(1) NULL; -ALTER TABLE cust_pkg ADD last_bill int NULL; -ALTER TABLE h_cust_pkg ADD last_bill int NULL; -ALTER TABLE agent ADD disabled char(1) NULL; -ALTER TABLE h_agent ADD disabled char(1) NULL; -ALTER TABLE agent ADD username varchar(80) NULL; -ALTER TABLE h_agent ADD username varchar(80) NULL; -ALTER TABLE agent ADD _password varchar(80) NULL; -ALTER TABLE h_agent ADD _password varchar(80) NULL; -ALTER TABLE cust_main ADD paycvv varchar(512) NULL; -ALTER TABLE h_cust_main ADD paycvv varchar(512) NULL; -ALTER TABLE cust_main ADD paymask varchar(80) NULL; -ALTER TABLE h_cust_main ADD paymask varchar(80) NULL; -ALTER TABLE part_referral ADD disabled char(1) NULL; -ALTER TABLE h_part_referral ADD disabled char(1) NULL; -CREATE INDEX part_referral1 ON part_referral ( disabled ); -ALTER TABLE pkg_svc ADD primary_svc char(1) NULL; -ALTER TABLE h_pkg_svc ADD primary_svc char(1) NULL; -ALTER TABLE svc_forward ADD src varchar(255) NULL; -ALTER TABLE h_svc_forward ADD src varchar(255) NULL; -ALTER TABLE part_pkg ADD promo_code varchar(80) NULL; -ALTER TABLE h_part_pkg ADD promo_code varchar(80) NULL; -CREATE INDEX part_pkg2 ON part_pkg ( promo_code ); -CREATE INDEX h_part_pkg2 ON h_part_pkg ( promo_code ); -ALTER TABLE prepay_credit ADD agentnum integer NULL; -ALTER TABLE h_prepay_credit ADD agentnum integer NULL; - -ALTER TABLE type_pkgs ADD typepkgnum int; -ALTER TABLE type_pkgs ALTER COLUMN typepkgnum SET DEFAULT nextval('public.type_pkgs_typepkgnum_seq'::text); -CREATE SEQUENCE type_pkgs_typepkgnum_seq; -UPDATE type_pkgs SET typepkgnum = nextval('public.type_pkgs_typepkgnum_seq'::text) WHERE typepkgnum IS NULL; -ALTER TABLE type_pkgs ALTER typepkgnum SET NOT NULL; -ALTER TABLE type_pkgs ADD PRIMARY KEY (typepkgnum); -ALTER TABLE h_type_pkgs ADD typepkgnum int; - -ALTER TABLE cust_bill_pkg ADD billpkgnum int; -ALTER TABLE cust_bill_pkg ALTER COLUMN billpkgnum SET DEFAULT nextval('public.cust_bill_pkg_billpkgnum_seq'::text); -CREATE SEQUENCE cust_bill_pkg_billpkgnum_seq; -UPDATE cust_bill_pkg SET billpkgnum = nextval('public.cust_bill_pkg_billpkgnum_seq'::text) WHERE billpkgnum IS NULL; -ALTER TABLE cust_bill_pkg ALTER billpkgnum SET NOT NULL; -ALTER TABLE cust_bill_pkg ADD PRIMARY KEY (billpkgnum); -ALTER TABLE h_cust_bill_pkg ADD billpkgnum int; - -ALTER TABLE pkg_svc ADD pkgsvcnum int; -ALTER TABLE pkg_svc ALTER COLUMN pkgsvcnum SET DEFAULT nextval('public.pkg_svc_pkgsvcnum_seq'::text); -CREATE SEQUENCE pkg_svc_pkgsvcnum_seq; -UPDATE pkg_svc SET pkgsvcnum = nextval('public.pkg_svc_pkgsvcnum_seq'::text) WHERE pkgsvcnum IS NULL; -ALTER TABLE pkg_svc ALTER pkgsvcnum SET NOT NULL; -ALTER TABLE pkg_svc ADD PRIMARY KEY (pkgsvcnum); -ALTER TABLE h_pkg_svc ADD pkgsvcnum int; - On recent Pg versions: ALTER TABLE cust_main ALTER COLUMN payinfo varchar(512) NULL; ALTER TABLE h_cust_main ALTER COLUMN payinfo varchar(512) NULL; Or on older Pg versions that don't support altering columns directly: - +(write me) On recent Pg versions: @@ -368,25 +59,25 @@ If you created your database with a version before 1.4.2, dump database, edit: - domain_record and h_domain_record: increase recdata from 80 to 255 then reload -optionally: - - CREATE INDEX cust_main6 ON cust_main ( daytime ); - CREATE INDEX cust_main7 ON cust_main ( night ); - CREATE INDEX cust_main8 ON cust_main ( fax ); - CREATE INDEX cust_main9 ON cust_main ( ship_daytime ); - CREATE INDEX cust_main10 ON cust_main ( ship_night ); - CREATE INDEX cust_main11 ON cust_main ( ship_fax ); - CREATE INDEX agent2 ON agent ( disabled ); - CREATE INDEX part_bill_event2 ON part_bill_event ( disabled ); - CREATE INDEX cust_pay4 ON cust_pay (_date); +mandatory again: - serial columns +make install-perl-modules to install the new libraries and CLI utilities +run "freeside-upgrade username" to create the remaining new tables and columns -mandatory again: +optionally: -dbdef-create username -create-history-tables username cust_bill_pkg_detail router part_svc_router addr_block svc_broadband acct_snarf svc_external cust_pay_refund cust_pay_void part_pkg_option rate rate_detail rate_region rate_prefix reg_code reg_code_pkg -dbdef-create username +CREATE INDEX cust_main6 ON cust_main ( daytime ); +CREATE INDEX cust_main7 ON cust_main ( night ); +CREATE INDEX cust_main8 ON cust_main ( fax ); +CREATE INDEX cust_main9 ON cust_main ( ship_daytime ); +CREATE INDEX cust_main10 ON cust_main ( ship_night ); +CREATE INDEX cust_main11 ON cust_main ( ship_fax ); +CREATE INDEX agent2 ON agent ( disabled ); +CREATE INDEX part_bill_event2 ON part_bill_event ( disabled ); +CREATE INDEX cust_pay4 ON cust_pay (_date); +CREATE INDEX part_referral1 ON part_referral ( disabled ); +CREATE INDEX part_pkg2 ON part_pkg ( promo_code ); +CREATE INDEX h_part_pkg2 ON h_part_pkg ( promo_code ); apache - fix sections to include .html also diff --git a/httemplate/edit/agent_payment_gateway.html b/httemplate/edit/agent_payment_gateway.html new file mode 100644 index 000000000..61d29e0e9 --- /dev/null +++ b/httemplate/edit/agent_payment_gateway.html @@ -0,0 +1,64 @@ +<% + +$cgi->param('agentnum') =~ /(\d+)$/ or die "illegal agentnum"; +my $agent = qsearchs('agent', { 'agentnum' => $1 } ); +die "agentnum $1 not found" unless $agent; + +#my @agent_payment_gateway; +if ( $cgi->param('error') ) { +} + +my $action = 'Add'; + +%> + +<%= header("$action payment gateway override for ". $agent->agent, menubar( + 'Main Menu' => $p, + #'View all payment gateways' => $p. 'browse/payment_gateway.html', + 'View all agents' => $p. 'browse/agent.html', +)) %> + +<% if ( $cgi->param('error') ) { %> +Error: <%= $cgi->param('error') %> +<% } %> + +
+ + +Use gateway +

+ +for +

+ +(optional) when invoice contains only items of taxclass +

+ + +
+ + diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html new file mode 100644 index 000000000..837d0c408 --- /dev/null +++ b/httemplate/edit/payment_gateway.html @@ -0,0 +1,108 @@ +<% + +my $payment_gateway; +if ( $cgi->param('error') ) { + $payment_gateway = new FS::payment_gateway ( { + map { $_, scalar($cgi->param($_)) } fields('payment_gateway') + } ); +} elsif ( $cgi->keywords ) { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/; + $payment_gateway = qsearchs( 'payment_gateway', { 'gatewaynum' => $1 } ); +} else { #adding + $payment_gateway = new FS::payment_gateway {}; +} +my $action = $payment_gateway->gatewaynum ? 'Edit' : 'Add'; +#my $hashref = $payment_gateway->hashref; + +%> + +<%= header("$action Payment gateway", menubar( + 'Main Menu' => $p, + 'View all payment gateways' => $p. 'browse/payment_gateway.html', +)) %> + +<% if ( $cgi->param('error') ) { %> +Error: <%= $cgi->param('error') %> +<% } %> + +
+ +Gateway #<%= $payment_gateway->gatewaynum || "(NEW)" %> + +<%= ntable('#cccccc', 2, '') %> + + + Gateway: + + + + + + Username: + + + + + Password: + + + + + Action: + + + + + + + Options: + + + + + +
"> +
+ + + diff --git a/httemplate/index.html b/httemplate/index.html index 067fec5d1..82a028126 100644 --- a/httemplate/index.html +++ b/httemplate/index.html @@ -244,6 +244,8 @@ Billing