summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS.pm12
-rw-r--r--FS/FS/CGI.pm10
-rw-r--r--FS/FS/ClientAPI.pm4
-rw-r--r--FS/FS/ClientAPI/MyAccount.pm105
-rw-r--r--FS/FS/ClientAPI/passwd.pm9
-rw-r--r--FS/FS/Conf.pm25
-rw-r--r--FS/FS/InitHandler.pm5
-rw-r--r--FS/FS/Record.pm489
-rw-r--r--FS/FS/UID.pm4
-rwxr-xr-xFS/FS/addr_block.pm331
-rw-r--r--FS/FS/agent.pm5
-rw-r--r--FS/FS/agent_type.pm5
-rw-r--r--FS/FS/cust_bill.pm448
-rw-r--r--FS/FS/cust_bill_event.pm2
-rw-r--r--FS/FS/cust_bill_pay.pm2
-rw-r--r--FS/FS/cust_bill_pkg.pm72
-rw-r--r--FS/FS/cust_bill_pkg_detail.pm124
-rw-r--r--FS/FS/cust_credit.pm2
-rw-r--r--FS/FS/cust_credit_bill.pm2
-rw-r--r--FS/FS/cust_credit_refund.pm4
-rw-r--r--FS/FS/cust_main.pm327
-rw-r--r--FS/FS/cust_main_county.pm5
-rw-r--r--FS/FS/cust_main_invoice.pm11
-rw-r--r--FS/FS/cust_pay.pm53
-rw-r--r--FS/FS/cust_pay_batch.pm2
-rw-r--r--FS/FS/cust_pkg.pm337
-rw-r--r--FS/FS/cust_refund.pm4
-rw-r--r--FS/FS/cust_svc.pm13
-rw-r--r--FS/FS/cust_tax_exempt.pm1
-rw-r--r--FS/FS/domain_record.pm6
-rw-r--r--FS/FS/export_svc.pm1
-rw-r--r--FS/FS/msgcat.pm2
-rw-r--r--FS/FS/nas.pm6
-rw-r--r--FS/FS/part_bill_event.pm7
-rw-r--r--FS/FS/part_export.pm86
-rw-r--r--FS/FS/part_export/router.pm166
-rw-r--r--FS/FS/part_export_option.pm2
-rw-r--r--FS/FS/part_pkg.pm1
-rw-r--r--FS/FS/part_pop_local.pm3
-rw-r--r--FS/FS/part_referral.pm2
-rw-r--r--FS/FS/part_svc.pm45
-rw-r--r--FS/FS/part_svc_column.pm8
-rwxr-xr-xFS/FS/part_svc_router.pm32
-rwxr-xr-xFS/FS/part_virtual_field.pm303
-rw-r--r--FS/FS/pkg_svc.pm2
-rw-r--r--FS/FS/port.pm4
-rw-r--r--FS/FS/prepay_credit.pm1
-rw-r--r--FS/FS/queue.pm4
-rw-r--r--FS/FS/queue_arg.pm4
-rw-r--r--FS/FS/queue_depend.pm1
-rw-r--r--FS/FS/radius_usergroup.pm1
-rwxr-xr-xFS/FS/router.pm144
-rw-r--r--FS/FS/session.pm4
-rw-r--r--FS/FS/svc_Common.pm62
-rw-r--r--FS/FS/svc_acct.pm83
-rw-r--r--FS/FS/svc_acct_pop.pm3
-rw-r--r--FS/FS/svc_acct_sm.pm260
-rwxr-xr-xFS/FS/svc_broadband.pm235
-rw-r--r--FS/FS/svc_domain.pm13
-rw-r--r--FS/FS/svc_external.pm174
-rw-r--r--FS/FS/svc_forward.pm2
-rw-r--r--FS/FS/svc_www.pm3
-rw-r--r--FS/FS/type_pkgs.pm4
-rw-r--r--FS/MANIFEST12
-rw-r--r--FS/bin/freeside-adduser6
-rwxr-xr-xFS/bin/freeside-cc-receipts-report2
-rwxr-xr-xFS/bin/freeside-credit-report2
-rwxr-xr-xFS/bin/freeside-daily2
-rwxr-xr-xFS/bin/freeside-email6
-rwxr-xr-xFS/bin/freeside-expiration-alerter6
-rwxr-xr-xFS/bin/freeside-overdue196
-rwxr-xr-xFS/bin/freeside-setup292
-rwxr-xr-xFS/bin/freeside-tax-report2
-rw-r--r--FS/t/cust_bill_pkg_detail.t5
-rw-r--r--FS/t/svc_acct_sm.t (renamed from FS/t/svc_external.t)2
-rw-r--r--FS/t/svc_broadband.t5
76 files changed, 1558 insertions, 3067 deletions
diff --git a/FS/FS.pm b/FS/FS.pm
index 36c3a1777..963c73548 100644
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -54,8 +54,6 @@ L<FS::svc_Common> - Service base class
L<FS::svc_acct> - Account (shell, RADIUS, POP3) class
-L<FS::acct_snarf> - External mail account class
-
L<FS::radius_usergroup> - RADIUS groups
L<FS::svc_domain> - Domain class
@@ -64,11 +62,9 @@ L<FS::domain_record> - DNS zone entries
L<FS::svc_forward> - Mail forwarding class
-L<FS::svc_www> - Web virtual host class.
-
-L<FS::svc_broadband> - DSL, wireless and other broadband class.
+L<FS::svc_acct_sm> - (Depreciated) Vitual mail alias class
-L<FS::svc_external> - Externally tracked service class.
+L<FS::svc_www> - Web virtual host class.
L<FS::part_svc> - Service definition class
@@ -108,8 +104,6 @@ L<FS::cust_bill> - Invoice class
L<FS::cust_bill_pkg> - Invoice line item class
-L<FS::cust_bill_pkg_detail> - Invoice line item detail class
-
L<FS::part_bill_event> - Invoice event definition class
L<FS::cust_bill_event> - Completed invoice event class
@@ -193,7 +187,7 @@ first time, the suggested order will tend to reduce the number of forward
references."
If you've never used OO modules before,
-http://www.perl.com/doc/FMTEYEWTK/easy_objects.html might help you out.
+http://www.cpan.org/doc/FMTEYEWTK/easy_objects.html might help you out.
=head1 DESCRIPTION
diff --git a/FS/FS/CGI.pm b/FS/FS/CGI.pm
index f6153761b..86d20f6cb 100644
--- a/FS/FS/CGI.pm
+++ b/FS/FS/CGI.pm
@@ -44,10 +44,8 @@ Returns an HTML header.
=cut
sub header {
- use Carp;
- carp 'FS::CGI::header deprecated; include /elements/header.html instead';
-
my($title,$menubar,$etc)=@_; #$etc is for things like onLoad= etc.
+ #use Carp;
$etc = '' unless defined $etc;
my $x = <<END;
@@ -109,9 +107,6 @@ Returns an HTML menubar.
=cut
sub menubar { #$menubar=menubar('Main Menu', '../', 'Item', 'url', ... );
- use Carp;
- carp 'FS::CGI::menubar deprecated; include /elements/menubar.html instead';
-
my($item,$url,@html);
while (@_) {
($item,$url)=splice(@_,0,2);
@@ -230,9 +225,6 @@ Returns HTML tag for beginning a table.
=cut
sub table {
- use Carp;
- carp 'FS::CGI::table deprecated; include /elements/table.html instead';
-
my $col = shift;
if ( $col ) {
qq!<TABLE BGCOLOR="$col" BORDER=1 WIDTH="100%" CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">!;
diff --git a/FS/FS/ClientAPI.pm b/FS/FS/ClientAPI.pm
index 7cbbdbf67..f7b8eb028 100644
--- a/FS/FS/ClientAPI.pm
+++ b/FS/FS/ClientAPI.pm
@@ -1,13 +1,13 @@
package FS::ClientAPI;
use strict;
-use vars qw(%handler $domain);
+use vars qw(%handler);
%handler = ();
#find modules
foreach my $INC ( @INC ) {
- foreach my $file ( glob("$INC/FS/ClientAPI/*.pm") ) {
+ foreach my $file ( glob("$INC/FS/ClientAPI/*") ) {
$file =~ /\/(\w+)\.pm$/ or do {
warn "unrecognized ClientAPI file: $file";
next
diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm
index 445f0ece8..22f0d4adb 100644
--- a/FS/FS/ClientAPI/MyAccount.pm
+++ b/FS/FS/ClientAPI/MyAccount.pm
@@ -4,16 +4,14 @@ use strict;
use vars qw($cache);
use Digest::MD5 qw(md5_hex);
use Date::Format;
-use Business::CreditCard;
use Cache::SharedMemoryCache; #store in db?
use FS::CGI qw(small_custview); #doh
use FS::Conf;
-use FS::Record qw(qsearch qsearchs);
+use FS::Record qw(qsearchs);
use FS::svc_acct;
use FS::svc_domain;
use FS::cust_main;
use FS::cust_bill;
-use FS::cust_main_county;
use FS::cust_pkg;
use FS::ClientAPI; #hmm
@@ -23,8 +21,6 @@ FS::ClientAPI->register_handlers(
'MyAccount/edit_info' => \&edit_info,
'MyAccount/invoice' => \&invoice,
'MyAccount/cancel' => \&cancel,
- 'MyAccount/payment_info' => \&payment_info,
- 'MyAccount/process_payment' => \&process_payment,
'MyAccount/list_pkgs' => \&list_pkgs,
'MyAccount/order_pkg' => \&order_pkg,
'MyAccount/cancel_pkg' => \&cancel_pkg,
@@ -128,6 +124,7 @@ sub customer_info {
}
+
return { 'error' => '',
'custnum' => $custnum,
%return,
@@ -156,104 +153,6 @@ sub edit_info {
return { 'error' => '' };
}
-sub payment_info {
- my $p = shift;
- my $session = $cache->get($p->{'session_id'})
- or return { 'error' => "Can't resume session" }; #better error message
-
- my %return;
-
- my $custnum = $session->{'custnum'};
-
- my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
- or return { 'error' => "unknown custnum $custnum" };
-
- $return{balance} = $cust_main->balance;
-
- $return{payname} = $cust_main->payname
- || ( $cust_main->first. ' '. $cust_main->get('last') );
-
- $return{$_} = $cust_main->get($_) for qw(address1 address2 city state zip);
-
- $return{payby} = $cust_main->payby;
-
- if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
- warn $return{card_type} = cardtype($cust_main->payinfo);
- $return{payinfo} = $cust_main->payinfo;
-
- if ( $cust_main->paydate =~ /^(\d{4})-(\d{2})-\d{2}$/ ) { #Pg date format
- @return{'month', 'year'} = ( $2, $1 );
- } elsif ( $cust_main->paydate =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) {
- @return{'month', 'year'} = ( $1, $3 );
- }
-
- }
-
- #list all counties/states/countries
- $return{'cust_main_county'} =
- [ map { $_->hashref } qsearch('cust_main_county', {}) ],
-
- #shortcut for one-country folks
- my $conf = new FS::Conf;
- my %states = map { $_->state => 1 }
- qsearch('cust_main_county', {
- 'country' => $conf->config('defaultcountry') || 'US'
- } );
- $return{'states'} = [ sort { $a cmp $b } keys %states ];
-
- $return{card_types} = {
- 'VISA' => 'VISA card',
- 'MasterCard' => 'MasterCard',
- 'Discover' => 'Discover card',
- 'American Express' => 'American Express card',
- };
-
- my $_date = time;
- $return{paybatch} = "webui-MyAccount-$_date-$$-". rand() * 2**32;
-
- return { 'error' => '',
- %return,
- };
-
-};
-
-sub process_payment {
- my $p = shift;
-
- my $session = $cache->get($p->{'session_id'})
- or return { 'error' => "Can't resume session" }; #better error message
-
- my %return;
-
- my $custnum = $session->{'custnum'};
-
- my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
- or return { 'error' => "unknown custnum $custnum" };
-
- if ( $p->{'save'} ) {
- my $new = new FS::cust_main { $cust_main->hash };
- $new->set( $_ => $p->{$_} )
- foreach qw( payname address1 address2 city state zip payinfo );
- $new->set( 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01' );
- $new->set( 'payby' => $p->{'auto'} ? 'CARD' : 'DCRD' );
- my $error = $new->replace($cust_main);
- return { 'error' => $error } if $error;
- $cust_main = $new;
- }
-
- my $error = $cust_main->realtime_bop( 'CC', $p->{'amount'}, quiet=>1,
- 'paydate' => $p->{'year'}. '-'. $p->{'month'}. '-01',
- map { $_ => $p->{$_} }
- qw( payname address1 address2 city state zip payinfo paybatch )
- );
- return { 'error' => $error } if $error;
-
- $cust_main->apply_payments;
-
- return { 'error' => '' };
-
-}
-
sub invoice {
my $p = shift;
my $session = $cache->get($p->{'session_id'})
diff --git a/FS/FS/ClientAPI/passwd.pm b/FS/FS/ClientAPI/passwd.pm
index 016ebff79..29606227d 100644
--- a/FS/FS/ClientAPI/passwd.pm
+++ b/FS/FS/ClientAPI/passwd.pm
@@ -15,9 +15,8 @@ FS::ClientAPI->register_handlers(
sub passwd {
my $packet = shift;
- my $domain = $FS::ClientAPI::domain || $packet->{'domain'};
- my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } )
- or return { error => "Domain $domain not found" };
+ #my $domain = qsearchs('svc_domain', { 'domain' => $packet->{'domain'} } )
+ # or return { error => "Domain $domain not found" };
my $old_password = $packet->{'old_password'};
my $new_password = $packet->{'new_password'};
@@ -28,11 +27,11 @@ sub passwd {
my $svc_acct =
( length($old_password) < 13
&& qsearchs( 'svc_acct', { 'username' => $packet->{'username'},
- 'domsvc' => $svc_domain->svcnum,
+ #'domsvc' => $svc_domain->svcnum,
'_password' => $old_password } )
)
|| qsearchs( 'svc_acct', { 'username' => $packet->{'username'},
- 'domsvc' => $svc_domain->svcnum,
+ #'domsvc' => $svc_domain->svcnum,
'_password' => $old_password } );
unless ( $svc_acct ) { return { error => 'Incorrect password.' } }
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index ec1bf5574..709d1030b 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -309,6 +309,13 @@ httemplate/docs/config.html
},
{
+ 'key' => 'cybercash3.2',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, CyberCash no longer exists. Used to enable <a href="http://www.cybercash.com/cashregister/">CyberCash Cashregister v3.2</a> support. Two lines: the full path and name of your merchant_conf file, and the transaction type (`mauthonly\' or `mauthcapture\').',
+ 'type' => 'textarea',
+ },
+
+ {
'key' => 'cyrus',
'section' => 'deprecated',
'description' => '<b>DEPRECATED</b>, add a <i>cyrus</i> <a href="../browse/part_export.cgi">export</a> instead. This option used to integrate with <a href="http://asg.web.cmu.edu/cyrus/imapd/">Cyrus IMAP Server</a>, three lines: IMAP server, admin username, and admin password. Cyrus::IMAP::Admin should be installed locally and the connection to the server secured.',
@@ -365,6 +372,13 @@ httemplate/docs/config.html
},
{
+ 'key' => 'domain',
+ 'section' => 'deprecated',
+ 'description' => 'Your domain name.',
+ 'type' => 'text',
+ },
+
+ {
'key' => 'editreferrals',
'section' => 'UI',
'description' => 'Enable advertising source modification for existing customers',
@@ -393,13 +407,6 @@ httemplate/docs/config.html
},
{
- 'key' => 'exclude_ip_addr',
- 'section' => '',
- 'description' => 'Exclude these from the list of available broadband service IP addresses. (One per line)',
- 'type' => 'textarea',
- },
-
- {
'key' => 'erpcdmachines',
'section' => 'deprecated',
'description' => '<b>DEPRECATED</b>, ERPCD is no longer supported. Used to be ERPCD authenticaion machines, one per line. This enables export of `/usr/annex/acp_passwd\' and `/usr/annex/acp_dialup\'',
@@ -967,7 +974,7 @@ httemplate/docs/config.html
'section' => '',
'description' => 'Acceptable payment types for the signup server',
'type' => 'selectmultiple',
- 'select_enum' => [ qw(CARD DCRD CHEK DCHK LECB PREPAY BILL COMP) ],
+ 'select_enum' => [ qw(CARD CHEK LECB PREPAY BILL COMP) ],
},
{
@@ -1095,7 +1102,7 @@ httemplate/docs/config.html
'section' => 'UI',
'description' => 'Default payment type. HIDE disables display of billing information and sets customers to BILL.',
'type' => 'select',
- 'select_enum' => [ '', qw(CARD DCRD CHEK DCHK LECB BILL COMP HIDE) ],
+ 'select_enum' => [ '', qw(CARD CHEK LECB BILL COMP HIDE) ],
},
{
diff --git a/FS/FS/InitHandler.pm b/FS/FS/InitHandler.pm
index 5038cf352..87f507c22 100644
--- a/FS/FS/InitHandler.pm
+++ b/FS/FS/InitHandler.pm
@@ -1,9 +1,5 @@
package FS::InitHandler;
-# this leaks memory under graceful restarts and i wouldn't use it on any
-# modern server. useful for very slow machines with memory to spare, just
-# always do a full restart
-
use strict;
use vars qw($DEBUG);
use FS::UID qw(adminsuidsetup);
@@ -52,6 +48,7 @@ sub handler {
use FS::session;
use FS::svc_acct;
use FS::svc_acct_pop;
+ use FS::svc_acct_sm;
use FS::svc_domain;
use FS::svc_forward;
use FS::svc_www;
diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm
index b950e306b..98acaf522 100644
--- a/FS/FS/Record.pm
+++ b/FS/FS/Record.pm
@@ -2,22 +2,18 @@ package FS::Record;
use strict;
use vars qw( $dbdef_file $dbdef $setup_hack $AUTOLOAD @ISA @EXPORT_OK $DEBUG
- $me %dbdef_cache %virtual_fields_cache );
+ $me %dbdef_cache );
use subs qw(reload_dbdef);
use Exporter;
use Carp qw(carp cluck croak confess);
use File::CounterFile;
use Locale::Country;
use DBI qw(:sql_types);
-use DBIx::DBSchema 0.21;
-use FS::UID qw(dbh getotaker datasrc driver_name);
+use DBIx::DBSchema 0.19;
+use FS::UID qw(dbh checkruid getotaker datasrc driver_name);
use FS::SearchCache;
use FS::Msgcat qw(gettext);
-use FS::part_virtual_field;
-
-use Tie::IxHash;
-
@ISA = qw(Exporter);
@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch);
@@ -64,12 +60,14 @@ FS::Record - Database record objects
$hashref = $record->hashref;
$error = $record->insert;
+ #$error = $record->add; #deprecated
$error = $record->delete;
+ #$error = $record->del; #deprecated
$error = $new_record->replace($old_record);
+ #$error = $new_record->rep($old_record); #deprecated
- # external use deprecated - handled by the database (at least for Pg, mysql)
$value = $record->unique('column');
$error = $record->ut_float('column');
@@ -90,7 +88,7 @@ FS::Record - Database record objects
$quoted_value = _quote($value,'table','field');
- #deprecated
+ #depriciated
$fields = hfields('table');
if ( $fields->{Field} ) { # etc.
@@ -169,7 +167,7 @@ sub create {
my $self = {};
bless ($self, $class);
if ( defined $self->table ) {
- cluck "create constructor is deprecated, use new!";
+ cluck "create constructor is depriciated, use new!";
$self->new(@_);
} else {
croak "FS::Record::create called (not from a subclass)!";
@@ -204,21 +202,18 @@ sub qsearch {
my $dbh = dbh;
my $table = $cache ? $cache->table : $stable;
- my $pkey = $dbdef->table($table)->primary_key;
- my @real_fields = grep exists($record->{$_}), real_fields($table);
- my @virtual_fields = grep exists($record->{$_}), "FS::$table"->virtual_fields;
+ my @fields = grep exists($record->{$_}), fields($table);
my $statement = "SELECT $select FROM $stable";
- if ( @real_fields or @virtual_fields ) {
- $statement .= ' WHERE '. join(' AND ',
- ( map {
+ if ( @fields ) {
+ $statement .= ' WHERE '. join(' AND ', map {
my $op = '=';
my $column = $_;
if ( ref($record->{$_}) ) {
$op = $record->{$_}{'op'} if $record->{$_}{'op'};
- #$op = 'LIKE' if $op =~ /^ILIKE$/i && driver_name ne 'Pg';
+ #$op = 'LIKE' if $op =~ /^ILIKE$/i && driver_name !~ /^Pg$/i;
if ( uc($op) eq 'ILIKE' ) {
$op = 'LIKE';
$record->{$_}{'value'} = lc($record->{$_}{'value'});
@@ -260,45 +255,8 @@ sub qsearch {
} else {
"$column $op ?";
}
- } @real_fields ),
- ( map {
- my $op = '=';
- my $column = $_;
- if ( ref($record->{$_}) ) {
- $op = $record->{$_}{'op'} if $record->{$_}{'op'};
- if ( uc($op) eq 'ILIKE' ) {
- $op = 'LIKE';
- $record->{$_}{'value'} = lc($record->{$_}{'value'});
- $column = "LOWER($_)";
- }
- $record->{$_} = $record->{$_}{'value'};
- }
-
- # ... EXISTS ( SELECT name, value FROM part_virtual_field
- # JOIN virtual_field
- # ON part_virtual_field.vfieldpart = virtual_field.vfieldpart
- # WHERE recnum = svc_acct.svcnum
- # AND (name, value) = ('egad', 'brain') )
-
- my $value = $record->{$_};
-
- my $subq;
-
- $subq = ($value ? 'EXISTS ' : 'NOT EXISTS ') .
- "( SELECT part_virtual_field.name, virtual_field.value ".
- "FROM part_virtual_field JOIN virtual_field ".
- "ON part_virtual_field.vfieldpart = virtual_field.vfieldpart ".
- "WHERE virtual_field.recnum = ${table}.${pkey} ".
- "AND part_virtual_field.name = '${column}'".
- ($value ?
- " AND virtual_field.value ${op} '${value}'"
- : "") . ")";
- $subq;
-
- } @virtual_fields ) );
-
+ } @fields );
}
-
$statement .= " $extra_sql" if defined($extra_sql);
warn "[debug]$me $statement\n" if $DEBUG > 1;
@@ -308,7 +266,7 @@ sub qsearch {
my $bind = 1;
foreach my $field (
- grep defined( $record->{$_} ) && $record->{$_} ne '', @real_fields
+ grep defined( $record->{$_} ) && $record->{$_} ne '', @fields
) {
if ( $record->{$field} =~ /^\d+(\.\d+)?$/
&& $dbdef->table($table)->column($field)->type =~ /(int|serial)/i
@@ -325,64 +283,31 @@ sub qsearch {
$sth->execute or croak "Error executing \"$statement\": ". $sth->errstr;
- my %result;
- tie %result, "Tie::IxHash";
- @virtual_fields = "FS::$table"->virtual_fields;
+ $dbh->commit or croak $dbh->errstr if $FS::UID::AutoCommit;
- my @stuff = @{ $sth->fetchall_arrayref( {} ) };
- if($pkey) {
- %result = map { $_->{$pkey}, $_ } @stuff;
- } else {
- @result{@stuff} = @stuff;
- }
-
- $sth->finish;
- if ( keys(%result) and @virtual_fields ) {
- $statement =
- "SELECT virtual_field.recnum, part_virtual_field.name, ".
- "virtual_field.value ".
- "FROM part_virtual_field JOIN virtual_field USING (vfieldpart) ".
- "WHERE part_virtual_field.dbtable = '$table' AND ".
- "virtual_field.recnum IN (".
- join(',', keys(%result)). ") AND part_virtual_field.name IN ('".
- join(q!', '!, @virtual_fields) . "')";
- warn "[debug]$me $statement\n" if $DEBUG > 1;
- $sth = $dbh->prepare($statement) or croak "$dbh->errstr doing $statement";
- $sth->execute or croak "Error executing \"$statement\": ". $sth->errstr;
-
- foreach (@{ $sth->fetchall_arrayref({}) }) {
- my $recnum = $_->{recnum};
- my $name = $_->{name};
- my $value = $_->{value};
- if (exists($result{$recnum})) {
- $result{$recnum}->{$name} = $value;
- }
- }
- }
-
if ( eval 'scalar(@FS::'. $table. '::ISA);' ) {
if ( eval 'FS::'. $table. '->can(\'new\')' eq \&new ) {
#derivied class didn't override new method, so this optimization is safe
if ( $cache ) {
map {
new_or_cached( "FS::$table", { %{$_} }, $cache )
- } values(%result);
+ } @{$sth->fetchall_arrayref( {} )};
} else {
map {
new( "FS::$table", { %{$_} } )
- } values(%result);
+ } @{$sth->fetchall_arrayref( {} )};
}
} else {
warn "untested code (class FS::$table uses custom new method)";
map {
eval 'FS::'. $table. '->new( { %{$_} } )';
- } values(%result);
+ } @{$sth->fetchall_arrayref( {} )};
}
} else {
cluck "warning: FS::$table not loaded; returning FS::Record objects";
map {
FS::Record->new( $table, { %{$_} } );
- } values(%result);
+ } @{$sth->fetchall_arrayref( {} )};
}
}
@@ -438,7 +363,7 @@ Returns the table name.
=cut
sub table {
-# cluck "warning: FS::Record::table deprecated; supply one in subclass!";
+# cluck "warning: FS::Record::table depriciated; supply one in subclass!";
my $self = shift;
$self -> {'Table'};
}
@@ -565,41 +490,25 @@ sub insert {
return $error if $error;
#single-field unique keys are given a value if false
- #(like MySQL's AUTO_INCREMENT or Pg SERIAL)
+ #(like MySQL's AUTO_INCREMENT)
foreach ( $self->dbdef_table->unique->singles ) {
$self->unique($_) unless $self->getfield($_);
}
-
- #and also the primary key, if the database isn't going to
+ #and also the primary key
my $primary_key = $self->dbdef_table->primary_key;
- my $db_seq = 0;
- if ( $primary_key ) {
- my $col = $self->dbdef_table->column($primary_key);
-
- $db_seq =
- uc($col->type) eq 'SERIAL'
- || ( driver_name eq 'Pg'
- && defined($col->default)
- && $col->default =~ /^nextval\(/i
- )
- || ( driver_name eq 'mysql'
- && defined($col->local)
- && $col->local =~ /AUTO_INCREMENT/i
- );
- $self->unique($primary_key) unless $self->getfield($primary_key) || $db_seq;
- }
+ $self->unique($primary_key)
+ if $primary_key && ! $self->getfield($primary_key);
- my $table = $self->table;
#false laziness w/delete
- my @real_fields =
+ my @fields =
grep defined($self->getfield($_)) && $self->getfield($_) ne "",
- real_fields($table)
+ $self->fields
;
- my @values = map { _quote( $self->getfield($_), $table, $_) } @real_fields;
+ my @values = map { _quote( $self->getfield($_), $self->table, $_) } @fields;
#eslaf
- my $statement = "INSERT INTO $table ( ".
- join( ', ', @real_fields ).
+ my $statement = "INSERT INTO ". $self->table. " ( ".
+ join( ', ', @fields ).
") VALUES (".
join( ', ', @values ).
")"
@@ -607,6 +516,15 @@ sub insert {
warn "[debug]$me $statement\n" if $DEBUG > 1;
my $sth = dbh->prepare($statement) or return dbh->errstr;
+ my $h_sth;
+ if ( defined $dbdef->table('h_'. $self->table) ) {
+ my $h_statement = $self->_h_statement('insert');
+ warn "[debug]$me $h_statement\n" if $DEBUG > 2;
+ $h_sth = dbh->prepare($h_statement) or return dbh->errstr;
+ } else {
+ $h_sth = '';
+ }
+
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
@@ -615,92 +533,7 @@ sub insert {
local $SIG{PIPE} = 'IGNORE';
$sth->execute or return $sth->errstr;
-
- my $insertid = '';
- if ( $db_seq ) { # get inserted id from the database, if applicable
- warn "[debug]$me retreiving sequence from database\n" if $DEBUG;
- if ( driver_name eq 'Pg' ) {
-
- my $oid = $sth->{'pg_oid_status'};
- my $i_sql = "SELECT $primary_key FROM $table WHERE oid = ?";
- my $i_sth = dbh->prepare($i_sql) or do {
- dbh->rollback if $FS::UID::AutoCommit;
- return dbh->errstr;
- };
- $i_sth->execute($oid) or do {
- dbh->rollback if $FS::UID::AutoCommit;
- return $i_sth->errstr;
- };
- $insertid = $i_sth->fetchrow_arrayref->[0];
-
- } elsif ( driver_name eq 'mysql' ) {
-
- $insertid = dbh->{'mysql_insertid'};
- # work around mysql_insertid being null some of the time, ala RT :/
- unless ( $insertid ) {
- warn "WARNING: DBD::mysql didn't return mysql_insertid; ".
- "using SELECT LAST_INSERT_ID();";
- my $i_sql = "SELECT LAST_INSERT_ID()";
- my $i_sth = dbh->prepare($i_sql) or do {
- dbh->rollback if $FS::UID::AutoCommit;
- return dbh->errstr;
- };
- $i_sth->execute or do {
- dbh->rollback if $FS::UID::AutoCommit;
- return $i_sth->errstr;
- };
- $insertid = $i_sth->fetchrow_arrayref->[0];
- }
-
- } else {
- dbh->rollback if $FS::UID::AutoCommit;
- return "don't know how to retreive inserted ids from ". driver_name.
- ", try using counterfiles (maybe run dbdef-create?)";
- }
- $self->setfield($primary_key, $insertid);
- }
-
- my @virtual_fields =
- grep defined($self->getfield($_)) && $self->getfield($_) ne "",
- $self->virtual_fields;
- if (@virtual_fields) {
- my %v_values = map { $_, $self->getfield($_) } @virtual_fields;
-
- my $vfieldpart = $self->vfieldpart_hashref;
-
- my $v_statement = "INSERT INTO virtual_field(recnum, vfieldpart, value) ".
- "VALUES (?, ?, ?)";
-
- my $v_sth = dbh->prepare($v_statement) or do {
- dbh->rollback if $FS::UID::AutoCommit;
- return dbh->errstr;
- };
-
- foreach (keys(%v_values)) {
- $v_sth->execute($self->getfield($primary_key),
- $vfieldpart->{$_},
- $v_values{$_})
- or do {
- dbh->rollback if $FS::UID::AutoCommit;
- return $v_sth->errstr;
- };
- }
- }
-
-
- my $h_sth;
- 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 {
- dbh->rollback if $FS::UID::AutoCommit;
- return dbh->errstr;
- };
- } else {
- $h_sth = '';
- }
$h_sth->execute or return $h_sth->errstr if $h_sth;
-
dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
'';
@@ -713,7 +546,7 @@ Depriciated (use insert instead).
=cut
sub add {
- cluck "warning: FS::Record::add deprecated!";
+ cluck "warning: FS::Record::add depriciated!";
insert @_; #call method in this scope
}
@@ -731,14 +564,14 @@ sub delete {
map {
$self->getfield($_) eq ''
#? "( $_ IS NULL OR $_ = \"\" )"
- ? ( driver_name eq 'Pg'
+ ? ( driver_name =~ /^Pg$/i
? "$_ IS NULL"
: "( $_ IS NULL OR $_ = \"\" )"
)
: "$_ = ". _quote($self->getfield($_),$self->table,$_)
} ( $self->dbdef_table->primary_key )
? ( $self->dbdef_table->primary_key)
- : real_fields($self->table)
+ : $self->fields
);
warn "[debug]$me $statement\n" if $DEBUG > 1;
my $sth = dbh->prepare($statement) or return dbh->errstr;
@@ -752,19 +585,6 @@ sub delete {
$h_sth = '';
}
- my $primary_key = $self->dbdef_table->primary_key;
- my $v_sth;
- my @del_vfields;
- my $vfp = $self->vfieldpart_hashref;
- foreach($self->virtual_fields) {
- next if $self->getfield($_) eq '';
- unless(@del_vfields) {
- my $st = "DELETE FROM virtual_field WHERE recnum = ? AND vfieldpart = ?";
- $v_sth = dbh->prepare($st) or return dbh->errstr;
- }
- push @del_vfields, $_;
- }
-
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
@@ -775,10 +595,6 @@ sub delete {
my $rc = $sth->execute or return $sth->errstr;
#not portable #return "Record not found, statement:\n$statement" if $rc eq "0E0";
$h_sth->execute or return $h_sth->errstr if $h_sth;
- $v_sth->execute($self->getfield($primary_key), $vfp->{$_})
- or return $v_sth->errstr
- foreach (@del_vfields);
-
dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
#no need to needlessly destoy the data either (causes problems actually)
@@ -794,7 +610,7 @@ Depriciated (use delete instead).
=cut
sub del {
- cluck "warning: FS::Record::del deprecated!";
+ cluck "warning: FS::Record::del depriciated!";
&delete(@_); #call method in this scope
}
@@ -819,11 +635,8 @@ sub replace {
my $error = $new->check;
return $error if $error;
- #my @diff = grep $new->getfield($_) ne $old->getfield($_), $old->fields;
- my %diff = map { ($new->getfield($_) ne $old->getfield($_))
- ? ($_, $new->getfield($_)) : () } $old->fields;
-
- unless ( keys(%diff) ) {
+ my @diff = grep $new->getfield($_) ne $old->getfield($_), $old->fields;
+ unless ( @diff ) {
carp "[warning]$me $new -> replace $old: records identical";
return '';
}
@@ -831,18 +644,18 @@ sub replace {
my $statement = "UPDATE ". $old->table. " SET ". join(', ',
map {
"$_ = ". _quote($new->getfield($_),$old->table,$_)
- } real_fields($old->table)
+ } @diff
). ' WHERE '.
join(' AND ',
map {
$old->getfield($_) eq ''
#? "( $_ IS NULL OR $_ = \"\" )"
- ? ( driver_name eq 'Pg'
- ? "( $_ IS NULL OR $_ = '' )"
+ ? ( driver_name =~ /^Pg$/i
+ ? "( $_ IS NULL OR $_ = '' ) "
: "( $_ IS NULL OR $_ = \"\" )"
)
: "$_ = ". _quote($old->getfield($_),$old->table,$_)
- } ( $primary_key ? ( $primary_key ) : real_fields($old->table) )
+ } ( $primary_key ? ( $primary_key ) : $old->fields )
)
;
warn "[debug]$me $statement\n" if $DEBUG > 1;
@@ -866,44 +679,6 @@ sub replace {
$h_new_sth = '';
}
- # For virtual fields we have three cases with different SQL
- # statements: add, replace, delete
- my $v_add_sth;
- my $v_rep_sth;
- my $v_del_sth;
- my (@add_vfields, @rep_vfields, @del_vfields);
- my $vfp = $old->vfieldpart_hashref;
- foreach(grep { exists($diff{$_}) } $new->virtual_fields) {
- if($diff{$_} eq '') {
- # Delete
- unless(@del_vfields) {
- my $st = "DELETE FROM virtual_field WHERE recnum = ? ".
- "AND vfieldpart = ?";
- warn "[debug]$me $st\n" if $DEBUG > 2;
- $v_del_sth = dbh->prepare($st) or return dbh->errstr;
- }
- push @del_vfields, $_;
- } elsif($old->getfield($_) eq '') {
- # Add
- unless(@add_vfields) {
- my $st = "INSERT INTO virtual_field (value, recnum, vfieldpart) ".
- "VALUES (?, ?, ?)";
- warn "[debug]$me $st\n" if $DEBUG > 2;
- $v_add_sth = dbh->prepare($st) or return dbh->errstr;
- }
- push @add_vfields, $_;
- } else {
- # Replace
- unless(@rep_vfields) {
- my $st = "UPDATE virtual_field SET value = ? ".
- "WHERE recnum = ? AND vfieldpart = ?";
- warn "[debug]$me $st\n" if $DEBUG > 2;
- $v_rep_sth = dbh->prepare($st) or return dbh->errstr;
- }
- push @rep_vfields, $_;
- }
- }
-
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
@@ -915,24 +690,6 @@ sub replace {
#not portable #return "Record not found (or records identical)." if $rc eq "0E0";
$h_old_sth->execute or return $h_old_sth->errstr if $h_old_sth;
$h_new_sth->execute or return $h_new_sth->errstr if $h_new_sth;
-
- $v_del_sth->execute($old->getfield($primary_key),
- $vfp->{$_})
- or return $v_del_sth->errstr
- foreach(@del_vfields);
-
- $v_add_sth->execute($new->getfield($_),
- $old->getfield($primary_key),
- $vfp->{$_})
- or return $v_add_sth->errstr
- foreach(@add_vfields);
-
- $v_rep_sth->execute($new->getfield($_),
- $old->getfield($primary_key),
- $vfp->{$_})
- or return $v_rep_sth->errstr
- foreach(@rep_vfields);
-
dbh->commit or croak dbh->errstr if $FS::UID::AutoCommit;
'';
@@ -946,34 +703,18 @@ Depriciated (use replace instead).
=cut
sub rep {
- cluck "warning: FS::Record::rep deprecated!";
+ cluck "warning: FS::Record::rep depriciated!";
replace @_; #call method in this scope
}
=item check
-Checks virtual fields (using check_blocks). Subclasses should still provide
-a check method to validate real fields, foreign keys, etc., and call this
-method via $self->SUPER::check.
-
-(FIXME: Should this method try to make sure that it I<is> being called from
-a subclass's check method, to keep the current semantics as far as possible?)
+Not yet implemented, croaks. Derived classes should provide a check method.
=cut
sub check {
- #confess "FS::Record::check not implemented; supply one in subclass!";
- my $self = shift;
-
- foreach my $field ($self->virtual_fields) {
- for ($self->getfield($field)) {
- # See notes on check_block in FS::part_virtual_field.
- eval $self->pvf($field)->check_block;
- return $@ if $@;
- $self->setfield($field, $_);
- }
- }
- '';
+ confess "FS::Record::check not implemented; supply one in subclass!";
}
sub _h_statement {
@@ -981,7 +722,7 @@ sub _h_statement {
my @fields =
grep defined($self->getfield($_)) && $self->getfield($_) ne "",
- real_fields($self->table);
+ $self->fields
;
my @values = map { _quote( $self->getfield($_), $self->table, $_) } @fields;
@@ -995,13 +736,8 @@ sub _h_statement {
=item unique COLUMN
-B<Warning>: External use is B<deprecated>.
-
-Replaces COLUMN in record with a unique number, using counters in the
-filesystem. Used by the B<insert> method on single-field unique columns
-(see L<DBIx::DBSchema::Table>) and also as a fallback for primary keys
-that aren't SERIAL (Pg) or AUTO_INCREMENT (mysql).
-
+Replaces COLUMN in record with a unique number. Called by the B<add> method
+on primary keys and single-field unique columns (see L<DBIx::DBSchema::Table>).
Returns the new value.
=cut
@@ -1010,6 +746,8 @@ sub unique {
my($self,$field) = @_;
my($table)=$self->table;
+ #croak("&FS::UID::checkruid failed") unless &checkruid;
+
croak "Unique called on field $field, but it is ",
$self->getfield($field),
", not null!"
@@ -1025,8 +763,9 @@ sub unique {
# my($counter) = new File::CounterFile "$user/$table.$field",0;
# endhack
- my $index = $counter->inc;
- $index = $counter->inc while qsearchs($table, { $field=>$index } );
+ my($index)=$counter->inc;
+ $index=$counter->inc
+ while qsearchs($table,{$field=>$index}); #just in case
$index =~ /^(\d*)$/;
$index=$1;
@@ -1377,94 +1116,36 @@ sub ut_foreign_keyn {
: '';
}
-
-=item virtual_fields [ TABLE ]
-
-Returns a list of virtual fields defined for the table. This should not
-be exported, and should only be called as an instance or class method.
-
-=cut
-
-sub virtual_fields {
- my $self = shift;
- my $table;
- $table = $self->table or confess "virtual_fields called on non-table";
-
- confess "Unknown table $table" unless $dbdef->table($table);
-
- return () unless $self->dbdef->table('part_virtual_field');
-
- unless ( $virtual_fields_cache{$table} ) {
- my $query = 'SELECT name from part_virtual_field ' .
- "WHERE dbtable = '$table'";
- my $dbh = dbh;
- my $result = $dbh->selectcol_arrayref($query);
- confess $dbh->errstr if $dbh->err;
- $virtual_fields_cache{$table} = $result;
- }
-
- @{$virtual_fields_cache{$table}};
-
-}
-
-
=item fields [ TABLE ]
-This is a wrapper for real_fields and virtual_fields. Code that called
-fields before should probably continue to call fields.
+This can be used as both a subroutine and a method call. It returns a list
+of the columns in this record's table, or an explicitly specified table.
+(See L<DBIx::DBSchema::Table>).
=cut
+# Usage: @fields = fields($table);
+# @fields = $record->fields;
sub fields {
my $something = shift;
my $table;
- if($something->isa('FS::Record')) {
+ if ( ref($something) ) {
$table = $something->table;
} else {
$table = $something;
- $something = "FS::$table";
}
- return (real_fields($table), $something->virtual_fields());
+ #croak "Usage: \@fields = fields(\$table)\n or: \@fields = \$record->fields" unless $table;
+ my($table_obj) = $dbdef->table($table);
+ confess "Unknown table $table" unless $table_obj;
+ $table_obj->columns;
}
=back
-=item pvf FIELD_NAME
-
-Returns the FS::part_virtual_field object corresponding to a field in the
-record (specified by FIELD_NAME).
-
-=cut
-
-sub pvf {
- my ($self, $name) = (shift, shift);
-
- if(grep /^$name$/, $self->virtual_fields) {
- return qsearchs('part_virtual_field', { dbtable => $self->table,
- name => $name } );
- }
- ''
-}
-
=head1 SUBROUTINES
=over 4
-=item real_fields [ TABLE ]
-
-Returns a list of the real columns in the specified table. Called only by
-fields() and other subroutines elsewhere in FS::Record.
-
-=cut
-
-sub real_fields {
- my $table = shift;
-
- my($table_obj) = $dbdef->table($table);
- confess "Unknown table $table" unless $table_obj;
- $table_obj->columns;
-}
-
=item reload_dbdef([FILENAME])
Load a database definition (see L<DBIx::DBSchema>), optionally from a
@@ -1523,40 +1204,16 @@ sub _quote {
}
}
-=item vfieldpart_hashref TABLE
-
-Returns a hashref of virtual field names and vfieldparts applicable to the given
-TABLE.
-
-=cut
-
-sub vfieldpart_hashref {
- my $self = shift;
- my $table = $self->table;
-
- return {} unless $self->dbdef->table('part_virtual_field');
-
- my $dbh = dbh;
- my $statement = "SELECT vfieldpart, name FROM part_virtual_field WHERE ".
- "dbtable = '$table'";
- my $sth = $dbh->prepare($statement);
- $sth->execute or croak "Execution of '$statement' failed: ".$dbh->errstr;
- return { map { $_->{name}, $_->{vfieldpart} }
- @{$sth->fetchall_arrayref({})} };
-
-}
-
-
=item hfields TABLE
-This is deprecated. Don't use it.
+This is depriciated. Don't use it.
It returns a hash-type list with the fields of this record's table set true.
=cut
sub hfields {
- carp "warning: hfields is deprecated";
+ carp "warning: hfields is depriciated";
my($table)=@_;
my(%hash);
foreach (fields($table)) {
@@ -1592,7 +1249,7 @@ sub DESTROY { return; }
This module should probably be renamed, since much of the functionality is
of general use. It is not completely unlike Adapter::DBI (see below).
-Exported qsearch and qsearchs should be deprecated in favor of method calls
+Exported qsearch and qsearchs should be depriciated in favor of method calls
(against an FS::Record object like the old search and searchs that qsearch
and qsearchs were on top of.)
@@ -1600,7 +1257,7 @@ The whole fields / hfields mess should be removed.
The various WHERE clauses should be subroutined.
-table string should be deprecated in favor of DBIx::DBSchema::Table.
+table string should be depriciated in favor of DBIx::DBSchema::Table.
No doubt we could benefit from a Tied hash. Documenting how exists / defined
true maps to the database (and WHERE clauses) would also help.
diff --git a/FS/FS/UID.pm b/FS/FS/UID.pm
index 8271f89f2..f56ba3915 100644
--- a/FS/FS/UID.pm
+++ b/FS/FS/UID.pm
@@ -195,7 +195,9 @@ Returns the current Freeside user.
=cut
sub getotaker {
- $user;
+ #$user;
+ #stupid kludge until schema otaker fields are not 8 chars
+ substr($user,0,8);
}
=item cgisetotaker
diff --git a/FS/FS/addr_block.pm b/FS/FS/addr_block.pm
deleted file mode 100755
index 1fb60606d..000000000
--- a/FS/FS/addr_block.pm
+++ /dev/null
@@ -1,331 +0,0 @@
-package FS::addr_block;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs qsearch dbh );
-use FS::router;
-use FS::svc_broadband;
-use FS::Conf;
-use NetAddr::IP;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::addr_block - Object methods for addr_block records
-
-=head1 SYNOPSIS
-
- use FS::addr_block;
-
- $record = new FS::addr_block \%hash;
- $record = new FS::addr_block { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::addr_block record describes an address block assigned for broadband
-access. FS::addr_block inherits from FS::Record. The following fields are
-currently supported:
-
-=over 4
-
-=item blocknum - primary key, used in FS::svc_broadband to associate
-services to the block.
-
-=item routernum - the router (see FS::router) to which this
-block is assigned.
-
-=item ip_gateway - the gateway address used by customers within this block.
-
-=item ip_netmask - the netmask of the block, expressed as an integer.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record. To add the record to the database, see "insert".
-
-=cut
-
-sub table { 'addr_block'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-sub delete {
- my $self = shift;
- return 'Block must be deallocated before deletion'
- if $self->router;
-
- $self->SUPER::delete;
-}
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_number('routernum')
- || $self->ut_ip('ip_gateway')
- || $self->ut_number('ip_netmask')
- ;
- return $error if $error;
-
-
- # A routernum of 0 indicates an unassigned block and is allowed
- return "Unknown routernum"
- if ($self->routernum and not $self->router);
-
- my $self_addr = $self->NetAddr;
- return "Cannot parse address: ". $self->ip_gateway . '/' . $self->ip_netmask
- unless $self_addr;
-
- if (not $self->blocknum) {
- my @block = grep {
- my $block_addr = $_->NetAddr;
- if($block_addr->contains($self_addr)
- or $self_addr->contains($block_addr)) { $_; };
- } qsearch( 'addr_block', {});
- foreach(@block) {
- return "Block intersects existing block ".$_->ip_gateway."/".$_->ip_netmask;
- }
- }
-
- $self->SUPER::check;
-}
-
-
-=item router
-
-Returns the FS::router object corresponding to this object. If the
-block is unassigned, returns undef.
-
-=cut
-
-sub router {
- my $self = shift;
- return qsearchs('router', { routernum => $self->routernum });
-}
-
-=item svc_broadband
-
-Returns a list of FS::svc_broadband objects associated
-with this object.
-
-=cut
-
-sub svc_broadband {
- my $self = shift;
- return qsearch('svc_broadband', { blocknum => $self->blocknum });
-}
-
-=item NetAddr
-
-Returns a NetAddr::IP object for this block's address and netmask.
-
-=cut
-
-sub NetAddr {
- my $self = shift;
-
- return new NetAddr::IP ($self->ip_gateway, $self->ip_netmask);
-}
-
-=item next_free_addr
-
-Returns a NetAddr::IP object corresponding to the first unassigned address
-in the block (other than the network, broadcast, or gateway address). If
-there are no free addresses, returns false.
-
-=cut
-
-sub next_free_addr {
- my $self = shift;
-
- my $conf = new FS::Conf;
- my @excludeaddr = $conf->config('exclude_ip_addr');
-
-my @used =
-( (map { $_->NetAddr->addr }
- ($self,
- qsearch('svc_broadband', { blocknum => $self->blocknum }))
- ), @excludeaddr
-);
-
- my @free = $self->NetAddr->hostenum;
- while (my $ip = shift @free) {
- if (not grep {$_ eq $ip->addr;} @used) { return $ip; };
- }
-
- '';
-
-}
-
-=item allocate
-
-Allocates this address block to a router. Takes an FS::router object
-as an argument.
-
-At present it's not possible to reallocate a block to a different router
-except by deallocating it first, which requires that none of its addresses
-be assigned. This is probably as it should be.
-
-=cut
-
-sub allocate {
- my ($self, $router) = @_;
-
- return 'Block is already allocated'
- if($self->router);
-
- return 'Block must be allocated to a router'
- unless(ref $router eq 'FS::router');
-
- my @svc = $self->svc_broadband;
- if (@svc) {
- return 'Block has assigned addresses: '. join ', ', map {$_->ip_addr} @svc;
- }
-
- my $new = new FS::addr_block {$self->hash};
- $new->routernum($router->routernum);
- return $new->replace($self);
-
-}
-
-=item deallocate
-
-Deallocates the block (i.e. sets the routernum to 0). If any addresses in the
-block are assigned to services, it fails.
-
-=cut
-
-sub deallocate {
- my $self = shift;
-
- my @svc = $self->svc_broadband;
- if (@svc) {
- return 'Block has assigned addresses: '. join ', ', map {$_->ip_addr} @svc;
- }
-
- my $new = new FS::addr_block {$self->hash};
- $new->routernum(0);
- return $new->replace($self);
-}
-
-=item split_block
-
-Splits this address block into two equal blocks, occupying the same space as
-the original block. The first of the two will also have the same blocknum.
-The gateway address of each block will be set to the first usable address, i.e.
-(network address)+1. Since this method is designed for use on unallocated
-blocks, this is probably the correct behavior.
-
-(At present, splitting allocated blocks is disallowed. Anyone who wants to
-implement this is reminded that each split costs three addresses, and any
-customers who were using these addresses will have to be moved; depending on
-how full the block was before being split, they might have to be moved to a
-different block. Anyone who I<still> wants to implement it is asked to tie it
-to a configuration switch so that site admins can disallow it.)
-
-=cut
-
-sub split_block {
-
- # We should consider using Attribute::Handlers/Aspect/Hook::LexWrap/
- # something to atomicize functions, so that we can say
- #
- # sub split_block : atomic {
- #
- # instead of repeating all this AutoCommit verbage in every
- # sub that does more than one database operation.
-
- my $oldAutoCommit = $FS::UID::AutoCommit;
- local $FS::UID::AutoCommit = 0;
- my $dbh = dbh;
-
- my $self = shift;
- my $error;
-
- if ($self->router) {
- return 'Block is already allocated';
- }
-
- #TODO: Smallest allowed block should be a config option.
- if ($self->NetAddr->masklen() ge 30) {
- return 'Cannot split blocks with a mask length >= 30';
- }
-
- my (@new, @ip);
- $ip[0] = $self->NetAddr;
- @ip = map {$_->first()} $ip[0]->split($self->ip_netmask + 1);
-
- foreach (0,1) {
- $new[$_] = new FS::addr_block {$self->hash};
- $new[$_]->ip_gateway($ip[$_]->addr);
- $new[$_]->ip_netmask($ip[$_]->masklen);
- }
-
- $new[1]->blocknum('');
-
- $error = $new[0]->replace($self);
- if ($error) {
- $dbh->rollback;
- return $error;
- }
-
- $error = $new[1]->insert;
- if ($error) {
- $dbh->rollback;
- return $error;
- }
-
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
- return '';
-}
-
-=item merge
-
-To be implemented.
-
-=back
-
-=head1 BUGS
-
-Minimum block size should be a config option. It's hardcoded at /30 right
-now because that's the smallest block that makes any sense at all.
-
-=cut
-
-1;
-
diff --git a/FS/FS/agent.pm b/FS/FS/agent.pm
index 2f70d654d..9b7492d07 100644
--- a/FS/FS/agent.pm
+++ b/FS/FS/agent.pm
@@ -137,7 +137,8 @@ sub check {
return "Unknown typenum!"
unless $self->agent_type;
- $self->SUPER::check;
+ '';
+
}
=item agent_type
@@ -168,7 +169,7 @@ sub pkgpart_hashref {
=head1 VERSION
-$Id: agent.pm,v 1.6 2003-09-30 15:01:46 ivan Exp $
+$Id: agent.pm,v 1.3.4.2 2003-09-30 15:01:42 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/agent_type.pm b/FS/FS/agent_type.pm
index 5ba5ef291..988533ae3 100644
--- a/FS/FS/agent_type.pm
+++ b/FS/FS/agent_type.pm
@@ -102,8 +102,7 @@ sub check {
my $self = shift;
$self->ut_numbern('typenum')
- or $self->ut_text('atype')
- or $self->SUPER::check;
+ or $self->ut_text('atype');
}
@@ -151,7 +150,7 @@ sub pkgpart {
=head1 VERSION
-$Id: agent_type.pm,v 1.2 2003-08-05 00:20:40 khoff Exp $
+$Id: agent_type.pm,v 1.1 1999-08-04 09:03:53 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm
index a3e76620e..1c3941b21 100644
--- a/FS/FS/cust_bill.pm
+++ b/FS/FS/cust_bill.pm
@@ -2,12 +2,19 @@ package FS::cust_bill;
use strict;
use vars qw( @ISA $conf $money_char );
+use vars qw( $lpr $invoice_from $smtpmachine );
+use vars qw( $cybercash );
+use vars qw( $xaction $E_NoErr );
+use vars qw( $bop_processor $bop_login $bop_password $bop_action @bop_options );
+use vars qw( $ach_processor $ach_login $ach_password $ach_action @ach_options );
use vars qw( $invoice_lines @buf ); #yuck
+use vars qw( $realtime_bop_decline_quiet );
use Date::Format;
+use Mail::Internet 1.44;
+use Mail::Header;
use Text::Template;
use FS::UID qw( datasrc );
use FS::Record qw( qsearch qsearchs );
-use FS::Misc qw( send_email );
use FS::cust_main;
use FS::cust_bill_pkg;
use FS::cust_credit;
@@ -19,11 +26,70 @@ use FS::cust_bill_event;
@ISA = qw( FS::Record );
+$realtime_bop_decline_quiet = 0;
+
#ask FS::UID to run this stuff for us later
-FS::UID->install_callback( sub {
+$FS::UID::callback{'FS::cust_bill'} = sub {
+
$conf = new FS::Conf;
+
$money_char = $conf->config('money_char') || '$';
-} );
+
+ $lpr = $conf->config('lpr');
+ $invoice_from = $conf->config('invoice_from');
+ $smtpmachine = $conf->config('smtpmachine');
+
+ ( $bop_processor,$bop_login, $bop_password, $bop_action ) = ( '', '', '', '');
+ @bop_options = ();
+ ( $ach_processor,$ach_login, $ach_password, $ach_action ) = ( '', '', '', '');
+ @ach_options = ();
+
+ if ( $conf->exists('cybercash3.2') ) {
+ require CCMckLib3_2;
+ #qw($MCKversion %Config InitConfig CCError CCDebug CCDebug2);
+ require CCMckDirectLib3_2;
+ #qw(SendCC2_1Server);
+ require CCMckErrno3_2;
+ #qw(MCKGetErrorMessage $E_NoErr);
+ import CCMckErrno3_2 qw($E_NoErr);
+
+ my $merchant_conf;
+ ($merchant_conf,$xaction)= $conf->config('cybercash3.2');
+ my $status = &CCMckLib3_2::InitConfig($merchant_conf);
+ if ( $status != $E_NoErr ) {
+ warn "CCMckLib3_2::InitConfig error:\n";
+ foreach my $key (keys %CCMckLib3_2::Config) {
+ warn " $key => $CCMckLib3_2::Config{$key}\n"
+ }
+ my($errmsg) = &CCMckErrno3_2::MCKGetErrorMessage($status);
+ die "CCMckLib3_2::InitConfig fatal error: $errmsg\n";
+ }
+ $cybercash='cybercash3.2';
+ } elsif ( $conf->exists('business-onlinepayment') ) {
+ ( $bop_processor,
+ $bop_login,
+ $bop_password,
+ $bop_action,
+ @bop_options
+ ) = $conf->config('business-onlinepayment');
+ $bop_action ||= 'normal authorization';
+ ( $ach_processor, $ach_login, $ach_password, $ach_action, @ach_options ) =
+ ( $bop_processor, $bop_login, $bop_password, $bop_action, @bop_options );
+ eval "use Business::OnlinePayment";
+ }
+
+ if ( $conf->exists('business-onlinepayment-ach') ) {
+ ( $ach_processor,
+ $ach_login,
+ $ach_password,
+ $ach_action,
+ @ach_options
+ ) = $conf->config('business-onlinepayment-ach');
+ $ach_action ||= 'normal authorization';
+ eval "use Business::OnlinePayment";
+ }
+
+};
=head1 NAME
@@ -161,7 +227,7 @@ sub check {
$self->printed(0) if $self->printed eq '';
- $self->SUPER::check;
+ ''; #no error
}
=item previous
@@ -328,18 +394,32 @@ sub send {
my @print_text = $self->print_text('', $template);
my @invoicing_list = $self->cust_main->invoicing_list;
- if ( grep { $_ ne 'POST' } @invoicing_list or !@invoicing_list ) { #email
+ if ( grep { $_ ne 'POST' } @invoicing_list or !@invoicing_list ) { #email
#better to notify this person than silence
- @invoicing_list = ($conf->config('invoice_from')) unless @invoicing_list;
-
- my $error = send_email(
- 'from' => $conf->config('invoice_from'),
- 'to' => [ grep { $_ ne 'POST' } @invoicing_list ],
- 'subject' => 'Invoice',
- 'body' => \@print_text,
+ @invoicing_list = ($invoice_from) unless @invoicing_list;
+
+ #false laziness w/FS::cust_pay::delete & fs_signup_server && ::realtime_card
+ #$ENV{SMTPHOSTS} = $smtpmachine;
+ $ENV{MAILADDRESS} = $invoice_from;
+ my $header = new Mail::Header ( [
+ "From: $invoice_from",
+ "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
+ "Sender: $invoice_from",
+ "Reply-To: $invoice_from",
+ "Date: ". time2str("%a, %d %b %Y %X %z", time),
+ "Subject: Invoice",
+ ] );
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [ @print_text ], #( date)
);
- return "can't send invoice: $error" if $error;
+ $!=0;
+ $message->smtpsend( Host => $smtpmachine )
+ or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
+ or return "(customer # ". $self->custnum. ") can't send invoice email".
+ " to ". join(', ', grep { $_ ne 'POST' } @invoicing_list ).
+ " via server $smtpmachine with SMTP: $!";
}
@@ -348,7 +428,6 @@ sub send {
}
if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal
- my $lpr = $conf->config('lpr');
open(LPR, "|$lpr")
or return "Can't open pipe to $lpr: $!";
print LPR @print_text;
@@ -481,13 +560,10 @@ sub send_csv {
time2str("%x", $cust_bill_pkg->edate),
);
- } else { #pkgnum tax
+ } else { #pkgnum Tax
next unless $cust_bill_pkg->setup != 0;
- my $itemdesc = defined $cust_bill_pkg->dbdef_table->column('itemdesc')
- ? ( $cust_bill_pkg->itemdesc || 'Tax' )
- : 'Tax';
($pkg, $setup, $recur, $sdate, $edate) =
- ( $itemdesc, sprintf("%10.2f",$cust_bill_pkg->setup), '', '', '' );
+ ( 'Tax', sprintf("%10.2f",$cust_bill_pkg->setup), '', '', '' );
}
$csv->combine(
@@ -559,7 +635,15 @@ for supported processors.
sub realtime_card {
my $self = shift;
- $self->realtime_bop( 'CC', @_ );
+ $self->realtime_bop(
+ 'CC',
+ $bop_processor,
+ $bop_login,
+ $bop_password,
+ $bop_action,
+ \@bop_options,
+ @_
+ );
}
=item realtime_ach
@@ -573,7 +657,15 @@ for supported processors.
sub realtime_ach {
my $self = shift;
- $self->realtime_bop( 'ECHECK', @_ );
+ $self->realtime_bop(
+ 'ECHECK',
+ $ach_processor,
+ $ach_login,
+ $ach_password,
+ $ach_action,
+ \@ach_options,
+ @_
+ );
}
=item realtime_lec
@@ -587,11 +679,22 @@ for supported processors.
sub realtime_lec {
my $self = shift;
- $self->realtime_bop( 'LEC', @_ );
+ $self->realtime_bop(
+ 'LEC',
+ $bop_processor,
+ $bop_login,
+ $bop_password,
+ $bop_action,
+ \@bop_options,
+ @_
+ );
}
sub realtime_bop {
- my( $self, $method ) = @_;
+ my( $self, $method, $processor, $login, $password, $action, $options ) = @_;
+
+ #trim an extraneous blank line
+ pop @$options if scalar(@$options) % 2 && $options->[-1] =~ /^\s*$/;
my $cust_main = $self->cust_main;
my $balance = $cust_main->balance;
@@ -599,6 +702,33 @@ sub realtime_bop {
$amount = sprintf("%.2f", $amount);
return "not run (balance $balance)" unless $amount > 0;
+ my $address = $cust_main->address1;
+ $address .= ", ". $cust_main->address2 if $cust_main->address2;
+
+ my($payname, $payfirst, $paylast);
+ if ( $cust_main->payname && $method ne 'ECHECK' ) {
+ $payname = $cust_main->payname;
+ $payname =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
+ or do {
+ #$dbh->rollback if $oldAutoCommit;
+ return "Illegal payname $payname";
+ };
+ ($payfirst, $paylast) = ($1, $2);
+ } else {
+ $payfirst = $cust_main->getfield('first');
+ $paylast = $cust_main->getfield('last');
+ $payname = "$payfirst $paylast";
+ }
+
+ my @invoicing_list = grep { $_ ne 'POST' } $cust_main->invoicing_list;
+ if ( $conf->exists('emailinvoiceauto')
+ || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
+ push @invoicing_list, $cust_main->all_emails;
+ }
+ my $email = $invoicing_list[0];
+
+ my( $action1, $action2 ) = split(/\s*\,\s*/, $action );
+
my $description = 'Internet Services';
if ( $conf->exists('business-onlinepayment-description') ) {
my $dtempl = $conf->config('business-onlinepayment-description');
@@ -612,12 +742,276 @@ sub realtime_bop {
grep { $_->pkgnum } $self->cust_bill_pkg
);
$description = eval qq("$dtempl");
+
}
- $cust_main->realtime_bop($method, $amount,
- 'description' => $description,
- 'invnum' => $self->invnum,
+ my %content;
+ if ( $method eq 'CC' ) {
+
+ $content{card_number} = $cust_main->payinfo;
+ $cust_main->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ $content{expiration} = "$2/$1";
+
+ $content{cvv2} = $cust_main->paycvv
+ if defined $cust_main->dbdef_table->column('paycvv')
+ && length($cust_main->paycvv);
+
+ $content{recurring_billing} = 'YES'
+ if qsearch('cust_pay', { 'custnum' => $cust_main->custnum,
+ 'payby' => 'CARD',
+ 'payinfo' => $cust_main->payinfo, } );
+
+ } elsif ( $method eq 'ECHECK' ) {
+ my($account_number,$routing_code) = $cust_main->payinfo;
+ ( $content{account_number}, $content{routing_code} ) =
+ split('@', $cust_main->payinfo);
+ $content{bank_name} = $cust_main->payname;
+ $content{account_type} = 'CHECKING';
+ $content{account_name} = $payname;
+ $content{customer_org} = $self->company ? 'B' : 'I';
+ $content{customer_ssn} = $self->ss;
+ } elsif ( $method eq 'LEC' ) {
+ $content{phone} = $cust_main->payinfo;
+ }
+
+ my $transaction =
+ new Business::OnlinePayment( $processor, @$options );
+ $transaction->content(
+ 'type' => $method,
+ 'login' => $login,
+ 'password' => $password,
+ 'action' => $action1,
+ 'description' => $description,
+ 'amount' => $amount,
+ 'invoice_number' => $self->invnum,
+ 'customer_id' => $self->custnum,
+ 'last_name' => $paylast,
+ 'first_name' => $payfirst,
+ 'name' => $payname,
+ 'address' => $address,
+ 'city' => $cust_main->city,
+ 'state' => $cust_main->state,
+ 'zip' => $cust_main->zip,
+ 'country' => $cust_main->country,
+ 'referer' => 'http://cleanwhisker.420.am/',
+ 'email' => $email,
+ 'phone' => $cust_main->daytime || $cust_main->night,
+ %content, #after
);
+ $transaction->submit();
+
+ if ( $transaction->is_success() && $action2 ) {
+ my $auth = $transaction->authorization;
+ my $ordernum = $transaction->can('order_number')
+ ? $transaction->order_number
+ : '';
+
+ #warn "********* $auth ***********\n";
+ #warn "********* $ordernum ***********\n";
+ my $capture =
+ new Business::OnlinePayment( $processor, @$options );
+
+ my %capture = (
+ %content,
+ type => $method,
+ action => $action2,
+ login => $login,
+ password => $password,
+ order_number => $ordernum,
+ amount => $amount,
+ authorization => $auth,
+ description => $description,
+ );
+
+ foreach my $field (qw( authorization_source_code returned_ACI transaction_identifier validation_code
+ transaction_sequence_num local_transaction_date
+ local_transaction_time AVS_result_code )) {
+ $capture{$field} = $transaction->$field() if $transaction->can($field);
+ }
+
+ $capture->content( %capture );
+
+ $capture->submit();
+
+ unless ( $capture->is_success ) {
+ my $e = "Authorization sucessful but capture failed, invnum #".
+ $self->invnum. ': '. $capture->result_code.
+ ": ". $capture->error_message;
+ warn $e;
+ return $e;
+ }
+
+ }
+
+ #remove paycvv after initial transaction
+ #make this disable-able via a config option if anyone insists?
+ # (though that probably violates cardholder agreements)
+ use Business::CreditCard;
+ if ( defined $cust_main->dbdef_table->column('paycvv')
+ && length($cust_main->paycvv)
+ && ! grep { $_ eq cardtype($cust_main->payinfo) } $conf->config('cvv-save')
+
+ ) {
+ my $new = new FS::cust_main { $cust_main->hash };
+ $new->paycvv('');
+ my $error = $new->replace($cust_main);
+ if ( $error ) {
+ warn "error removing cvv: $error\n";
+ }
+ }
+
+ #result handling
+ if ( $transaction->is_success() ) {
+
+ my %method2payby = (
+ 'CC' => 'CARD',
+ 'ECHECK' => 'CHEK',
+ 'LEC' => 'LECB',
+ );
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'invnum' => $self->invnum,
+ 'paid' => $amount,
+ '_date' => '',
+ 'payby' => $method2payby{$method},
+ 'payinfo' => $cust_main->payinfo,
+ 'paybatch' => "$processor:". $transaction->authorization,
+ } );
+ my $error = $cust_pay->insert;
+ if ( $error ) {
+ # gah, even with transactions.
+ my $e = 'WARNING: Card/ACH debited but database not updated - '.
+ 'error applying payment, invnum #' . $self->invnum.
+ " ($processor): $error";
+ warn $e;
+ return $e;
+ } else {
+ return '';
+ }
+ #} elsif ( $options{'report_badcard'} ) {
+ } else {
+
+ my $perror = "$processor error, invnum #". $self->invnum. ': '.
+ $transaction->result_code. ": ". $transaction->error_message;
+
+ if ( !$realtime_bop_decline_quiet && $conf->exists('emaildecline')
+ && grep { $_ ne 'POST' } $cust_main->invoicing_list
+ && ! grep { $_ eq $transaction->error_message }
+ $conf->config('emaildecline-exclude')
+ ) {
+ my @templ = $conf->config('declinetemplate');
+ my $template = new Text::Template (
+ TYPE => 'ARRAY',
+ SOURCE => [ map "$_\n", @templ ],
+ ) or return "($perror) can't create template: $Text::Template::ERROR";
+ $template->compile()
+ or return "($perror) can't compile template: $Text::Template::ERROR";
+
+ my $templ_hash = { error => $transaction->error_message };
+
+ #false laziness w/FS::cust_pay::delete & fs_signup_server && ::send
+ $ENV{MAILADDRESS} = $invoice_from;
+ my $header = new Mail::Header ( [
+ "From: $invoice_from",
+ "To: ". join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list ),
+ "Sender: $invoice_from",
+ "Reply-To: $invoice_from",
+ "Date: ". time2str("%a, %d %b %Y %X %z", time),
+ "Subject: Your payment could not be processed",
+ ] );
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [ $template->fill_in(HASH => $templ_hash) ],
+ );
+ $!=0;
+ $message->smtpsend( Host => $smtpmachine )
+ or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
+ or return "($perror) (customer # ". $self->custnum.
+ ") can't send card decline email to ".
+ join(', ', grep { $_ ne 'POST' } $cust_main->invoicing_list ).
+ " via server $smtpmachine with SMTP: $!";
+ }
+
+ return $perror;
+ }
+
+}
+
+=item realtime_card_cybercash
+
+Attempts to pay this invoice with the CyberCash CashRegister realtime gateway.
+
+=cut
+
+sub realtime_card_cybercash {
+ my $self = shift;
+ my $cust_main = $self->cust_main;
+ my $amount = $self->owed;
+
+ return "CyberCash CashRegister real-time card processing not enabled!"
+ unless $cybercash eq 'cybercash3.2';
+
+ my $address = $cust_main->address1;
+ $address .= ", ". $cust_main->address2 if $cust_main->address2;
+
+ #fix exp. date
+ #$cust_main->paydate =~ /^(\d+)\/\d*(\d{2})$/;
+ $cust_main->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ my $exp = "$2/$1";
+
+ #
+
+ my $paybatch = $self->invnum.
+ '-' . time2str("%y%m%d%H%M%S", time);
+
+ my $payname = $cust_main->payname ||
+ $cust_main->getfield('first').' '.$cust_main->getfield('last');
+
+ my $country = $cust_main->country eq 'US' ? 'USA' : $cust_main->country;
+
+ my @full_xaction = ( $xaction,
+ 'Order-ID' => $paybatch,
+ 'Amount' => "usd $amount",
+ 'Card-Number' => $cust_main->getfield('payinfo'),
+ 'Card-Name' => $payname,
+ 'Card-Address' => $address,
+ 'Card-City' => $cust_main->getfield('city'),
+ 'Card-State' => $cust_main->getfield('state'),
+ 'Card-Zip' => $cust_main->getfield('zip'),
+ 'Card-Country' => $country,
+ 'Card-Exp' => $exp,
+ );
+
+ my %result;
+ %result = &CCMckDirectLib3_2::SendCC2_1Server(@full_xaction);
+
+ if ( $result{'MStatus'} eq 'success' ) { #cybercash smps v.2 or 3
+ my $cust_pay = new FS::cust_pay ( {
+ 'invnum' => $self->invnum,
+ 'paid' => $amount,
+ '_date' => '',
+ 'payby' => 'CARD',
+ 'payinfo' => $cust_main->payinfo,
+ 'paybatch' => "$cybercash:$paybatch",
+ } );
+ my $error = $cust_pay->insert;
+ if ( $error ) {
+ # gah, even with transactions.
+ my $e = 'WARNING: Card debited but database not updated - '.
+ 'error applying payment, invnum #' . $self->invnum.
+ " (CyberCash Order-ID $paybatch): $error";
+ warn $e;
+ return $e;
+ } else {
+ return '';
+ }
+# } elsif ( $result{'Mstatus'} ne 'failure-bad-money'
+# || $options{'report_badcard'}
+# ) {
+ } else {
+ return 'Cybercash error, invnum #' .
+ $self->invnum. ':'. $result{'MErrMsg'};
+ }
}
@@ -729,8 +1123,6 @@ sub print_text {
map { [ " ". $_->[0]. ": ". $_->[1], '' ] } $cust_pkg->labels;
}
- push @buf, map { [ " $_", '' ] } $cust_bill_pkg->details;
-
} else { #pkgnum tax or one-shot line item
my $itemdesc = defined $cust_bill_pkg->dbdef_table->column('itemdesc')
? ( $cust_bill_pkg->itemdesc || 'Tax' )
diff --git a/FS/FS/cust_bill_event.pm b/FS/FS/cust_bill_event.pm
index ddd676281..c97734780 100644
--- a/FS/FS/cust_bill_event.pm
+++ b/FS/FS/cust_bill_event.pm
@@ -122,7 +122,7 @@ sub check {
return "Unknown eventpart ". $self->eventpart
unless qsearchs( 'part_bill_event' ,{ 'eventpart' => $self->eventpart } );
- $self->SUPER::check;
+ ''; #no error
}
=item part_bill_event
diff --git a/FS/FS/cust_bill_pay.pm b/FS/FS/cust_bill_pay.pm
index c8b5525ea..ea0236deb 100644
--- a/FS/FS/cust_bill_pay.pm
+++ b/FS/FS/cust_bill_pay.pm
@@ -181,7 +181,7 @@ sub check {
$self->_date(time) unless $self->_date;
- $self->SUPER::check;
+ ''; #no error
}
=item cust_pay
diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm
index 6800707fe..72f9ce4a9 100644
--- a/FS/FS/cust_bill_pkg.pm
+++ b/FS/FS/cust_bill_pkg.pm
@@ -2,12 +2,11 @@ package FS::cust_bill_pkg;
use strict;
use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs dbdef dbh );
+use FS::Record qw( qsearchs );
use FS::cust_pkg;
use FS::cust_bill;
-use FS::cust_bill_pkg_detail;
-@ISA = qw( FS::Record );
+@ISA = qw(FS::Record );
=head1 NAME
@@ -48,8 +47,6 @@ supported:
=item edate - ending date of recurring fee
-=item itemdesc - Line item description (currentlty used only when pkgnum is 0)
-
=back
sdate and edate are specified as UNIX timestamps; see L<perlfunc/"time">. Also
@@ -74,51 +71,6 @@ sub table { 'cust_bill_pkg'; }
Adds this line item to the database. If there is an error, returns the error,
otherwise returns false.
-=cut
-
-sub insert {
- 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::insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
-
- unless ( defined dbdef->table('cust_bill_pkg_detail') && $self->get('details') ) {
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
- return '';
- }
-
- foreach my $detail ( @{$self->get('details')} ) {
- my $cust_bill_pkg_detail = new FS::cust_bill_pkg_detail {
- 'pkgnum' => $self->pkgnum,
- 'invnum' => $self->invnum,
- 'detail' => $detail,
- };
- $error = $cust_bill_pkg_detail->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
- }
- }
-
- $dbh->commit or die $dbh->errstr if $oldAutoCommit;
- '';
-
-}
-
=item delete
Currently unimplemented. I don't remove line items because there would then be
@@ -159,7 +111,6 @@ sub check {
|| $self->ut_money('recur')
|| $self->ut_numbern('sdate')
|| $self->ut_numbern('edate')
- || $self->ut_textn('itemdesc')
;
return $error if $error;
@@ -171,7 +122,7 @@ sub check {
return "Unknown invnum"
unless qsearchs( 'cust_bill' ,{ 'invnum' => $self->invnum } );
- $self->SUPER::check;
+ ''; #no error
}
=item cust_pkg
@@ -185,22 +136,11 @@ sub cust_pkg {
qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
}
-=item details
-
-Returns an array of detail information for the invoice line item.
-
-=cut
+=back
-sub details {
- my $self = shift;
- return () unless defined dbdef->table('cust_bill_pkg_detail');
- map { $_->detail }
- qsearch ( 'cust_bill_pkg_detail', { 'pkgnum' => $self->pkgnum,
- 'invnum' => $self->invnum, } );
- #qsearch ( 'cust_bill_pkg_detail', { 'lineitemnum' => $self->lineitemnum });
-}
+=head1 VERSION
-=back
+$Id: cust_bill_pkg.pm,v 1.3 2002-04-06 22:32:43 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/cust_bill_pkg_detail.pm b/FS/FS/cust_bill_pkg_detail.pm
deleted file mode 100644
index 261aa80ea..000000000
--- a/FS/FS/cust_bill_pkg_detail.pm
+++ /dev/null
@@ -1,124 +0,0 @@
-package FS::cust_bill_pkg_detail;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearch qsearchs );
-
-@ISA = qw(FS::Record);
-
-=head1 NAME
-
-FS::cust_bill_pkg_detail - Object methods for cust_bill_pkg_detail records
-
-=head1 SYNOPSIS
-
- use FS::cust_bill_pkg_detail;
-
- $record = new FS::cust_bill_pkg_detail \%hash;
- $record = new FS::cust_bill_pkg_detail { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::cust_bill_pkg_detail object represents additional detail information for
-an invoice line item (see L<FS::cust_bill_pkg>). FS::cust_bill_pkg_detail
-inherits from FS::Record. The following fields are currently supported:
-
-=over 4
-
-=item detailnum - primary key
-
-=item pkgnum -
-
-=item invnum -
-
-=item detail - detail description
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new line item detail. To add the line item detail to the database,
-see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-# the new method can be inherited from FS::Record, if a table method is defined
-
-sub table { 'cust_bill_pkg_detail'; }
-
-=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 line item detail. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-# the check method should currently be supplied - FS::Record contains some
-# data checking routines
-
-sub check {
- my $self = shift;
-
- $self->ut_numbern('detailnum')
- || $self->ut_foreign_key('pkgnum', 'cust_pkg', 'pkgnum')
- || $self->ut_foreign_key('invnum', 'cust_pkg', 'invnum')
- || $self->ut_text('detail')
- || $self->SUPER::check
- ;
-
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::cust_bill_pkg>, L<FS::Record>, schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index 19a54534f..8f783d9a5 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -236,7 +236,7 @@ sub check {
$self->otaker(getotaker);
- $self->SUPER::check;
+ ''; #no error
}
=item cust_refund
diff --git a/FS/FS/cust_credit_bill.pm b/FS/FS/cust_credit_bill.pm
index bd76c2e1a..449f01149 100644
--- a/FS/FS/cust_credit_bill.pm
+++ b/FS/FS/cust_credit_bill.pm
@@ -150,7 +150,7 @@ sub check {
return "Cannot apply more than remaining value of invoice"
unless $self->amount <= $cust_bill->owed;
- $self->SUPER::check;
+ ''; #no error
}
=item sub cust_credit
diff --git a/FS/FS/cust_credit_refund.pm b/FS/FS/cust_credit_refund.pm
index d0deae2f3..cc3b32cdb 100644
--- a/FS/FS/cust_credit_refund.pm
+++ b/FS/FS/cust_credit_refund.pm
@@ -156,7 +156,7 @@ sub check {
return "unknown cust_credit.crednum: ". $self->crednum
unless qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
- $self->SUPER::check;
+ ''; #no error
}
=item cust_refund
@@ -185,7 +185,7 @@ sub cust_credit {
=head1 VERSION
-$Id: cust_credit_refund.pm,v 1.10 2003-08-05 00:20:41 khoff Exp $
+$Id: cust_credit_refund.pm,v 1.9 2002-01-26 01:52:31 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 6ca32871d..986fef3a5 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -2,7 +2,6 @@ package FS::cust_main;
use strict;
use vars qw( @ISA $conf $Debug $import );
-use vars qw( $realtime_bop_decline_quiet ); #ugh
use Safe;
use Carp;
BEGIN {
@@ -16,7 +15,6 @@ use Date::Format;
use Business::CreditCard;
use FS::UID qw( getotaker dbh );
use FS::Record qw( qsearchs qsearch dbdef );
-use FS::Misc qw( send_email );
use FS::cust_pkg;
use FS::cust_bill;
use FS::cust_bill_pkg;
@@ -40,16 +38,13 @@ use FS::Msgcat qw(gettext);
@ISA = qw( FS::Record );
-$realtime_bop_decline_quiet = 0;
-
$Debug = 0;
#$Debug = 1;
$import = 0;
#ask FS::UID to run this stuff for us later
-#$FS::UID::callback{'FS::cust_main'} = sub {
-install_callback FS::UID sub {
+$FS::UID::callback{'FS::cust_main'} = sub {
$conf = new FS::Conf;
#yes, need it for stuff below (prolly should be cached)
};
@@ -169,12 +164,10 @@ FS::Record. The following fields are currently supported:
=item ship_fax - phone (optional)
-=item payby - I<CARD> (credit card - automatic), I<DCRD> (credit card - on-demand), I<CHEK> (electronic check - automatic), I<DCHK> (electronic check - on-demand), I<LECB> (Phone bill billing), I<BILL> (billing), I<COMP> (free), or I<PREPAY> (special billing type: applies a credit - see L<FS::prepay_credit> and sets billing type to I<BILL>)
+=item payby - `CARD' (credit cards), `CHEK' (electronic check), `LECB' (Phone bill billing), `BILL' (billing), `COMP' (free), or `PREPAY' (special billing type: applies a credit - see L<FS::prepay_credit> and sets billing type to BILL)
=item payinfo - card number, P.O., comp issuer (4-8 lowercase alphanumerics; think username) or prepayment identifier (see L<FS::prepay_credit>)
-=item paycvv - Card Verification Value, "CVV2" (also known as CVC2 or CID), the 3 or 4 digit number on the back (or front, for American Express) of the credit card
-
=item paydate - expiration date, mm/yyyy, m/yyyy, mm/yy or m/yy
=item payname - name on card or billing name
@@ -185,8 +178,6 @@ FS::Record. The following fields are currently supported:
=item comments - comments (optional)
-=item referral_custnum - referring customer number
-
=back
=head1 METHODS
@@ -781,11 +772,11 @@ sub check {
}
}
- $self->payby =~ /^(CARD|DCRD|CHEK|DCHK|LECB|BILL|COMP|PREPAY)$/
+ $self->payby =~ /^(CARD|CHEK|LECB|BILL|COMP|PREPAY)$/
or return "Illegal payby: ". $self->payby;
$self->payby($1);
- if ( $self->payby eq 'CARD' || $self->payby eq 'DCRD' ) {
+ if ( $self->payby eq 'CARD' ) {
my $payinfo = $self->payinfo;
$payinfo =~ s/\D//g;
@@ -813,7 +804,7 @@ sub check {
}
}
- } elsif ( $self->payby eq 'CHEK' || $self->payby eq 'DCHK' ) {
+ } elsif ( $self->payby eq 'CHEK' ) {
my $payinfo = $self->payinfo;
$payinfo =~ s/[^\d\@]//g;
@@ -866,24 +857,17 @@ sub check {
unless $self->payby =~ /^(BILL|PREPAY|CHEK|LECB)$/;
$self->paydate('');
} else {
- my( $m, $y );
- if ( $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/ ) {
- ( $m, $y ) = ( $1, length($2) == 4 ? $2 : "20$2" );
- } elsif ( $self->paydate =~ /^(20)?(\d{2})[\/\-](\d{2})[\/\-]\d+$/ ) {
- ( $m, $y ) = ( $3, "20$2" );
- } else {
- return "Illegal expiration date: ". $self->paydate;
- }
- $self->paydate("$y-$m-01");
+ $self->paydate =~ /^(\d{1,2})[\/\-](\d{2}(\d{2})?)$/
+ or return "Illegal expiration date: ". $self->paydate;
+ my $y = length($2) == 4 ? $2 : "20$2";
+ $self->paydate("$y-$1-01");
my($nowm,$nowy)=(localtime(time))[4,5]; $nowm++; $nowy+=1900;
return gettext('expired_card')
if !$import && ( $y<$nowy || ( $y==$nowy && $1<$nowm ) );
}
- if ( $self->payname eq '' && $self->payby !~ /^(CHEK|DCHK)$/ &&
- ( ! $conf->exists('require_cardname')
- || $self->payby !~ /^(CARD|DCRD)$/ )
- ) {
+ if ( $self->payname eq '' && $self->payby ne 'CHEK' &&
+ ( ! $conf->exists('require_cardname') || $self->payby ne 'CARD' ) ) {
$self->payname( $self->first. " ". $self->getfield('last') );
} else {
$self->payname =~ /^([\w \,\.\-\']+)$/
@@ -898,7 +882,7 @@ sub check {
#warn "AFTER: \n". $self->_dump;
- $self->SUPER::check;
+ ''; #no error
}
=item all_pkgs
@@ -1102,8 +1086,6 @@ sub bill {
my %hash = $cust_pkg->hash;
my $old_cust_pkg = new FS::cust_pkg \%hash;
- my @details = ();
-
# bill setup
my $setup = 0;
if ( !$cust_pkg->setup || $options{'resetup'} ) {
@@ -1211,12 +1193,11 @@ sub bill {
}
if ( $setup != 0 || $recur != 0 ) {
my $cust_bill_pkg = new FS::cust_bill_pkg ({
- 'pkgnum' => $cust_pkg->pkgnum,
- 'setup' => $setup,
- 'recur' => $recur,
- 'sdate' => $sdate,
- 'edate' => $cust_pkg->bill,
- 'details' => \@details,
+ 'pkgnum' => $cust_pkg->pkgnum,
+ 'setup' => $setup,
+ 'recur' => $recur,
+ 'sdate' => $sdate,
+ 'edate' => $cust_pkg->bill,
});
push @cust_bill_pkg, $cust_bill_pkg;
$total_setup += $setup;
@@ -1257,7 +1238,7 @@ sub bill {
join('/', ( map $self->$_(), qw(state county country) ),
$part_pkg->taxclass ). "\n";
}
-
+
foreach my $tax ( @taxes ) {
my $taxable_charged = 0;
@@ -1424,9 +1405,8 @@ sub bill {
(Attempt to) collect money for this customer's outstanding invoices (see
L<FS::cust_bill>). Usually used after the bill method.
-Depending on the value of `payby', this may print or email an invoice (I<BILL>,
-I<DCRD>, or I<DCHK>), charge a credit card (I<CARD>), charge via electronic
-check/ACH (I<CHEK>), or just add any necessary (pseudo-)payment (I<COMP>).
+Depending on the value of `payby', this may print an invoice (`BILL'), charge
+a credit card (`CARD'), or just add any necessary (pseudo-)payment (`COMP').
Most actions are now triggered by invoice events; see L<FS::part_bill_event>
and the invoice events web interface.
@@ -1526,7 +1506,10 @@ sub collect {
my $error;
{
- local $realtime_bop_decline_quiet = 1 if $options{'quiet'};
+ #supress "used only once" warning
+ $FS::cust_bill::realtime_bop_decline_quiet += 0;
+ local $FS::cust_bill::realtime_bop_decline_quiet = 1
+ if $options{'quiet'};
$error = eval $part_bill_event->eventcode;
}
@@ -1630,268 +1613,6 @@ sub retry_realtime {
}
-=item realtime_bop METHOD AMOUNT [ OPTION => VALUE ... ]
-
-Runs a realtime credit card, ACH (electronic check) or phone bill transaction
-via a Business::OnlinePayment realtime gateway. See
-L<http://420.am/business-onlinepayment> for supported gateways.
-
-Available methods are: I<CC>, I<ECHECK> and I<LEC>
-
-Available options are: I<description>, I<invnum>, I<quiet>
-
-The additional options I<payname>, I<address1>, I<address2>, I<city>, I<state>,
-I<zip>, I<payinfo> and I<paydate> are also available. Any of these options,
-if set, will override the value from the customer record.
-
-I<description> is a free-text field passed to the gateway. It defaults to
-"Internet services".
-
-If an I<invnum> is specified, this payment (if sucessful) is applied to the
-specified invoice. If you don't specify an I<invnum> you might want to
-call the B<apply_payments> method.
-
-I<quiet> can be set true to surpress email decline notices.
-
-(moved from cust_bill) (probably should get realtime_{card,ach,lec} here too)
-
-=cut
-
-sub realtime_bop {
- my( $self, $method, $amount, %options ) = @_;
- if ( $Debug ) {
- warn "$self $method $amount\n";
- warn " $_ => $options{$_}\n" foreach keys %options;
- }
-
- $options{'description'} ||= 'Internet services';
-
- #pre-requisites
- die "Real-time processing not enabled\n"
- unless $conf->exists('business-onlinepayment');
- eval "use Business::OnlinePayment";
- die $@ if $@;
-
- #overrides
- $self->set( $_ => $options{$_} )
- foreach grep { exists($options{$_}) }
- qw( payname address1 address2 city state zip payinfo paydate );
-
- #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*$/;
-
- #massage data
-
- my $address = $self->address1;
- $address .= ", ". $self->address2 if $self->address2;
-
- my($payname, $payfirst, $paylast);
- if ( $self->payname && $method ne 'ECHECK' ) {
- $payname = $self->payname;
- $payname =~ /^\s*([\w \,\.\-\']*)?\s+([\w\,\.\-\']+)\s*$/
- or return "Illegal payname $payname";
- ($payfirst, $paylast) = ($1, $2);
- } else {
- $payfirst = $self->getfield('first');
- $paylast = $self->getfield('last');
- $payname = "$payfirst $paylast";
- }
-
- my @invoicing_list = grep { $_ ne 'POST' } $self->invoicing_list;
- if ( $conf->exists('emailinvoiceauto')
- || ( $conf->exists('emailinvoiceonly') && ! @invoicing_list ) ) {
- push @invoicing_list, $self->all_emails;
- }
- my $email = $invoicing_list[0];
-
- my %content;
- if ( $method eq 'CC' ) {
-
- $content{card_number} = $self->payinfo;
- $self->paydate =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
- $content{expiration} = "$2/$1";
-
- $content{cvv2} = $self->paycvv
- if defined $self->dbdef_table->column('paycvv')
- && length($self->paycvv);
-
- $content{recurring_billing} = 'YES'
- if qsearch('cust_pay', { 'custnum' => $self->custnum,
- 'payby' => 'CARD',
- 'payinfo' => $self->payinfo, } );
-
- } elsif ( $method eq 'ECHECK' ) {
- my($account_number,$routing_code) = $self->payinfo;
- ( $content{account_number}, $content{routing_code} ) =
- split('@', $self->payinfo);
- $content{bank_name} = $self->payname;
- $content{account_type} = 'CHECKING';
- $content{account_name} = $payname;
- $content{customer_org} = $self->company ? 'B' : 'I';
- $content{customer_ssn} = $self->ss;
- } elsif ( $method eq 'LEC' ) {
- $content{phone} = $self->payinfo;
- }
-
- #transaction(s)
-
- my( $action1, $action2 ) = split(/\s*\,\s*/, $action );
-
- my $transaction =
- new Business::OnlinePayment( $processor, @bop_options );
- $transaction->content(
- 'type' => $method,
- 'login' => $login,
- 'password' => $password,
- 'action' => $action1,
- 'description' => $options{'description'},
- 'amount' => $amount,
- 'invoice_number' => $options{'invnum'},
- 'customer_id' => $self->custnum,
- 'last_name' => $paylast,
- 'first_name' => $payfirst,
- 'name' => $payname,
- 'address' => $address,
- 'city' => $self->city,
- 'state' => $self->state,
- 'zip' => $self->zip,
- 'country' => $self->country,
- 'referer' => 'http://cleanwhisker.420.am/',
- 'email' => $email,
- 'phone' => $self->daytime || $self->night,
- %content, #after
- );
- $transaction->submit();
-
- if ( $transaction->is_success() && $action2 ) {
- my $auth = $transaction->authorization;
- my $ordernum = $transaction->can('order_number')
- ? $transaction->order_number
- : '';
-
- my $capture =
- new Business::OnlinePayment( $processor, @bop_options );
-
- my %capture = (
- %content,
- type => $method,
- action => $action2,
- login => $login,
- password => $password,
- order_number => $ordernum,
- amount => $amount,
- authorization => $auth,
- description => $options{'description'},
- );
-
- foreach my $field (qw( authorization_source_code returned_ACI transaction_identifier validation_code
- transaction_sequence_num local_transaction_date
- local_transaction_time AVS_result_code )) {
- $capture{$field} = $transaction->$field() if $transaction->can($field);
- }
-
- $capture->content( %capture );
-
- $capture->submit();
-
- unless ( $capture->is_success ) {
- my $e = "Authorization sucessful but capture failed, custnum #".
- $self->custnum. ': '. $capture->result_code.
- ": ". $capture->error_message;
- warn $e;
- return $e;
- }
-
- }
-
- #remove paycvv after initial transaction
- #make this disable-able via a config option if anyone insists?
- # (though that probably violates cardholder agreements)
- if ( defined $self->dbdef_table->column('paycvv')
- && length($self->paycvv)
- && ! grep { $_ eq cardtype($self->payinfo) } $conf->config('cvv-save')
- ) {
- my $new = new FS::cust_main { $self->hash };
- $new->paycvv('');
- my $error = $new->replace($self);
- if ( $error ) {
- warn "error removing cvv: $error\n";
- }
- }
-
- #result handling
- if ( $transaction->is_success() ) {
-
- my %method2payby = (
- 'CC' => 'CARD',
- 'ECHECK' => 'CHEK',
- 'LEC' => 'LECB',
- );
-
- my $cust_pay = new FS::cust_pay ( {
- 'custnum' => $self->custnum,
- 'invnum' => $options{'invnum'},
- 'paid' => $amount,
- '_date' => '',
- 'payby' => $method2payby{$method},
- 'payinfo' => $self->payinfo,
- 'paybatch' => "$processor:". $transaction->authorization,
- } );
- my $error = $cust_pay->insert;
- if ( $error ) {
- # gah, even with transactions.
- my $e = 'WARNING: Card/ACH debited but database not updated - '.
- 'error applying payment, invnum #' . $self->invnum.
- " ($processor): $error";
- warn $e;
- return $e;
- } else {
- return '';
- }
-
- } else {
-
- my $perror = "$processor error: ". $transaction->error_message;
-
- if ( !$options{'quiet'} && !$realtime_bop_decline_quiet
- && $conf->exists('emaildecline')
- && grep { $_ ne 'POST' } $self->invoicing_list
- && ! grep { $_ eq $transaction->error_message }
- $conf->config('emaildecline-exclude')
- ) {
- my @templ = $conf->config('declinetemplate');
- my $template = new Text::Template (
- TYPE => 'ARRAY',
- SOURCE => [ map "$_\n", @templ ],
- ) or return "($perror) can't create template: $Text::Template::ERROR";
- $template->compile()
- or return "($perror) can't compile template: $Text::Template::ERROR";
-
- my $templ_hash = { error => $transaction->error_message };
-
- my $error = send_email(
- 'from' => $conf->config('invoice_from'),
- 'to' => [ grep { $_ ne 'POST' } $self->invoicing_list ],
- 'subject' => 'Your payment could not be processed',
- 'body' => [ $template->fill_in(HASH => $templ_hash) ],
- );
-
- $perror .= " (also received error sending decline notification: $error)"
- if $error;
-
- }
-
- return $perror;
- }
-
-}
-
=item total_owed
Returns the total owed for this customer on all invoices
diff --git a/FS/FS/cust_main_county.pm b/FS/FS/cust_main_county.pm
index 76c982ae8..c124f960b 100644
--- a/FS/FS/cust_main_county.pm
+++ b/FS/FS/cust_main_county.pm
@@ -61,8 +61,6 @@ currently supported:
=item exempt_amount
-=item taxname - if defined, printed on invoices instead of "Tax"
-
=item setuptax - if 'Y', this tax does not apply to setup fees
=item recurtax - if 'Y', this tax does not apply to recurring fees
@@ -119,8 +117,7 @@ sub check {
|| $self->ut_textn('taxname')
|| $self->ut_enum('setuptax', [ '', 'Y' ] )
|| $self->ut_enum('recurtax', [ '', 'Y' ] )
- || $self->SUPER::check
- ;
+ ;
}
diff --git a/FS/FS/cust_main_invoice.pm b/FS/FS/cust_main_invoice.pm
index add0ccab1..a5533a088 100644
--- a/FS/FS/cust_main_invoice.pm
+++ b/FS/FS/cust_main_invoice.pm
@@ -107,7 +107,7 @@ sub check {
return "Unknown customer"
unless qsearchs('cust_main',{ 'custnum' => $self->custnum });
- $self->SUPER::check;
+ ''; #noerror
}
=item checkdest
@@ -134,6 +134,13 @@ sub checkdest {
unless qsearchs( 'svc_acct', { 'svcnum' => $self->dest } );
} elsif ( $self->dest =~ /^([\w\.\-\&\+]+)\@(([\w\.\-]+\.)+\w+)$/ ) {
my($user, $domain) = ($1, $2);
+# if ( $domain eq $mydomain ) {
+# my $svc_acct = qsearchs( 'svc_acct', { 'username' => $user } );
+# return "Unknown local account: $user\@$domain (specified literally)"
+# unless $svc_acct;
+# $svc_acct->svcnum =~ /^(\d+)$/ or die "Non-numeric svcnum?!";
+# $self->dest($1);
+# }
$self->dest("$1\@$2");
} else {
return gettext("illegal_email_invoice_address");
@@ -163,7 +170,7 @@ sub address {
=head1 VERSION
-$Id: cust_main_invoice.pm,v 1.14 2003-08-05 00:20:42 khoff Exp $
+$Id: cust_main_invoice.pm,v 1.12 2002-04-12 13:22:02 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/cust_pay.pm b/FS/FS/cust_pay.pm
index e1943ae2d..1afd22a43 100644
--- a/FS/FS/cust_pay.pm
+++ b/FS/FS/cust_pay.pm
@@ -1,12 +1,13 @@
package FS::cust_pay;
use strict;
-use vars qw( @ISA $conf $unsuspendauto );
+use vars qw( @ISA $conf $unsuspendauto $smtpmachine $invoice_from );
use Date::Format;
+use Mail::Header;
+use Mail::Internet 1.44;
use Business::CreditCard;
use FS::UID qw( dbh );
use FS::Record qw( dbh qsearch qsearchs dbh );
-use FS::Misc qw(send_email);
use FS::cust_bill;
use FS::cust_bill_pay;
use FS::cust_main;
@@ -14,10 +15,14 @@ use FS::cust_main;
@ISA = qw( FS::Record );
#ask FS::UID to run this stuff for us later
-FS::UID->install_callback( sub {
+$FS::UID::callback{'FS::cust_pay'} = sub {
+
$conf = new FS::Conf;
$unsuspendauto = $conf->exists('unsuspendauto');
-} );
+ $smtpmachine = $conf->config('smtpmachine');
+ $invoice_from = $conf->config('invoice_from');
+
+};
=head1 NAME
@@ -260,12 +265,19 @@ sub delete {
if ( $conf->config('deletepayments') ne '' ) {
my $cust_main = qsearchs('cust_main',{ 'custnum' => $self->custnum });
-
- my $error = send_email(
- 'from' => $conf->config('invoice_from'), #??? well as good as any
- 'to' => $conf->config('deletepayments'),
- 'subject' => 'FREESIDE NOTIFICATION: Payment deleted',
- 'body' => [
+ #false laziness w/FS::cust_bill::send & fs_signup_server
+ $ENV{MAILADDRESS} = $invoice_from; #??? well as good as any
+ my $header = new Mail::Header ( [
+ "From: $invoice_from",
+ "To: ". $conf->config('deletepayments'),
+ "Sender: $invoice_from",
+ "Reply-To: $invoice_from",
+ "Date: ". time2str("%a, %d %b %Y %X %z", time),
+ "Subject: FREESIDE NOTIFICATION: Payment deleted",
+ ] );
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [
"This is an automatic message from your Freeside installation\n",
"informing you that the following payment has been deleted:\n",
"\n",
@@ -279,12 +291,16 @@ sub delete {
'paybatch: '. $self->paybatch. "\n",
],
);
-
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "can't send payment deletion notification: $error";
- }
-
+ $!=0;
+ $message->smtpsend( Host => $smtpmachine )
+ or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
+ or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "(customer # ". $self->custnum.
+ ") can't send payment deletion email to ".
+ $conf->config('deletepayments').
+ " via server $smtpmachine with SMTP: $!";
+ };
}
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
@@ -354,7 +370,8 @@ sub check {
return $error if $error;
}
- $self->SUPER::check;
+ ''; #no error
+
}
=item cust_bill_pay
@@ -401,7 +418,7 @@ sub cust_main {
=head1 VERSION
-$Id: cust_pay.pm,v 1.26 2003-09-10 10:54:46 ivan Exp $
+$Id: cust_pay.pm,v 1.21.4.3 2003-09-10 10:54:47 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/cust_pay_batch.pm b/FS/FS/cust_pay_batch.pm
index 8059f1ca2..b58e772ce 100644
--- a/FS/FS/cust_pay_batch.pm
+++ b/FS/FS/cust_pay_batch.pm
@@ -185,7 +185,7 @@ sub check {
#check invnum, custnum, ?
- $self->SUPER::check;
+ ''; #no error
}
=item cust_main
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 5700b654e..455a3805f 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -1,10 +1,9 @@
package FS::cust_pkg;
use strict;
-use vars qw(@ISA $disable_agentcheck);
+use vars qw(@ISA $disable_agentcheck $DEBUG);
use FS::UID qw( getotaker dbh );
use FS::Record qw( qsearch qsearchs );
-use FS::Misc qw( send_email );
use FS::cust_svc;
use FS::part_pkg;
use FS::cust_main;
@@ -16,15 +15,22 @@ use FS::cust_bill_pkg;
# setup }
# because they load configuraion by setting FS::UID::callback (see TODO)
use FS::svc_acct;
+use FS::svc_acct_sm;
use FS::svc_domain;
use FS::svc_www;
use FS::svc_forward;
-# for sending cancel emails in sub cancel
+# need all this for sending cancel emails in sub cancel
+
use FS::Conf;
+use Date::Format;
+use Mail::Internet 1.44;
+use Mail::Header;
@ISA = qw( FS::Record );
+$DEBUG = 0;
+
$disable_agentcheck = 0;
sub _cache {
@@ -99,8 +105,6 @@ inherits from FS::Record. The following fields are currently supported:
=item bill - date (next bill date)
-=item last_bill - last bill date
-
=item susp - date
=item expire - date
@@ -250,7 +254,7 @@ sub check {
$self->manual_flag($1);
}
- $self->SUPER::check;
+ ''; #no error
}
=item cancel [ OPTION => VALUE ... ]
@@ -308,16 +312,38 @@ sub cancel {
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
my $conf = new FS::Conf;
- my @invoicing_list = grep { $_ ne 'POST' } $self->cust_main->invoicing_list;
- if ( !$options{'quiet'} && $conf->exists('emailcancel') && @invoicing_list ) {
- my $conf = new FS::Conf;
- my $error = send_email(
- 'from' => $conf->config('invoice_from'),
- 'to' => \@invoicing_list,
- 'subject' => $conf->config('cancelsubject'),
- 'body' => [ map "$_\n", $conf->config('cancelmessage') ],
- );
- #should this do something on errors?
+
+ if ( !$options{'quiet'} && $conf->exists('emailcancel')
+ && grep { $_ ne 'POST' } $self->cust_main->invoicing_list) {
+
+ my @invoicing_list = $self->cust_main->invoicing_list;
+
+ my $invoice_from = $conf->config('invoice_from');
+ my @print_text = map "$_\n", $conf->config('cancelmessage');
+ my $subject = $conf->config('cancelsubject');
+ my $smtpmachine = $conf->config('smtpmachine');
+
+ if ( grep { $_ ne 'POST' } @invoicing_list ) { #email invoice
+ #false laziness w/FS::cust_pay::delete & fs_signup_server && ::realtime_card
+ #$ENV{SMTPHOSTS} = $smtpmachine;
+ $ENV{MAILADDRESS} = $invoice_from;
+ my $header = new Mail::Header ( [
+ "From: $invoice_from",
+ "To: ". join(', ', grep { $_ ne 'POST' } @invoicing_list ),
+ "Sender: $invoice_from",
+ "Reply-To: $invoice_from",
+ "Date: ". time2str("%a, %d %b %Y %X %z", time),
+ "Subject: $subject",
+ ] );
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [ @print_text ],
+ );
+ $!=0;
+ $message->smtpsend( Host => $smtpmachine )
+ or $message->smtpsend( Host => $smtpmachine, Debug => 1 );
+ #should this return an error?
+ }
}
''; #no errors
@@ -459,8 +485,8 @@ Useful for billing metered services.
sub last_bill {
my $self = shift;
- if ( $self->dbdef_table->column('last_bill') ) {
- return $self->setfield('last_bill', $_[0]) if @_;
+ if ( $self->dbdef_table->column('manual_flag') ) {
+ return $self->setfield('last_bill', $_[1]) if @_;
return $self->getfield('last_bill') if $self->getfield('last_bill');
}
my $cust_bill_pkg = qsearchs('cust_bill_pkg', { 'pkgnum' => $self->pkgnum,
@@ -582,8 +608,7 @@ sub seconds_since_sqlradacct {
Returns the sum of the given attribute for all accounts (see L<FS::svc_acct>)
in this package for sessions ending between TIMESTAMP_START (inclusive) and
-TIMESTAMP_END
-(exclusive).
+TIMESTAMP_END (exclusive).
TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see
L<perlfunc/"time">. Also see L<Time::Local> and L<Date::Parse> for conversion
@@ -610,61 +635,6 @@ sub attribute_since_sqlradacct {
}
-=item transfer DEST_PKGNUM
-
-Transfers as many services as possible from this package to another package.
-The destination package must already exist. Services are moved only if
-the destination allows services with the correct I<svcnum> (not svcdb).
-Any services that can't be moved remain in the original package.
-
-Returns an error, if there is one; otherwise, returns the number of services
-that couldn't be moved.
-
-=cut
-
-sub transfer {
- my ($self, $dest_pkgnum) = @_;
-
- my $remaining = 0;
- my $dest;
- my %target;
- my $pkg_svc;
-
- if (ref ($dest_pkgnum) eq 'FS::cust_pkg') {
- $dest = $dest_pkgnum;
- $dest_pkgnum = $dest->pkgnum;
- } else {
- $dest = qsearchs('cust_pkg', { pkgnum => $dest_pkgnum });
- }
-
- return ('Package does not exist: '.$dest_pkgnum) unless $dest;
-
- foreach $pkg_svc (qsearch('pkg_svc', { pkgpart => $dest->pkgpart })) {
- $target{$pkg_svc->svcpart} = $pkg_svc->quantity;
- }
-
- my $cust_svc;
-
- foreach $cust_svc ($dest->cust_svc) {
- $target{$cust_svc->svcpart}--;
- }
-
- foreach $cust_svc ($self->cust_svc) {
- if($target{$cust_svc->svcpart} > 0) {
- $target{$cust_svc->svcpart}--;
- my $new = new FS::cust_svc {
- svcnum => $cust_svc->svcnum,
- svcpart => $cust_svc->svcpart,
- pkgnum => $dest_pkgnum };
- my $error = $new->replace($cust_svc);
- return $error if $error;
- } else {
- $remaining++
- }
- }
- return $remaining;
-}
-
=item reexport
=cut
@@ -726,62 +696,186 @@ newly-created cust_pkg objects.
=cut
sub order {
+ my($custnum, $pkgparts, $remove_pkgnums, $return_cust_pkg) = @_;
+ $remove_pkgnums = [] unless defined($remove_pkgnums);
- # Rewritten to make use of the transfer() method, and in general
- # to not suck so badly.
-
- my ($custnum, $pkgparts, $remove_pkgnum, $return_cust_pkg) = @_;
-
- # Transactionize this whole mess
my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- my $error;
- my $cust_main = qsearchs('cust_main', { custnum => $custnum });
- return "Customer not found: $custnum" unless $cust_main;
-
- # Create the new packages.
- my $cust_pkg;
- foreach (@$pkgparts) {
- $cust_pkg = new FS::cust_pkg { custnum => $custnum,
- pkgpart => $_ };
- $error = $cust_pkg->insert;
- if ($error) {
+ # generate %part_pkg
+ # $part_pkg{$pkgpart} is true iff $custnum may purchase $pkgpart
+ #
+ my($cust_main)=qsearchs('cust_main',{'custnum'=>$custnum});
+ my($agent)=qsearchs('agent',{'agentnum'=> $cust_main->agentnum });
+ my %part_pkg = %{ $agent->pkgpart_hashref };
+
+ my(%svcnum);
+ # generate %svcnum
+ # for those packages being removed:
+ #@{ $svcnum{$svcpart} } goes from a svcpart to a list of FS::cust_svc objects
+ my($pkgnum);
+ foreach $pkgnum ( @{$remove_pkgnums} ) {
+ foreach my $cust_svc (qsearch('cust_svc',{'pkgnum'=>$pkgnum})) {
+ push @{ $svcnum{$cust_svc->getfield('svcpart')} }, $cust_svc;
+ }
+ }
+ if ( $DEBUG ) {
+ foreach my $svcpart ( keys %svcnum ) {
+ warn "initial svcpart $svcpart: existing svcnums ".
+ join(', ', map { $_->svcnum } @{$svcnum{$svcpart}} ). "\n";
+ }
+ }
+
+ my @cust_svc;
+ #generate @cust_svc
+ # for those packages the customer is purchasing:
+ # @{$pkgparts} is a list of said packages, by pkgpart
+ # @cust_svc is a corresponding list of lists of FS::Record objects
+ foreach my $pkgpart ( @{$pkgparts} ) {
+ unless ( $part_pkg{$pkgpart} ) {
$dbh->rollback if $oldAutoCommit;
- return $error;
+ return "Customer not permitted to purchase pkgpart $pkgpart!";
}
- push @$return_cust_pkg, $cust_pkg;
+ push @cust_svc, [
+ map {
+ my $svcnum = $svcnum{$_->{svcpart}};
+ if ( $svcnum && @$svcnum ) {
+ my $num = ( $_->{quantity} < scalar(@$svcnum) )
+ ? $_->{quantity}
+ : scalar(@$svcnum);
+ splice @$svcnum, 0, $num;
+ } else {
+ ();
+ }
+ } map { { 'svcpart' => $_->svcpart,
+ 'quantity' => $_->quantity } }
+ qsearch('pkg_svc', { pkgpart => $pkgpart,
+ quantity => { op=>'>', value=>'0', } } )
+ ];
}
- # $return_cust_pkg now contains refs to all of the newly
- # created packages.
-
- # Transfer services and cancel old packages.
- foreach my $old_pkgnum (@$remove_pkgnum) {
- my $old_pkg = qsearchs ('cust_pkg', { pkgnum => $old_pkgnum });
- foreach my $new_pkg (@$return_cust_pkg) {
- $error = $old_pkg->transfer($new_pkg);
- if ($error and $error == 0) {
- # $old_pkg->transfer failed.
- $dbh->rollback if $oldAutoCommit;
- return $error;
+
+ if ( $DEBUG ) {
+ foreach my $svcpart ( keys %svcnum ) {
+ warn "after regular move svcpart $svcpart: existing svcnums ".
+ join(', ', map { $_->svcnum } @{$svcnum{$svcpart}} ). "\n";
+ }
+ }
+
+ #special-case until this can be handled better
+ # move services to new svcparts - even if the svcparts don't match (svcdb
+ # needs to...)
+ # looks like they're moved in no particular order, ewwwwwwww
+ # and looks like just one of each svcpart can be moved... o well
+
+ #start with still-leftover services
+ #foreach my $svcpart ( grep { scalar(@{ $svcnum{$_} }) } keys %svcnum ) {
+ foreach my $svcpart ( keys %svcnum ) {
+ next unless @{ $svcnum{$svcpart} };
+
+ my $svcdb = $svcnum{$svcpart}->[0]->part_svc->svcdb;
+
+ #find an empty place to put one
+ my $i = 0;
+ foreach my $pkgpart ( @{$pkgparts} ) {
+ my @pkg_svc =
+ qsearch('pkg_svc', { pkgpart => $pkgpart,
+ quantity => { op=>'>', value=>'0', } } );
+ #my @pkg_svc =
+ # grep { $_->quantity > 0 } qsearch('pkg_svc', { pkgpart=>$pkgpart } );
+ if ( ! @{$cust_svc[$i]} #find an empty place to put them with
+ && grep { $svcdb eq $_->part_svc->svcdb } #with appropriate svcdb
+ @pkg_svc
+ ) {
+ my $new_svcpart =
+ ( grep { $svcdb eq $_->part_svc->svcdb } @pkg_svc )[0]->svcpart;
+ my $cust_svc = shift @{$svcnum{$svcpart}};
+ $cust_svc->svcpart($new_svcpart);
+ #warn "changing from $svcpart to $new_svcpart!!!\n";
+ $cust_svc[$i] = [ $cust_svc ];
}
+ $i++;
+ }
+
+ }
+
+ if ( $DEBUG ) {
+ foreach my $svcpart ( keys %svcnum ) {
+ warn "after special-case move svcpart $svcpart: existing svcnums ".
+ join(', ', map { $_->svcnum } @{$svcnum{$svcpart}} ). "\n";
}
- if ($error > 0) {
- # Transfers were successful, but we went through all of the
- # new packages and still had services left on the old package.
- # We can't cancel the package under the circumstances, so abort.
+ }
+
+
+ #check for leftover services
+ foreach (keys %svcnum) {
+ next unless @{ $svcnum{$_} };
+ $dbh->rollback if $oldAutoCommit;
+ return "Leftover services, svcpart $_: svcnum ".
+ join(', ', map { $_->svcnum } @{ $svcnum{$_} } );
+ }
+
+ #no leftover services, let's make changes.
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ #first cancel old packages
+ foreach my $pkgnum ( @{$remove_pkgnums} ) {
+ my($old) = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ unless ( $old ) {
$dbh->rollback if $oldAutoCommit;
- return "Unable to transfer all services from package ".$old_pkg->pkgnum;
+ return "Package $pkgnum not found to remove!";
}
- $error = $old_pkg->cancel;
- if ($error) {
- $dbh->rollback;
- return $error;
+ my(%hash) = $old->hash;
+ $hash{'cancel'}=time;
+ my($new) = new FS::cust_pkg ( \%hash );
+ my($error)=$new->replace($old);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Couldn't update package $pkgnum: $error";
}
}
+
+ #now add new packages, changing cust_svc records if necessary
+ my $pkgpart;
+ while ($pkgpart=shift @{$pkgparts} ) {
+
+ my $new = new FS::cust_pkg {
+ 'custnum' => $custnum,
+ 'pkgpart' => $pkgpart,
+ };
+ my $error = $new->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Couldn't insert new cust_pkg record: $error";
+ }
+ push @{$return_cust_pkg}, $new if $return_cust_pkg;
+ my $pkgnum = $new->pkgnum;
+
+ foreach my $cust_svc ( @{ shift @cust_svc } ) {
+ my(%hash) = $cust_svc->hash;
+ $hash{'pkgnum'}=$pkgnum;
+ my $new = new FS::cust_svc ( \%hash );
+
+ #avoid Record diffing missing changed svcpart field from above.
+ my $old = qsearchs('cust_svc', { 'svcnum' => $cust_svc->svcnum } );
+
+ my $error = $new->replace($old);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Couldn't link old service to new package: $error";
+ }
+ }
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
- '';
+
+ ''; #no errors
}
=back
@@ -795,12 +889,11 @@ In sub order, the @pkgparts array (passed by reference) is clobbered.
Also in sub order, no money is adjusted. Once FS::part_pkg defines a standard
method to pass dates to the recur_prog expression, it should do so.
-FS::svc_acct, FS::svc_domain, FS::svc_www, FS::svc_ip and FS::svc_forward are
-loaded via 'use' at compile time, rather than via 'require' in sub { setup,
-suspend, unsuspend, cancel } because they use %FS::UID::callback to load
-configuration values. Probably need a subroutine which decides what to do
-based on whether or not we've fetched the user yet, rather than a hash. See
-FS::UID and the TODO.
+FS::svc_acct, FS::svc_acct_sm, and FS::svc_domain are loaded via 'use' at
+compile time, rather than via 'require' in sub { setup, suspend, unsuspend,
+cancel } because they use %FS::UID::callback to load configuration values.
+Probably need a subroutine which decides what to do based on whether or not
+we've fetched the user yet, rather than a hash. See FS::UID and the TODO.
Now that things are transactional should the check in the insert method be
moved to check ?
diff --git a/FS/FS/cust_refund.pm b/FS/FS/cust_refund.pm
index 250bd20e0..aa81003b1 100644
--- a/FS/FS/cust_refund.pm
+++ b/FS/FS/cust_refund.pm
@@ -260,14 +260,14 @@ sub check {
$self->otaker(getotaker);
- $self->SUPER::check;
+ ''; #no error
}
=back
=head1 VERSION
-$Id: cust_refund.pm,v 1.21 2003-08-05 00:20:42 khoff Exp $
+$Id: cust_refund.pm,v 1.18.4.2 2002-11-19 09:52:02 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm
index ce2b969f9..91874e0d2 100644
--- a/FS/FS/cust_svc.pm
+++ b/FS/FS/cust_svc.pm
@@ -9,9 +9,9 @@ use FS::part_pkg;
use FS::part_svc;
use FS::pkg_svc;
use FS::svc_acct;
+use FS::svc_acct_sm;
use FS::svc_domain;
use FS::svc_forward;
-use FS::svc_broadband;
use FS::domain_record;
use FS::part_export;
@@ -234,7 +234,7 @@ sub check {
if scalar(@cust_svc) >= $quantity && !$ignore_quantity;
}
- $self->SUPER::check;
+ ''; #no error
}
=item part_svc
@@ -280,6 +280,11 @@ sub label {
my $tag;
if ( $svcdb eq 'svc_acct' ) {
$tag = $svc_x->email;
+ } elsif ( $svcdb eq 'svc_acct_sm' ) {
+ my $domuser = $svc_x->domuser eq '*' ? '(anything)' : $svc_x->domuser;
+ my $svc_domain = qsearchs ( 'svc_domain', { 'svcnum' => $svc_x->domsvc } );
+ my $domain = $svc_domain->domain;
+ $tag = "$domuser\@$domain";
} elsif ( $svcdb eq 'svc_forward' ) {
my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->srcsvc } );
$tag = $svc_acct->email. '->';
@@ -294,10 +299,6 @@ sub label {
} elsif ( $svcdb eq 'svc_www' ) {
my $domain = qsearchs( 'domain_record', { 'recnum' => $svc_x->recnum } );
$tag = $domain->zone;
- } elsif ( $svcdb eq 'svc_broadband' ) {
- $tag = $svc_x->ip_addr;
- } elsif ( $svcdb eq 'svc_external' ) {
- $tag = $svc_x->id. ': '. $svc_x->title;
} else {
cluck "warning: asked for label of unsupported svcdb; using svcnum";
$tag = $svc_x->getfield('svcnum');
diff --git a/FS/FS/cust_tax_exempt.pm b/FS/FS/cust_tax_exempt.pm
index da0de000a..ab873c0a7 100644
--- a/FS/FS/cust_tax_exempt.pm
+++ b/FS/FS/cust_tax_exempt.pm
@@ -111,7 +111,6 @@ sub check {
|| $self->ut_number('year') #check better
|| $self->ut_number('month') #check better
|| $self->ut_money('amount')
- || $self->SUPER::check
;
}
diff --git a/FS/FS/domain_record.pm b/FS/FS/domain_record.pm
index ea0c48d4f..dd16675fb 100644
--- a/FS/FS/domain_record.pm
+++ b/FS/FS/domain_record.pm
@@ -241,7 +241,7 @@ sub check {
if ( $self->rectype eq 'SOA' ) {
my $recdata = $self->recdata;
$recdata =~ s/\s+/ /g;
- $recdata =~ /^([a-z0-9\.\-]+ [\w\-\+]+\.[a-z0-9\.\-]+ \( ((\d+|((\d+[WDHMS])+)) ){5}\))$/i
+ $recdata =~ /^([a-z0-9\.\-]+ [\w\-\+]+\.[a-z0-9\.\-]+ \( (\d+ ){5}\))$/i
or return "Illegal data for SOA record: $recdata";
$self->recdata($1);
} elsif ( $self->rectype eq 'NS' ) {
@@ -271,7 +271,7 @@ sub check {
die "ack!";
}
- $self->SUPER::check;
+ ''; #no error
}
=item increment_serial
@@ -332,7 +332,7 @@ sub zone {
=head1 VERSION
-$Id: domain_record.pm,v 1.16 2003-08-05 00:20:43 khoff Exp $
+$Id: domain_record.pm,v 1.11.4.2 2003-03-29 04:52:35 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/export_svc.pm b/FS/FS/export_svc.pm
index c104e4538..da9ac698a 100644
--- a/FS/FS/export_svc.pm
+++ b/FS/FS/export_svc.pm
@@ -105,7 +105,6 @@ sub check {
|| $self->ut_foreign_key('exportnum', 'part_export', 'exportnum')
|| $self->ut_number('svcpart')
|| $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
- || $self->SUPER::check
;
}
diff --git a/FS/FS/msgcat.pm b/FS/FS/msgcat.pm
index 855b8b291..fa10d34fa 100644
--- a/FS/FS/msgcat.pm
+++ b/FS/FS/msgcat.pm
@@ -113,7 +113,7 @@ sub check {
$self->locale =~ /^([\w\@]+)$/ or return "illegal locale: ". $self->locale;
$self->locale($1);
- $self->SUPER::check
+ ''; #no error
}
=back
diff --git a/FS/FS/nas.pm b/FS/FS/nas.pm
index 2d17df899..58c6827ea 100644
--- a/FS/FS/nas.pm
+++ b/FS/FS/nas.pm
@@ -114,9 +114,7 @@ sub check {
|| $self->ut_text('nas')
|| $self->ut_ip('nasip')
|| $self->ut_domain('nasfqdn')
- || $self->ut_numbern('last')
- || $self->SUPER::check
- ;
+ || $self->ut_numbern('last');
}
=item heartbeat TIMESTAMP
@@ -138,7 +136,7 @@ sub heartbeat {
=head1 VERSION
-$Id: nas.pm,v 1.7 2003-08-05 00:20:43 khoff Exp $
+$Id: nas.pm,v 1.6 2002-03-04 12:48:49 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/part_bill_event.pm b/FS/FS/part_bill_event.pm
index 86f929424..2638328ea 100644
--- a/FS/FS/part_bill_event.pm
+++ b/FS/FS/part_bill_event.pm
@@ -37,7 +37,7 @@ FS::Record. The following fields are currently supported:
=item eventpart - primary key
-=item payby - CARD, DCRD, CHEK, DCHK, LECB, BILL, or COMP
+=item payby - CARD, CHEK, LECB, BILL, or COMP
=item event - event name
@@ -140,7 +140,7 @@ sub check {
}
my $error = $self->ut_numbern('eventpart')
- || $self->ut_enum('payby', [qw( CARD DCRD CHEK DCHK LECB BILL COMP )] )
+ || $self->ut_enum('payby', [qw( CARD CHEK LECB BILL COMP )] )
|| $self->ut_text('event')
|| $self->ut_anything('eventcode')
|| $self->ut_number('seconds')
@@ -168,7 +168,8 @@ sub check {
}
}
- $self->SUPER::check;
+ '';
+
}
=back
diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm
index a27213773..2615e645a 100644
--- a/FS/FS/part_export.pm
+++ b/FS/FS/part_export.pm
@@ -281,7 +281,7 @@ sub check {
#check exporttype?
- $self->SUPER::check;
+ ''; #no error
}
#=item part_svc
@@ -303,7 +303,7 @@ sub part_svc {
=item svc_x
-Returns a list of associated FS::svc_* records.
+Returns a list of associate FS::svc_* records.
=cut
@@ -663,19 +663,6 @@ END
},
;
-tie my %router_options, 'Tie::IxHash',
- 'protocol' => {
- label=>'Protocol',
- type =>'select',
- options => [qw(telnet ssh)],
- default => 'telnet'},
- 'insert' => {label=>'Insert command', default=>'' },
- 'delete' => {label=>'Delete command', default=>'' },
- 'replace' => {label=>'Replace command', default=>'' },
- 'Timeout' => {label=>'Time to wait for prompt', default=>'20' },
- 'Prompt' => {label=>'Prompt string', default=>'#' }
-;
-
tie my %domain_shellcommands_options, 'Tie::IxHash',
'user' => { label=>'Remote username', default=>'root' },
'useradd' => { label=>'Insert command',
@@ -773,30 +760,18 @@ tie my %communigate_pro_singledomain_options, 'Tie::IxHash',
;
tie my %bind_options, 'Tie::IxHash',
- #'machine' => { label=>'named machine' },
- 'named_conf' => { label => 'named.conf location',
- default=> '/etc/bind/named.conf' },
- 'zonepath' => { label => 'path to zone files',
- default=> '/etc/bind/', },
- 'bind_release' => { label => 'ISC BIND Release',
- type => 'select',
- options => [qw(BIND8 BIND9)],
- default => 'BIND8' },
- 'bind9_minttl' => { label => 'The minttl required by bind9 and RFC1035.',
- default => '1D' },
+ #'machine' => { label=>'named machine' },
+ 'named_conf' => { label => 'named.conf location',
+ default=> '/etc/bind/named.conf' },
+ 'zonepath' => { label => 'path to zone files',
+ default=> '/etc/bind/', },
;
tie my %bind_slave_options, 'Tie::IxHash',
- #'machine' => { label=> 'Slave machine' },
- 'master' => { label=> 'Master IP address(s) (semicolon-separated)' },
- 'named_conf' => { label => 'named.conf location',
- default => '/etc/bind/named.conf' },
- 'bind_release' => { label => 'ISC BIND Release',
- type => 'select',
- options => [qw(BIND8 BIND9)],
- default => 'BIND8' },
- 'bind9_minttl' => { label => 'The minttl required by bind9 and RFC1035.',
- default => '1D' },
+ #'machine' => { label=> 'Slave machine' },
+ 'master' => { label=> 'Master IP address(s) (semicolon-separated)' },
+ 'named_conf' => { label => 'named.conf location',
+ default => '/etc/bind/named.conf' },
;
tie my %http_options, 'Tie::IxHash',
@@ -831,27 +806,9 @@ tie my %http_options, 'Tie::IxHash',
;
tie my %sqlmail_options, 'Tie::IxHash',
- 'datasrc' => { label => 'DBI data source' },
- 'username' => { label => 'Database username' },
- 'password' => { label => 'Database password' },
- 'server_type' => {
- label => 'Server type',
- type => 'select',
- options => [qw(dovecot_plain dovecot_crypt dovecot_digest_md5 courier_plain
- courier_crypt)],
- default => ['dovecot_plain'], },
- 'svc_acct_table' => { label => 'User Table', default => 'user_acct' },
- 'svc_forward_table' => { label => 'Forward Table', default => 'forward' },
- 'svc_domain_table' => { label => 'Domain Table', default => 'domain' },
- 'svc_acct_fields' => { label => 'svc_acct Export Fields',
- default => 'username _password domsvc svcnum' },
- 'svc_forward_fields' => { label => 'svc_forward Export Fields',
- default => 'domain svcnum catchall' },
- 'svc_domain_fields' => { label => 'svc_domain Export Fields',
- default => 'srcsvc dstsvc dst' },
- 'resolve_dstsvc' => { label => q{Resolve svc_forward.dstsvc to an email address and store it in dst. (Doesn't require that you also export dstsvc.)},
- type => 'checkbox' },
-
+ 'datasrc' => { label=>'DBI data source' },
+ 'username' => { label=>'Database username' },
+ 'password' => { label=>'Database password' },
;
tie my %ldap_options, 'Tie::IxHash',
@@ -959,7 +916,7 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
'sqlmail' => {
'desc' => 'Real-time export to SQL-backed mail server',
'options' => \%sqlmail_options,
- 'nodomain' => '',
+ 'nodomain' => 'Y',
'notes' => 'Database schema can be made to work with Courier IMAP and Exim. Others could work but are untested. (...extended description from pc-intouch?...)',
},
@@ -1040,6 +997,8 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
},
+ 'svc_acct_sm' => {},
+
'svc_forward' => {
'sqlmail' => {
'desc' => 'Real-time export to SQL-backed mail server',
@@ -1069,17 +1028,6 @@ tie my %forward_shellcommands_options, 'Tie::IxHash',
},
},
- 'svc_broadband' => {
- 'router' => {
- 'desc' => 'Send a command to a router.',
- 'options' => \%router_options,
- 'notes' => '',
- },
- },
-
- 'svc_external' => {
- },
-
);
=back
diff --git a/FS/FS/part_export/router.pm b/FS/FS/part_export/router.pm
deleted file mode 100644
index 1d1f907e2..000000000
--- a/FS/FS/part_export/router.pm
+++ /dev/null
@@ -1,166 +0,0 @@
-package FS::part_export::router;
-
-=head1 FS::part_export::router
-
-This export connects to a router and transmits commands via telnet or SSH.
-It requires the following custom router fields:
-
-=over 4
-
-=item admin_address - IP address (or hostname) to connect
-
-=item admin_user - username for admin access
-
-=item admin_password - password for admin access
-
-=back
-
-The export itself needs the following options:
-
-=over 4
-
-=item insert, replace, delete - command strings (to be interpolated)
-
-=item Prompt - prompt string to expect from router after successful login
-
-=item Timeout - time to wait for prompt string
-
-=back
-
-(Prompt and Timeout are required only for telnet connections.)
-
-=cut
-
-use vars qw(@ISA @saltset);
-use String::ShellQuote;
-use FS::part_export;
-
-@ISA = qw(FS::part_export);
-
-@saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' );
-
-sub rebless { shift; }
-
-sub _export_insert {
- my($self) = shift;
- $self->_export_command('insert', @_);
-}
-
-sub _export_delete {
- my($self) = shift;
- $self->_export_command('delete', @_);
-}
-
-sub _export_suspend {
- my($self) = shift;
- $self->_export_command('suspend', @_);
-}
-
-sub _export_unsuspend {
- my($self) = shift;
- $self->_export_command('unsuspend', @_);
-}
-
-sub _export_command {
- my ( $self, $action, $svc_broadband) = (shift, shift, shift);
- my $command = $self->option($action);
- return '' if $command =~ /^\s*$/;
-
- no strict 'vars';
- {
- no strict 'refs';
- ${$_} = $svc_broadband->getfield($_) foreach $svc_broadband->fields;
- }
- # fetch router info
- my $router = $svc_broadband->addr_block->router;
- my %r;
- $r{$_} = $router->getfield($_) foreach $router->virtual_fields;
- #warn qq("$command");
- #warn eval(qq("$command"));
-
- warn "admin_address: '$r{admin_address}'";
-
- if ($r{admin_address} ne '') {
- $self->router_queue( $svc_broadband->svcnum, $self->option('protocol'),
- user => $r{admin_user},
- password => $r{admin_password},
- host => $r{admin_address},
- Timeout => $self->option('Timeout'),
- Prompt => $self->option('Prompt'),
- command => eval(qq("$command")),
- );
- } else {
- return '';
- }
-}
-
-sub _export_replace {
-
- # We don't handle the case of a svc_broadband moving between routers.
- # If you want to do that, reprovision the service.
-
- my($self, $new, $old ) = (shift, shift, shift);
- my $command = $self->option('replace');
- no strict 'vars';
- {
- no strict 'refs';
- ${"old_$_"} = $old->getfield($_) foreach $old->fields;
- ${"new_$_"} = $new->getfield($_) foreach $new->fields;
- }
-
- my $router = $new->addr_block->router;
- my %r;
- $r{$_} = $router->getfield($_) foreach $router->virtual_fields;
-
- if ($r{admin_address} ne '') {
- $self->router_queue( $new->svcnum, $self->option('protocol'),
- user => $r{admin_user},
- password => $r{admin_password},
- host => $r{admin_address},
- Timeout => $self->option('Timeout'),
- Prompt => $self->option('Prompt'),
- command => eval(qq("$command")),
- );
- } else {
- return '';
- }
-}
-
-#a good idea to queue anything that could fail or take any time
-sub router_queue {
- #warn join ':', @_;
- my( $self, $svcnum, $protocol ) = (shift, shift, shift);
- my $queue = new FS::queue {
- 'svcnum' => $svcnum,
- };
- $queue->job ("FS::part_export::router::".$protocol."_cmd");
- $queue->insert( @_ );
-}
-
-sub ssh_cmd { #subroutine, not method
- use Net::SSH '0.07';
- &Net::SSH::ssh_cmd( { @_ } );
-}
-
-sub telnet_cmd {
- use Net::Telnet;
-
- warn join(', ', @_);
-
- my %arg = @_;
-
- my $t = new Net::Telnet (Timeout => $arg{Timeout},
- Prompt => $arg{Prompt});
- $t->open($arg{host});
- $t->login($arg{user}, $arg{password});
- my @error = $t->cmd($arg{command});
- die @error if (grep /^ERROR/, @error);
-}
-
-#sub router_insert { #subroutine, not method
-#}
-#sub router_replace { #subroutine, not method
-#}
-#sub router_delete { #subroutine, not method
-#}
-
diff --git a/FS/FS/part_export_option.pm b/FS/FS/part_export_option.pm
index 33b5e5a67..a0b19fde1 100644
--- a/FS/FS/part_export_option.pm
+++ b/FS/FS/part_export_option.pm
@@ -115,7 +115,7 @@ sub check {
#check options & values?
- $self->SUPER::check;
+ ''; #no error
}
=back
diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm
index dcce66b38..45760668f 100644
--- a/FS/FS/part_pkg.pm
+++ b/FS/FS/part_pkg.pm
@@ -249,7 +249,6 @@ sub check {
|| $self->ut_enum('recurtax', [ '', 'Y' ] )
|| $self->ut_textn('taxclass')
|| $self->ut_enum('disabled', [ '', 'Y' ] )
- || $self->SUPER::check
;
}
diff --git a/FS/FS/part_pop_local.pm b/FS/FS/part_pop_local.pm
index f7d5eac9a..0b7cdf6c9 100644
--- a/FS/FS/part_pop_local.pm
+++ b/FS/FS/part_pop_local.pm
@@ -92,7 +92,6 @@ sub check {
or $self->ut_text('state')
or $self->ut_number('npa')
or $self->ut_number('nxx')
- or $self->SUPER::check
;
}
@@ -101,7 +100,7 @@ sub check {
=head1 VERSION
-$Id: part_pop_local.pm,v 1.2 2003-08-05 00:20:44 khoff Exp $
+$Id: part_pop_local.pm,v 1.1 2001-09-26 09:17:06 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/part_referral.pm b/FS/FS/part_referral.pm
index c0858c0ed..90ce7fef2 100644
--- a/FS/FS/part_referral.pm
+++ b/FS/FS/part_referral.pm
@@ -103,7 +103,7 @@ sub check {
return $error if $error;
}
- $self->SUPER::check;
+ '';
}
=back
diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm
index aacc3ab48..552019acb 100644
--- a/FS/FS/part_svc.pm
+++ b/FS/FS/part_svc.pm
@@ -68,7 +68,7 @@ TODOC:
=item I<svcdb>__I<field> - Default or fixed value for I<field> in I<svcdb>.
-=item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null, `D' for default, or `F' for fixed. For virtual fields, can also be 'X' for excluded.
+=item I<svcdb>__I<field>_flag - defines I<svcdb>__I<field> action: null, `D' for default, or `F' for fixed
TODOC: EXTRA_FIELDS_ARRAYREF
@@ -113,7 +113,7 @@ sub insert {
} );
my $flag = $self->getfield($svcdb.'__'.$field.'_flag');
- if ( uc($flag) =~ /^([DFX])$/ ) {
+ if ( uc($flag) =~ /^([DF])$/ ) {
$part_svc_column->setfield('columnflag', $1);
$part_svc_column->setfield('columnvalue',
$self->getfield($svcdb.'__'.$field)
@@ -201,7 +201,7 @@ sub replace {
} );
my $flag = $new->getfield($svcdb.'__'.$field.'_flag');
- if ( uc($flag) =~ /^([DFX])$/ ) {
+ if ( uc($flag) =~ /^([DF])$/ ) {
$part_svc_column->setfield('columnflag', $1);
$part_svc_column->setfield('columnvalue',
$new->getfield($svcdb.'__'.$field)
@@ -254,7 +254,32 @@ sub check {
my @fields = eval { fields( $recref->{svcdb} ) }; #might die
return "Unknown svcdb!" unless @fields;
- $self->SUPER::check;
+##REPLACED BY part_svc_column
+# my $svcdb;
+# foreach $svcdb ( qw(
+# svc_acct svc_acct_sm svc_domain
+# ) ) {
+# my @rows = map { /^${svcdb}__(.*)$/; $1 }
+# grep ! /_flag$/,
+# grep /^${svcdb}__/,
+# fields('part_svc');
+# foreach my $row (@rows) {
+# unless ( $svcdb eq $recref->{svcdb} ) {
+# $recref->{$svcdb.'__'.$row}='';
+# $recref->{$svcdb.'__'.$row.'_flag'}='';
+# next;
+# }
+# $recref->{$svcdb.'__'.$row.'_flag'} =~ /^([DF]?)$/
+# or return "Illegal flag for $svcdb $row";
+# $recref->{$svcdb.'__'.$row.'_flag'} = $1;
+#
+# my $error = $self->ut_anything($svcdb.'__'.$row);
+# return $error if $error;
+#
+# }
+# }
+
+ ''; #no error
}
=item part_svc_column COLUMNNAME
@@ -265,12 +290,12 @@ COLUMNNAME, or a new part_svc_column object if none exists.
=cut
sub part_svc_column {
- my( $self, $columnname) = @_;
- $self->svcpart &&
- qsearchs('part_svc_column', {
- 'svcpart' => $self->svcpart,
- 'columnname' => $columnname,
- }
+ my $self = shift;
+ my $columnname = shift;
+ qsearchs('part_svc_column', {
+ 'svcpart' => $self->svcpart,
+ 'columnname' => $columnname,
+ }
) or new FS::part_svc_column {
'svcpart' => $self->svcpart,
'columnname' => $columnname,
diff --git a/FS/FS/part_svc_column.pm b/FS/FS/part_svc_column.pm
index 885155be3..37e841e87 100644
--- a/FS/FS/part_svc_column.pm
+++ b/FS/FS/part_svc_column.pm
@@ -41,7 +41,7 @@ fields are currently supported:
=item columnvalue - default or fixed value for the column
-=item columnflag - null, D, F, X (virtual fields)
+=item columnflag - null, D or F
=back
@@ -91,18 +91,18 @@ sub check {
;
return $error if $error;
- $self->columnflag =~ /^([DFX])$/
+ $self->columnflag =~ /^([DF])$/
or return "illegal columnflag ". $self->columnflag;
$self->columnflag(uc($1));
- $self->SUPER::check;
+ ''; #no error
}
=back
=head1 VERSION
-$Id: part_svc_column.pm,v 1.2 2003-08-05 00:20:44 khoff Exp $
+$Id: part_svc_column.pm,v 1.1 2001-09-07 20:49:15 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/part_svc_router.pm b/FS/FS/part_svc_router.pm
deleted file mode 100755
index 0b23ab580..000000000
--- a/FS/FS/part_svc_router.pm
+++ /dev/null
@@ -1,32 +0,0 @@
-package FS::part_svc_router;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw(qsearchs);
-use FS::router;
-use FS::part_svc;
-
-@ISA = qw(FS::Record);
-
-sub table { 'part_svc_router'; }
-
-sub check {
- my $self = shift;
- my $error =
- $self->ut_foreign_key('svcpart', 'part_svc', 'svcpart')
- || $self->ut_foreign_key('routernum', 'router', 'routernum');
- return $error if $error;
- ''; #no error
-}
-
-sub router {
- my $self = shift;
- return qsearchs('router', { routernum => $self->routernum });
-}
-
-sub part_svc {
- my $self = shift;
- return qsearchs('part_svc', { svcpart => $self->svcpart });
-}
-
-1;
diff --git a/FS/FS/part_virtual_field.pm b/FS/FS/part_virtual_field.pm
deleted file mode 100755
index 03c34cca5..000000000
--- a/FS/FS/part_virtual_field.pm
+++ /dev/null
@@ -1,303 +0,0 @@
-package FS::part_virtual_field;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs qsearch dbdef );
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::part_virtual_field - Object methods for part_virtual_field records
-
-=head1 SYNOPSIS
-
- use FS::part_virtual_field;
-
- $record = new FS::part_virtual_field \%hash;
- $record = new FS::part_virtual_field { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::part_virtual_field object represents the definition of a virtual field
-(see the BACKGROUND section). FS::part_virtual_field contains the name and
-base table of the field, as well as validation rules and UI hints about the
-display of the field. The actual data is stored in FS::virtual_field; see
-its manpage for details.
-
-FS::part_virtual_field inherits from FS::Record. The following fields are
-currently supported:
-
-=over 2
-
-=item vfieldpart - primary key (assigned automatically)
-
-=item name - name of the field
-
-=item dbtable - table for which this virtual field is defined
-
-=item check_block - Perl code to validate/normalize data
-
-=item list_source - Perl code to generate a list of values (UI hint)
-
-=item length - expected length of the value (UI hint)
-
-=item label - descriptive label for the field (UI hint)
-
-=item sequence - sort key (UI hint; unimplemented)
-
-=back
-
-=head1 BACKGROUND
-
-"Form is none other than emptiness,
- and emptiness is none other than form."
--- Heart Sutra
-
-The virtual field mechanism allows site admins to make trivial changes to
-the Freeside database schema without modifying the code. Specifically, the
-user can add custom-defined 'fields' to the set of data tracked by Freeside
-about objects such as customers and services. These fields are not associated
-with any logic in the core Freeside system, but may be referenced in peripheral
-code such as exports, price calculations, or alternate interfaces, or may just
-be stored in the database for future reference.
-
-This system was originally devised for svc_broadband, which (by necessity)
-comprises such a wide range of access technologies that no static set of fields
-could contain all the information needed by the exports. In an appalling
-display of False Laziness, a parallel mechanism was implemented for the
-router table, to store properties such as passwords to configure routers.
-
-The original system treated svc_broadband custom fields (sb_fields) as records
-in a completely separate table. Any code that accessed or manipulated these
-fields had to be aware that they were I<not> fields in svc_broadband, but
-records in sb_field. For example, code that inserted a svc_broadband with
-several custom fields had to create an FS::svc_broadband object, call its
-insert() method, and then create several FS::sb_field objects and call I<their>
-insert() methods.
-
-This created a problem for exports. The insert method on any FS::svc_Common
-object (including svc_broadband) automatically triggers exports after the
-record has been inserted. However, at this point, the sb_fields had not yet
-been inserted, so the export could not rely on their presence, which was the
-original purpose of sb_fields.
-
-Hence the new system. Virtual fields are appended to the field list of every
-record at the FS::Record level, whether the object is created ex nihilo with
-new() or fetched with qsearch(). The fields() method now returns a list of
-both real and virtual fields. The insert(), replace(), and delete() methods
-now update both the base table and the virtual fields, in a single transaction.
-
-A new method is provided, virtual_fields(), which gives only the virtual
-fields. UI code that dynamically generates form widgets to edit virtual field
-data should use this to figure out what fields are defined. (See below.)
-
-Subclasses may override virtual_fields() to restrict the set of virtual
-fields available. Some discipline and sanity on the part of the programmer
-are required; in particular, this function should probably not depend on any
-fields in the record other than the primary key, since the others may change
-after the object is instantiated. (Making it depend on I<virtual> fields is
-just asking for pain.) One use of this is seen in FS::svc_Common; another
-possibility is field-level access control based on FS::UID::getotaker().
-
-As a trivial case, a subclass may opt out of supporting virtual fields with
-the following code:
-
-sub virtual_fields { () }
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record. To add the record to the database, see "insert".
-
-=cut
-
-sub table { 'part_virtual_field'; }
-sub virtual_fields { () }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-If there is an error, returns the error, otherwise returns false.
-Called by the insert and replace methods.
-
-=back
-
-=cut
-
-sub check {
- my $self = shift;
-
- my $error = $self->ut_text('name') ||
- $self->ut_text('dbtable') ||
- $self->ut_number('length')
- ;
- return $error if $error;
-
- # Make sure it's a real table with a numeric primary key
- my ($table, $pkey);
- if($table = $FS::Record::dbdef->table($self->dbtable)) {
- if($pkey = $table->primary_key) {
- if($table->column($pkey)->type =~ /int/i) {
- # this is what it should be
- } else {
- $error = "$table.$pkey is not an integer";
- }
- } else {
- $error = "$table does not have a single-field primary key";
- }
- } else {
- $error = "$table does not exist in the schema";
- }
- return $error if $error;
-
- # Possibly some sanity checks for check_block and list_source?
-
- $self->SUPER::check;
-}
-
-=item list
-
-Evaluates list_source.
-
-=cut
-
-sub list {
- my $self = shift;
- return () unless $self->list_source;
-
- my @opts = eval($self->list_source);
- if($@) {
- warn $@;
- return ();
- } else {
- return @opts;
- }
-}
-
-=item widget UI_TYPE MODE [ VALUE ]
-
-Generates UI code for a widget suitable for editing/viewing the field, based on
-list_source and length.
-
-The only UI_TYPE currently supported is 'HTML', and the only MODE is 'view'.
-Others will be added later.
-
-In HTML, all widgets are assumed to be table rows. View widgets look like
-<TR><TD ALIGN="right">Label</TD><TD BGCOLOR="#ffffff">Value</TD></TR>
-
-(Most of the display style stuff, such as the colors, should probably go into
-a separate module specific to the UI. That can wait, though. The API for
-this function won't change.)
-
-VALUE (optional) is the current value of the field.
-
-=cut
-
-sub widget {
- my $self = shift;
- my ($ui_type, $mode, $value) = @_;
- my $text;
- my $label = $self->label || $self->name;
-
- if ($ui_type eq 'HTML') {
- if ($mode eq 'view') {
- $text = q!<TR><TD ALIGN="right">! . $label .
- q!</TD><TD BGCOLOR="#ffffff">! . $value .
- q!</TD></TR>! . "\n";
- } elsif ($mode eq 'edit') {
- $text = q!<TR><TD ALIGN="right">! . $label .
- q!</TD><TD>!;
- if ($self->list_source) {
- $text .= q!<SELECT NAME="! . $self->name .
- q!" SIZE=1>! . "\n";
- foreach ($self->list) {
- $text .= q!<OPTION VALUE="! . $_ . q!"!;
- $text .= ' SELECTED' if ($_ eq $value);
- $text .= '>' . $_ . '</OPTION>' . "\n";
- }
- } else {
- $text .= q!<INPUT NAME="! . $self->name .
- q!" VALUE="! . $value . q!"!;
- if ($self->length) {
- $text .= q! SIZE="! . $self->length . q!"!;
- }
- $text .= '>';
- }
- $text .= q!</TD></TR>! . "\n";
- } else {
- return '';
- }
- } else {
- return '';
- }
- return $text;
-}
-
-=head1 VERSION
-
-$Id: part_virtual_field.pm,v 1.2 2003-08-05 00:20:45 khoff Exp $
-
-=head1 NOTES
-
-=head2 Semantics of check_block:
-
-This has been changed from the sb_field implementation to make check_blocks
-simpler and more natural to Perl programmers who work on things other than
-Freeside.
-
-The check_block is eval'd with the (proposed) new value of the field in $_,
-and the object to be updated in $self. Its return value is ignored. The
-check_block may change the value of $_ to override the proposed value, or
-call die() (with an appropriate error message) to reject the update entirely;
-the error string will be returned as the output of the check() method.
-
-This makes check_blocks like
-
-C<s/foo/bar/>
-
-do what you expect.
-
-The check_block is expected NOT to do anything freaky to $self, like modifying
-other fields or calling $self->check(). You have been warned.
-
-(FIXME: Rewrite some of the warnings from part_sb_field and insert here.)
-
-=head1 BUGS
-
-None. It's absolutely falwless.
-
-=head1 SEE ALSO
-
-L<FS::Record>, L<FS::virtual_field>
-
-=cut
-
-1;
-
-
diff --git a/FS/FS/pkg_svc.pm b/FS/FS/pkg_svc.pm
index ea52176cb..3956dd831 100644
--- a/FS/FS/pkg_svc.pm
+++ b/FS/FS/pkg_svc.pm
@@ -115,7 +115,7 @@ sub check {
return $error if $error;
}
- $self->SUPER::check;
+ ''; #no error
}
=item part_pkg
diff --git a/FS/FS/port.pm b/FS/FS/port.pm
index 620030afc..13455ca89 100644
--- a/FS/FS/port.pm
+++ b/FS/FS/port.pm
@@ -113,7 +113,7 @@ sub check {
unless $self->ip || $self->nasport;
return "Unknown nasnum"
unless qsearchs('nas', { 'nasnum' => $self->nasnum } );
- $self->SUPER::check;
+ ''; #no error
}
=item session
@@ -133,7 +133,7 @@ sub session {
=head1 VERSION
-$Id: port.pm,v 1.6 2003-08-05 00:20:45 khoff Exp $
+$Id: port.pm,v 1.5 2001-02-14 04:33:06 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/prepay_credit.pm b/FS/FS/prepay_credit.pm
index a9d26d151..7ed9b8344 100644
--- a/FS/FS/prepay_credit.pm
+++ b/FS/FS/prepay_credit.pm
@@ -108,7 +108,6 @@ sub check {
|| $self->ut_alpha('identifier')
|| $self->ut_money('amount')
|| $self->utnumbern('seconds')
- || $self->SUPER::check
;
}
diff --git a/FS/FS/queue.pm b/FS/FS/queue.pm
index 634f7f4bd..d35dc883f 100644
--- a/FS/FS/queue.pm
+++ b/FS/FS/queue.pm
@@ -207,7 +207,7 @@ sub check {
$self->status('new') unless $self->status;
$self->_date(time) unless $self->_date;
- $self->SUPER::check;
+ ''; #no error
}
=item args
@@ -385,7 +385,7 @@ END
=head1 VERSION
-$Id: queue.pm,v 1.16 2003-08-05 00:20:46 khoff Exp $
+$Id: queue.pm,v 1.15 2002-07-02 06:48:59 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/queue_arg.pm b/FS/FS/queue_arg.pm
index d23ee2afd..08fe47341 100644
--- a/FS/FS/queue_arg.pm
+++ b/FS/FS/queue_arg.pm
@@ -100,14 +100,14 @@ sub check {
;
return $error if $error;
- $self->SUPER::check;
+ ''; #no error
}
=back
=head1 VERSION
-$Id: queue_arg.pm,v 1.2 2003-08-05 00:20:46 khoff Exp $
+$Id: queue_arg.pm,v 1.1 2001-09-11 00:08:18 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/queue_depend.pm b/FS/FS/queue_depend.pm
index bc910d8e9..4a4e3c55c 100644
--- a/FS/FS/queue_depend.pm
+++ b/FS/FS/queue_depend.pm
@@ -103,7 +103,6 @@ sub check {
$self->ut_numbern('dependnum')
|| $self->ut_foreign_key('jobnum', 'queue', 'jobnum')
|| $self->ut_foreign_key('depend_jobnum', 'queue', 'jobnum')
- || $self->SUPER::check
;
}
diff --git a/FS/FS/radius_usergroup.pm b/FS/FS/radius_usergroup.pm
index 9bba057c9..647621d28 100644
--- a/FS/FS/radius_usergroup.pm
+++ b/FS/FS/radius_usergroup.pm
@@ -100,7 +100,6 @@ sub check {
|| $self->ut_number('svcnum')
|| $self->ut_foreign_key('svcnum','svc_acct','svcnum')
|| $self->ut_text('groupname')
- || $self->SUPER::check
;
}
diff --git a/FS/FS/router.pm b/FS/FS/router.pm
deleted file mode 100755
index 2554ce86b..000000000
--- a/FS/FS/router.pm
+++ /dev/null
@@ -1,144 +0,0 @@
-package FS::router;
-
-use strict;
-use vars qw( @ISA );
-use FS::Record qw( qsearchs qsearch );
-use FS::addr_block;
-
-@ISA = qw( FS::Record );
-
-=head1 NAME
-
-FS::router - Object methods for router records
-
-=head1 SYNOPSIS
-
- use FS::router;
-
- $record = new FS::router \%hash;
- $record = new FS::router { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
-=head1 DESCRIPTION
-
-An FS::router record describes a broadband router, such as a DSLAM or a wireless
- access point. FS::router inherits from FS::Record. The following
-fields are currently supported:
-
-=over 4
-
-=item routernum - primary key
-
-=item routername - descriptive name for the router
-
-=item svcnum - svcnum of the owning FS::svc_broadband, if appropriate
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Create a new record. To add the record to the database, see "insert".
-
-=cut
-
-sub table { 'router'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-=item delete
-
-Deletes this record from the database. If there is an error, returns the
-error, otherwise returns false.
-
-=item replace OLD_RECORD
-
-Replaces OLD_RECORD with this one in the database. If there is an error,
-returns the error, otherwise returns false.
-
-=item check
-
-Checks all fields to make sure this is a valid record. If there is an error,
-returns the error, otherwise returns false. Called by the insert and replace
-methods.
-
-=cut
-
-sub check {
- my $self = shift;
-
- my $error =
- $self->ut_numbern('routernum')
- || $self->ut_text('routername');
- return $error if $error;
-
- $self->SUPER::check;
-}
-
-=item addr_block
-
-Returns a list of FS::addr_block objects (address blocks) associated
-with this object.
-
-=cut
-
-sub addr_block {
- my $self = shift;
- return qsearch('addr_block', { routernum => $self->routernum });
-}
-
-=item part_svc_router
-
-Returns a list of FS::part_svc_router objects associated with this
-object. This is unlikely to be useful for any purpose other than retrieving
-the associated FS::part_svc objects. See below.
-
-=cut
-
-sub part_svc_router {
- my $self = shift;
- return qsearch('part_svc_router', { routernum => $self->routernum });
-}
-
-=item part_svc
-
-Returns a list of FS::part_svc objects associated with this object.
-
-=cut
-
-sub part_svc {
- my $self = shift;
- return map { qsearchs('part_svc', { svcpart => $_->svcpart }) }
- $self->part_svc_router;
-}
-
-=back
-
-=head1 VERSION
-
-$Id:
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-FS::svc_broadband, FS::router, FS::addr_block, FS::part_svc,
-schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/session.pm b/FS/FS/session.pm
index 2ad594cf2..de0f2a76a 100644
--- a/FS/FS/session.pm
+++ b/FS/FS/session.pm
@@ -216,7 +216,7 @@ sub check {
return $error if $error;
return "Unknown svcnum"
unless qsearchs('svc_acct', { 'svcnum' => $self->svcnum } );
- $self->SUPER::check;
+ '';
}
=item nas_heartbeat
@@ -247,7 +247,7 @@ sub svc_acct {
=head1 VERSION
-$Id: session.pm,v 1.8 2003-08-05 00:20:46 khoff Exp $
+$Id: session.pm,v 1.7 2001-04-15 13:35:12 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/svc_Common.pm b/FS/FS/svc_Common.pm
index cadb997da..2e236ee2e 100644
--- a/FS/FS/svc_Common.pm
+++ b/FS/FS/svc_Common.pm
@@ -2,7 +2,7 @@ package FS::svc_Common;
use strict;
use vars qw( @ISA $noexport_hack );
-use FS::Record qw( qsearch qsearchs fields dbh );
+use FS::Record qw( qsearchs fields dbh );
use FS::cust_svc;
use FS::part_svc;
use FS::queue;
@@ -28,60 +28,6 @@ inherit from, i.e. FS::svc_acct. FS::svc_Common inherits from FS::Record.
=over 4
-=cut
-
-sub virtual_fields {
-
- # This restricts the fields based on part_svc_column and the svcpart of
- # the service. There are four possible cases:
- # 1. svcpart passed as part of the svc_x hash.
- # 2. svcpart fetched via cust_svc based on svcnum.
- # 3. No svcnum or svcpart. In this case, return ALL the fields with
- # dbtable eq $self->table.
- # 4. Called via "fields('svc_acct')" or something similar. In this case
- # there is no $self object.
-
- my $self = shift;
- my $svcpart;
- my @vfields = $self->SUPER::virtual_fields;
-
- return @vfields unless (ref $self); # Case 4
-
- if ($self->svcpart) { # Case 1
- $svcpart = $self->svcpart;
- } elsif ( $self->svcnum
- && qsearchs('cust_svc',{'svcnum'=>$self->svcnum} )
- ) { #Case 2
- $svcpart = $self->cust_svc->svcpart;
- } else { # Case 3
- $svcpart = '';
- }
-
- if ($svcpart) { #Cases 1 and 2
- my %flags = map { $_->columnname, $_->columnflag } (
- qsearch ('part_svc_column', { svcpart => $svcpart } )
- );
- return grep { not ($flags{$_} eq 'X') } @vfields;
- } else { # Case 3
- return @vfields;
- }
- return ();
-}
-
-=item check
-
-Checks the validity of fields in this record.
-
-At present, this does nothing but call FS::Record::check (which, in turn,
-does nothing but run virtual field checks).
-
-=cut
-
-sub check {
- my $self = shift;
- $self->SUPER::check;
-}
-
=item insert [ JOBNUM_ARRAYREF [ OBJECTS_ARRAYREF ] ]
Adds this record to the database. If there is an error, returns the error,
@@ -324,7 +270,7 @@ sub setx {
#set default/fixed/whatever fields from part_svc
my $table = $self->table;
- foreach my $field ( grep { $_ ne 'svcnum' } $self->fields ) {
+ foreach my $field ( grep { $_ ne 'svcnum' } fields($table) ) {
my $part_svc_column = $part_svc->part_svc_column($field);
if ( $part_svc_column->columnflag eq $x ) {
$self->setfield( $field, $part_svc_column->columnvalue );
@@ -432,6 +378,10 @@ sub cancel { ''; }
=back
+=head1 VERSION
+
+$Id: svc_Common.pm,v 1.12.4.4 2003-11-12 12:29:55 ivan Exp $
+
=head1 BUGS
The setfixed method return value.
diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm
index 9d8566d77..8c99c9e48 100644
--- a/FS/FS/svc_acct.pm
+++ b/FS/FS/svc_acct.pm
@@ -7,6 +7,7 @@ use vars qw( @ISA $DEBUG $me $conf
$username_ampersand $username_letter $username_letterfirst
$username_noperiod $username_nounderscore $username_nodash
$username_uppercase
+ $mydomain
$welcome_template $welcome_from $welcome_subject $welcome_mimetype
$smtpmachine
$radius_password $radius_ip
@@ -18,9 +19,11 @@ use FS::UID qw( datasrc );
use FS::Conf;
use FS::Record qw( qsearch qsearchs fields dbh dbdef );
use FS::svc_Common;
+use Net::SSH;
use FS::cust_svc;
use FS::part_svc;
use FS::svc_acct_pop;
+use FS::svc_acct_sm;
use FS::cust_main_invoice;
use FS::svc_domain;
use FS::raddb;
@@ -51,6 +54,7 @@ $FS::UID::callback{'FS::svc_acct'} = sub {
$username_nodash = $conf->exists('username-nodash');
$username_uppercase = $conf->exists('username-uppercase');
$username_ampersand = $conf->exists('username-ampersand');
+ $mydomain = $conf->config('domain');
$dirhash = $conf->config('dirhash') || 0;
if ( $conf->exists('welcome_email') ) {
$welcome_template = new Text::Template (
@@ -314,8 +318,8 @@ sub insert {
if ( exists($conflict_user_svcpart{$dup_svcpart})
|| exists($conflict_userdomain_svcpart{$dup_svcpart}) ) {
$dbh->rollback if $oldAutoCommit;
- return "duplicate uid: conflicts with svcnum". $dup_uid->svcnum.
- "via exportnum ". $conflict_user_svcpart{$dup_svcpart}
+ return "duplicate uid: conflicts with svcnum ". $dup_uid->svcnum.
+ " via exportnum ". $conflict_user_svcpart{$dup_svcpart}
|| $conflict_userdomain_svcpart{$dup_svcpart};
}
}
@@ -427,6 +431,11 @@ The corresponding FS::cust_svc record will be deleted as well.
sub delete {
my $self = shift;
+ if ( defined( $FS::Record::dbdef->table('svc_acct_sm') ) ) {
+ return "Can't delete an account which has (svc_acct_sm) mail aliases!"
+ if $self->uid && qsearch( 'svc_acct_sm', { 'domuid' => $self->uid } );
+ }
+
return "can't delete system account" if $self->_check_system;
return "Can't delete an account which is a (svc_forward) source!"
@@ -609,11 +618,13 @@ sub replace {
=item suspend
-Suspends this account by calling export-specific suspend hooks. If there is
-an error, returns the error, otherwise returns false.
+Suspends this account by prefixing *SUSPENDED* to the password. If there is an
+error, returns the error, otherwise returns false.
Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+Calls any export-specific suspend hooks.
+
=cut
sub suspend {
@@ -624,11 +635,13 @@ sub suspend {
=item unsuspend
-Unsuspends this account by by calling export-specific suspend hooks. If there
-is an error, returns the error, otherwise returns false.
+Unsuspends this account by removing *SUSPENDED* from the password. If there is
+an error, returns the error, otherwise returns false.
Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+Calls any export-specific unsuspend hooks.
+
=cut
sub unsuspend {
@@ -791,7 +804,7 @@ sub check {
$recref->{slipip} = '0e0';
} else {
$recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/
- or return "Illegal slipip: ". $self->slipip;
+ or return "Illegal slipip". $self->slipip;
$recref->{slipip} = $1;
}
@@ -831,13 +844,13 @@ sub check {
": ". $recref->{_password};
}
- $self->SUPER::check;
+ ''; #no error
}
=item _check_system
-
+
=cut
-
+
sub _check_system {
my $self = shift;
scalar( grep { $self->username eq $_ || $self->email eq $_ }
@@ -845,6 +858,7 @@ sub _check_system {
);
}
+
=item radius
Depriciated, use radius_reply instead.
@@ -897,7 +911,7 @@ sub radius_check {
my $self = shift;
my $password = $self->_password;
my $pw_attrib = length($password) <= 12 ? $radius_password : 'Crypt-Password';
- ( $pw_attrib => $password,
+ ( $pw_attrib => $self->_password,
map {
/^(rc_(.*))$/;
my($column, $attrib) = ($1, $2);
@@ -915,10 +929,14 @@ Returns the domain associated with this account.
sub domain {
my $self = shift;
- die "svc_acct.domsvc is null for svcnum ". $self->svcnum unless $self->domsvc;
- my $svc_domain = $self->svc_domain
- or die "no svc_domain.svcnum for svc_acct.domsvc ". $self->domsvc;
- $svc_domain->domain;
+ if ( $self->domsvc ) {
+ #$self->svc_domain->domain;
+ my $svc_domain = $self->svc_domain
+ or die "no svc_domain.svcnum for svc_acct.domsvc ". $self->domsvc;
+ $svc_domain->domain;
+ } else {
+ $mydomain or die "svc_acct.domsvc is null and no legacy domain config file";
+ }
}
=item svc_domain
@@ -1031,6 +1049,7 @@ sub attribute_since_sqlradacct {
$self->cust_svc->attribute_since_sqlradacct(@_);
}
+
=item get_session_history_sqlradacct TIMESTAMP_START TIMESTAMP_END
Returns an array of hash references of this customers login history for the
@@ -1069,28 +1088,36 @@ sub radius_groups {
=item send_email
-This is the FS::svc_acct job-queue-able version. It still uses
-FS::Misc::send_email under-the-hood.
-
=cut
sub send_email {
my %opt = @_;
- eval "use FS::Misc qw(send_email)";
- die $@ if $@;
+ use Date::Format;
+ use Mail::Internet 1.44;
+ use Mail::Header;
$opt{mimetype} ||= 'text/plain';
$opt{mimetype} .= '; charset="iso-8859-1"' unless $opt{mimetype} =~ /charset/;
- my $error = send_email(
- 'from' => $opt{from},
- 'to' => $opt{to},
- 'subject' => $opt{subject},
- 'content-type' => $opt{mimetype},
- 'body' => [ map "$_\n", split("\n", $opt{body}) ],
+ $ENV{MAILADDRESS} = $opt{from};
+ my $header = new Mail::Header ( [
+ "From: $opt{from}",
+ "To: $opt{to}",
+ "Sender: $opt{from}",
+ "Reply-To: $opt{from}",
+ "Date: ". time2str("%a, %d %b %Y %X %z", time),
+ "Subject: $opt{subject}",
+ "Content-Type: $opt{mimetype}",
+ ] );
+ my $message = new Mail::Internet (
+ 'Header' => $header,
+ 'Body' => [ map "$_\n", split("\n", $opt{body}) ],
);
- die $error if $error;
+ $!=0;
+ $message->smtpsend( Host => $smtpmachine )
+ or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
+ or die "can't send email to $opt{to} via $smtpmachine with SMTP: $!";
}
=item check_and_rebuild_fuzzyfiles
@@ -1241,7 +1268,7 @@ probably live somewhere else...
L<FS::svc_Common>, edit/part_svc.cgi from an installed web interface,
export.html from the base documentation, L<FS::Record>, L<FS::Conf>,
L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>, L<FS::queue>,
-L<freeside-queued>), L<FS::svc_acct_pop>,
+L<freeside-queued>), L<Net::SSH>, L<ssh>, L<FS::svc_acct_pop>,
schema.html from the base documentation.
=cut
diff --git a/FS/FS/svc_acct_pop.pm b/FS/FS/svc_acct_pop.pm
index f98f91a4f..d2247658b 100644
--- a/FS/FS/svc_acct_pop.pm
+++ b/FS/FS/svc_acct_pop.pm
@@ -93,7 +93,6 @@ sub check {
or $self->ut_number('ac')
or $self->ut_number('exch')
or $self->ut_numbern('loc')
- or $self->SUPER::check
;
}
@@ -188,7 +187,7 @@ END
=head1 VERSION
-$Id: svc_acct_pop.pm,v 1.10 2003-08-05 00:20:47 khoff Exp $
+$Id: svc_acct_pop.pm,v 1.7.4.2 2003-07-04 01:37:44 ivan Exp $
=head1 BUGS
diff --git a/FS/FS/svc_acct_sm.pm b/FS/FS/svc_acct_sm.pm
new file mode 100644
index 000000000..c92f1421f
--- /dev/null
+++ b/FS/FS/svc_acct_sm.pm
@@ -0,0 +1,260 @@
+package FS::svc_acct_sm;
+
+use strict;
+use vars qw( @ISA $nossh_hack $conf $shellmachine @qmailmachines );
+use FS::Record qw( fields qsearch qsearchs );
+use FS::svc_Common;
+use FS::cust_svc;
+use Net::SSH qw(ssh);
+use FS::Conf;
+use FS::svc_acct;
+use FS::svc_domain;
+
+@ISA = qw( FS::svc_Common );
+
+#ask FS::UID to run this stuff for us later
+#$FS::UID::callback{'FS::svc_acct_sm'} = sub {
+# $conf = new FS::Conf;
+# $shellmachine = $conf->exists('qmailmachines')
+# ? $conf->config('shellmachine')
+# : '';
+#};
+
+=head1 NAME
+
+FS::svc_acct_sm - Object methods for svc_acct_sm records
+
+=head1 SYNOPSIS
+
+ use FS::svc_acct_sm;
+
+ $record = new FS::svc_acct_sm \%hash;
+ $record = new FS::svc_acct_sm { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+ $error = $record->suspend;
+
+ $error = $record->unsuspend;
+
+ $error = $record->cancel;
+
+=head1 WARNING
+
+FS::svc_acct_sm is B<depreciated>. This class is only included for migration
+purposes. See L<FS::svc_forward>.
+
+=head1 DESCRIPTION
+
+An FS::svc_acct_sm object represents a virtual mail alias. FS::svc_acct_sm
+inherits from FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item svcnum - primary key (assigned automatcially for new accounts)
+
+=item domsvc - svcnum of the virtual domain (see L<FS::svc_domain>)
+
+=item domuid - uid of the target account (see L<FS::svc_acct>)
+
+=item domuser - virtual username
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new virtual mail alias. To add the virtual mail alias to the
+database, see L<"insert">.
+
+=cut
+
+sub table { 'svc_acct_sm'; }
+
+=item insert
+
+Adds this virtual mail alias to the database. If there is an error, returns
+the error, otherwise returns false.
+
+The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
+defined. An FS::cust_svc record will be created and inserted.
+
+ #If the configuration values (see L<FS::Conf>) shellmachine and qmailmachines
+ #exist, and domuser is `*' (meaning a catch-all mailbox), the command:
+ #
+ # [ -e $dir/.qmail-$qdomain-default ] || {
+ # touch $dir/.qmail-$qdomain-default;
+ # chown $uid:$gid $dir/.qmail-$qdomain-default;
+ # }
+ #
+ #is executed on shellmachine via ssh (see L<dot-qmail/"EXTENSION ADDRESSES">).
+ #This behaviour can be surpressed by setting $FS::svc_acct_sm::nossh_hack true.
+
+=cut
+
+sub insert {
+ my $self = shift;
+ my $error;
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ $error=$self->check;
+ return $error if $error;
+
+ return "Domain username (domuser) in use for this domain (domsvc)"
+ if qsearchs('svc_acct_sm',{ 'domuser'=> $self->domuser,
+ 'domsvc' => $self->domsvc,
+ } );
+
+ return "First domain username (domuser) for domain (domsvc) must be " .
+ qq='*' (catch-all)!=
+ if $self->domuser ne '*'
+ && ! qsearch('svc_acct_sm',{ 'domsvc' => $self->domsvc } )
+ && ! $conf->exists('maildisablecatchall');
+
+ $error = $self->SUPER::insert;
+ return $error if $error;
+
+ #my $svc_domain = qsearchs( 'svc_domain', { 'svcnum' => $self->domsvc } );
+ #my $svc_acct = qsearchs( 'svc_acct', { 'uid' => $self->domuid } );
+ #my ( $uid, $gid, $dir, $domain ) = (
+ # $svc_acct->uid,
+ # $svc_acct->gid,
+ # $svc_acct->dir,
+ # $svc_domain->domain,
+ #);
+ #my $qdomain = $domain;
+ #$qdomain =~ s/\./:/g; #see manpage for 'dot-qmail': EXTENSION ADDRESSES
+ #ssh("root\@$shellmachine","[ -e $dir/.qmail-$qdomain-default ] || { touch $dir/.qmail-$qdomain-default; chown $uid:$gid $dir/.qmail-$qdomain-default; }")
+ # if ( ! $nossh_hack && $shellmachine && $dir && $self->domuser eq '*' );
+
+ ''; #no error
+
+}
+
+=item delete
+
+Deletes this virtual mail alias from the database. If there is an error,
+returns the error, otherwise returns false.
+
+The corresponding FS::cust_svc record will be deleted as well.
+
+=item replace OLD_RECORD
+
+Replaces OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+sub replace {
+ my ( $new, $old ) = ( shift, shift );
+ my $error;
+
+ return "Domain username (domuser) in use for this domain (domsvc)"
+ if ( $old->domuser ne $new->domuser
+ || $old->domsvc != $new->domsvc
+ ) && qsearchs('svc_acct_sm',{
+ 'domuser'=> $new->domuser,
+ 'domsvc' => $new->domsvc,
+ } )
+ ;
+
+ $new->SUPER::replace($old);
+
+}
+
+=item suspend
+
+Just returns false (no error) for now.
+
+Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item unsuspend
+
+Just returns false (no error) for now.
+
+Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item cancel
+
+Just returns false (no error) for now.
+
+Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
+
+=item check
+
+Checks all fields to make sure this is a valid virtual mail alias. If there is
+an error, returns the error, otherwise returns false. Called by the insert and
+replace methods.
+
+Sets any fixed values; see L<FS::part_svc>.
+
+=cut
+
+sub check {
+ my $self = shift;
+ my $error;
+
+ my $x = $self->setfixed;
+ return $x unless ref($x);
+ #my $part_svc = $x;
+
+ my($recref) = $self->hashref;
+
+ $recref->{domuser} =~ /^(\*|[a-z0-9_\-]{2,32})$/
+ or return "Illegal domain username (domuser)";
+ $recref->{domuser} = $1;
+
+ $recref->{domsvc} =~ /^(\d+)$/ or return "Illegal domsvc";
+ $recref->{domsvc} = $1;
+ my($svc_domain);
+ return "Unknown domsvc" unless
+ $svc_domain=qsearchs('svc_domain',{'svcnum'=> $recref->{domsvc} } );
+
+ $recref->{domuid} =~ /^(\d+)$/ or return "Illegal uid";
+ $recref->{domuid} = $1;
+ my($svc_acct);
+ return "Unknown uid" unless
+ $svc_acct=qsearchs('svc_acct',{'uid'=> $recref->{domuid} } );
+
+ ''; #no error
+}
+
+=back
+
+=head1 VERSION
+
+$Id: svc_acct_sm.pm,v 1.5 2001-09-06 20:41:59 ivan Exp $
+
+=head1 BUGS
+
+The remote commands should be configurable.
+
+The $recref stuff in sub check should be cleaned up.
+
+=head1 SEE ALSO
+
+L<FS::svc_forward>
+
+L<FS::Record>, L<FS::Conf>, L<FS::cust_svc>, L<FS::part_svc>, L<FS::cust_pkg>,
+L<FS::svc_acct>, L<FS::svc_domain>, L<Net::SSH>, L<ssh>, L<dot-qmail>,
+schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm
deleted file mode 100755
index ec915327b..000000000
--- a/FS/FS/svc_broadband.pm
+++ /dev/null
@@ -1,235 +0,0 @@
-package FS::svc_broadband;
-
-use strict;
-use vars qw(@ISA $conf);
-use FS::Record qw( qsearchs qsearch dbh );
-use FS::svc_Common;
-use FS::cust_svc;
-use FS::addr_block;
-use NetAddr::IP;
-
-@ISA = qw( FS::svc_Common );
-
-$FS::UID::callback{'FS::svc_broadband'} = sub {
- $conf = new FS::Conf;
-};
-
-=head1 NAME
-
-FS::svc_broadband - Object methods for svc_broadband records
-
-=head1 SYNOPSIS
-
- use FS::svc_broadband;
-
- $record = new FS::svc_broadband \%hash;
- $record = new FS::svc_broadband { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-=head1 DESCRIPTION
-
-An FS::svc_broadband object represents a 'broadband' Internet connection, such
-as a DSL, cable modem, or fixed wireless link. These services are assumed to
-have the following properties:
-
-FS::svc_broadband inherits from FS::svc_Common. The following fields are
-currently supported:
-
-=over 4
-
-=item svcnum - primary key
-
-=item blocknum - see FS::addr_block
-
-=item
-speed_up - maximum upload speed, in bits per second. If set to zero, upload
-speed will be unlimited. Exports that do traffic shaping should handle this
-correctly, and not blindly set the upload speed to zero and kill the customer's
-connection.
-
-=item
-speed_down - maximum download speed, as above
-
-=item ip_addr - the customer's IP address. If the customer needs more than one
-IP address, set this to the address of the customer's router. As a result, the
-customer's router will have the same address for both its internal and external
-interfaces thus saving address space. This has been found to work on most NAT
-routers available.
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new svc_broadband. To add the record to the database, see
-"insert".
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-sub table { 'svc_broadband'; }
-
-=item insert
-
-Adds this record to the database. If there is an error, returns the error,
-otherwise returns false.
-
-The additional fields pkgnum and svcpart (see FS::cust_svc) should be
-defined. An FS::cust_svc record will be created and inserted.
-
-=cut
-
-# Standard FS::svc_Common::insert
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-# Standard FS::svc_Common::delete
-
-=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
-
-# Standard FS::svc_Common::replace
-
-=item suspend
-
-Called by the suspend method of FS::cust_pkg (see FS::cust_pkg).
-
-=item unsuspend
-
-Called by the unsuspend method of FS::cust_pkg (see FS::cust_pkg).
-
-=item cancel
-
-Called by the cancel method of FS::cust_pkg (see FS::cust_pkg).
-
-=item check
-
-Checks all fields to make sure this is a valid broadband service. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and replace methods.
-
-=cut
-
-sub check {
- my $self = shift;
- my $x = $self->setfixed;
-
- return $x unless ref($x);
-
- my $error =
- $self->ut_numbern('svcnum')
- || $self->ut_foreign_key('blocknum', 'addr_block', 'blocknum')
- || $self->ut_number('speed_up')
- || $self->ut_number('speed_down')
- || $self->ut_ipn('ip_addr')
- ;
- return $error if $error;
-
- if($self->speed_up < 0) { return 'speed_up must be positive'; }
- if($self->speed_down < 0) { return 'speed_down must be positive'; }
-
- if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
- $self->ip_addr($self->addr_block->next_free_addr->addr);
- if (not $self->ip_addr) {
- return "No free addresses in addr_block (blocknum: ".$self->blocknum.")";
- }
- }
-
- # This should catch errors in the ip_addr. If it doesn't,
- # they'll almost certainly not map into the block anyway.
- my $self_addr = $self->NetAddr; #netmask is /32
- return ('Cannot parse address: ' . $self->ip_addr) unless $self_addr;
-
- my $block_addr = $self->addr_block->NetAddr;
- unless ($block_addr->contains($self_addr)) {
- return 'blocknum '.$self->blocknum.' does not contain address '.$self->ip_addr;
- }
-
- my $router = $self->addr_block->router
- or return 'Cannot assign address from unallocated block:'.$self->addr_block->blocknum;
- if(grep { $_->routernum == $router->routernum} $self->allowed_routers) {
- } # do nothing
- else {
- return 'Router '.$router->routernum.' cannot provide svcpart '.$self->svcpart;
- }
-
- $self->SUPER::check;
-}
-
-=item NetAddr
-
-Returns a NetAddr::IP object containing the IP address of this service. The netmask
-is /32.
-
-=cut
-
-sub NetAddr {
- my $self = shift;
- return new NetAddr::IP ($self->ip_addr);
-}
-
-=item addr_block
-
-Returns the FS::addr_block record (i.e. the address block) for this broadband service.
-
-=cut
-
-sub addr_block {
- my $self = shift;
-
- return qsearchs('addr_block', { blocknum => $self->blocknum });
-}
-
-=back
-
-=item allowed_routers
-
-Returns a list of allowed FS::router objects.
-
-=cut
-
-sub allowed_routers {
- my $self = shift;
-
- return map { $_->router } qsearch('part_svc_router', { svcpart => $self->svcpart });
-}
-
-=head1 BUGS
-
-The business with sb_field has been 'fixed', in a manner of speaking.
-
-=head1 SEE ALSO
-
-FS::svc_Common, FS::Record, FS::addr_block,
-FS::part_svc, schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/svc_domain.pm b/FS/FS/svc_domain.pm
index 10d5d8f5c..58e4c790b 100644
--- a/FS/FS/svc_domain.pm
+++ b/FS/FS/svc_domain.pm
@@ -1,11 +1,13 @@
package FS::svc_domain;
use strict;
-use vars qw( @ISA $whois_hack $conf
+use vars qw( @ISA $whois_hack $conf $smtpmachine
@defaultrecords $soadefaultttl $soaemail $soaexpire $soamachine
$soarefresh $soaretry
);
use Carp;
+use Mail::Internet 1.44;
+use Mail::Header;
use Date::Format;
use Net::Whois 1.0;
use FS::Record qw(fields qsearch qsearchs dbh);
@@ -24,6 +26,8 @@ use FS::queue;
$FS::UID::callback{'FS::domain'} = sub {
$conf = new FS::Conf;
+ $smtpmachine = $conf->config('smtpmachine');
+
@defaultrecords = $conf->config('defaultrecords');
$soadefaultttl = $conf->config('soadefaultttl');
$soaemail = $conf->config('soaemail');
@@ -206,6 +210,10 @@ sub delete {
return "Can't delete a domain which has accounts!"
if qsearch( 'svc_acct', { 'domsvc' => $self->svcnum } );
+ return "Can't delete a domain with (svc_acct_sm) mail aliases!"
+ if defined( $FS::Record::dbdef->table('svc_acct_sm') )
+ && qsearch('svc_acct_sm', { 'domsvc' => $self->svcnum } );
+
#return "Can't delete a domain with (domain_record) zone entries!"
# if qsearch('domain_record', { 'svcnum' => $self->svcnum } );
@@ -342,8 +350,7 @@ sub check {
return "Unknown catchall" unless $svc_acct;
}
- $self->ut_textn('purpose')
- or $self->SUPER::check;
+ $self->ut_textn('purpose');
}
diff --git a/FS/FS/svc_external.pm b/FS/FS/svc_external.pm
deleted file mode 100644
index fe4ea1d67..000000000
--- a/FS/FS/svc_external.pm
+++ /dev/null
@@ -1,174 +0,0 @@
-package FS::svc_external;
-
-use strict;
-use vars qw(@ISA); # $conf
-use FS::UID;
-#use FS::Record qw( qsearch qsearchs dbh);
-use FS::svc_Common;
-
-@ISA = qw( FS::svc_Common );
-
-#FS::UID::install_callback( sub {
-# $conf = new FS::Conf;
-#};
-
-=head1 NAME
-
-FS::svc_external - Object methods for svc_external records
-
-=head1 SYNOPSIS
-
- use FS::svc_external;
-
- $record = new FS::svc_external \%hash;
- $record = new FS::svc_external { 'column' => 'value' };
-
- $error = $record->insert;
-
- $error = $new_record->replace($old_record);
-
- $error = $record->delete;
-
- $error = $record->check;
-
- $error = $record->suspend;
-
- $error = $record->unsuspend;
-
- $error = $record->cancel;
-
-=head1 DESCRIPTION
-
-An FS::svc_external object represents a externally tracked service.
-FS::svc_external inherits from FS::svc_Common. The following fields are
-currently supported:
-
-=over 4
-
-=item svcnum - primary key
-
-=item id - unique number of external record
-
-=item title - for invoice line items
-
-=back
-
-=head1 METHODS
-
-=over 4
-
-=item new HASHREF
-
-Creates a new external service. To add the external service to the database,
-see L<"insert">.
-
-Note that this stores the hash reference, not a distinct copy of the hash it
-points to. You can ask the object for a copy with the I<hash> method.
-
-=cut
-
-sub table { 'svc_external'; }
-
-=item insert
-
-Adds this external service to the database. If there is an error, returns the
-error, otherwise returns false.
-
-The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
-defined. An FS::cust_svc record will be created and inserted.
-
-=cut
-
-sub insert {
- my $self = shift;
- my $error;
-
- $error = $self->SUPER::insert;
- return $error if $error;
-
- '';
-}
-
-=item delete
-
-Delete this record from the database.
-
-=cut
-
-sub delete {
- my $self = shift;
- my $error;
-
- $error = $self->SUPER::delete;
- return $error if $error;
-
- '';
-}
-
-
-=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
-
-sub replace {
- my ( $new, $old ) = ( shift, shift );
- my $error;
-
- $error = $new->SUPER::replace($old);
- return $error if $error;
-
- '';
-}
-
-=item suspend
-
-Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=item unsuspend
-
-Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=item cancel
-
-Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
-
-=item check
-
-Checks all fields to make sure this is a valid external service. If there is
-an error, returns the error, otherwise returns false. Called by the insert
-and repalce methods.
-
-=cut
-
-sub check {
- my $self = shift;
-
- my $x = $self->setfixed;
- return $x unless ref($x);
- my $part_svc = $x;
-
- my $error =
- $self->ut_numbern('svcnum')
- || $self->ut_number('id')
- || $self->ut_textn('title')
- ;
-
- $self->SUPER::check;
-}
-
-=back
-
-=head1 BUGS
-
-=head1 SEE ALSO
-
-L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
-L<FS::cust_pkg>, schema.html from the base documentation.
-
-=cut
-
-1;
-
diff --git a/FS/FS/svc_forward.pm b/FS/FS/svc_forward.pm
index 7a121b835..2b1fb9225 100644
--- a/FS/FS/svc_forward.pm
+++ b/FS/FS/svc_forward.pm
@@ -241,7 +241,7 @@ sub check {
$self->dst('');
}
- $self->SUPER::check;
+ ''; #no error
}
=item srcsvc_acct
diff --git a/FS/FS/svc_www.pm b/FS/FS/svc_www.pm
index 7e8908346..d7a42c8ae 100644
--- a/FS/FS/svc_www.pm
+++ b/FS/FS/svc_www.pm
@@ -234,8 +234,7 @@ sub check {
return "Unknown usersvc (svc_acct.svcnum): ". $self->usersvc
unless qsearchs('svc_acct', { 'svcnum' => $self->usersvc } );
- $self->SUPER::check;
-
+ ''; #no error
}
=item domain_record
diff --git a/FS/FS/type_pkgs.pm b/FS/FS/type_pkgs.pm
index 5b3b11c09..99a79b93f 100644
--- a/FS/FS/type_pkgs.pm
+++ b/FS/FS/type_pkgs.pm
@@ -91,7 +91,7 @@ sub check {
return "Unknown pkgpart"
unless qsearchs( 'part_pkg', { 'pkgpart' => $self->pkgpart } );
- $self->SUPER::check;
+ ''; #no error
}
=item part_pkg
@@ -111,7 +111,7 @@ sub part_pkg {
=head1 VERSION
-$Id: type_pkgs.pm,v 1.3 2003-08-05 00:20:48 khoff Exp $
+$Id: type_pkgs.pm,v 1.1.14.1 2002-10-04 12:56:35 ivan Exp $
=head1 BUGS
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 3cbf0e91f..80b246f48 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -17,6 +17,7 @@ bin/freeside-deloutsourceuser
bin/freeside-deluser
bin/freeside-email
bin/freeside-expiration-alerter
+bin/freeside-overdue
bin/freeside-queued
bin/freeside-radgroup
bin/freeside-reexport
@@ -49,7 +50,6 @@ FS/agent.pm
FS/agent_type.pm
FS/cust_bill.pm
FS/cust_bill_pkg.pm
-FS/cust_bill_pkg_detail.pm
FS/cust_credit.pm
FS/cust_credit_bill.pm
FS/cust_main.pm
@@ -94,16 +94,12 @@ FS/part_pop_local.pm
FS/part_referral.pm
FS/part_svc.pm
FS/part_svc_column.pm
-FS/part_svc_router.pm
-FS/part_virtual_field.pm
FS/pkg_svc.pm
FS/svc_Common.pm
FS/svc_acct.pm
FS/svc_acct_pop.pm
-FS/svc_broadband.pm
+FS/svc_acct_sm.pm
FS/svc_domain.pm
-FS/svc_external.pm
-FS/router.pm
FS/type_pkgs.pm
FS/nas.pm
FS/port.pm
@@ -135,7 +131,6 @@ t/cust_bill.t
t/cust_bill_event.t
t/cust_bill_pay.t
t/cust_bill_pkg.t
-t/cust_bill_pkg_detail.t
t/cust_credit.t
t/cust_credit_bill.t
t/cust_credit_refund.t
@@ -185,10 +180,9 @@ t/radius_usergroup.t
t/session.t
t/svc_acct.t
t/svc_acct_pop.t
-t/svc_broadband.t
+t/svc_acct_sm.t
t/svc_Common.t
t/svc_domain.t
-t/svc_external.t
t/svc_forward.t
t/svc_www.t
t/type_pkgs.t
diff --git a/FS/bin/freeside-adduser b/FS/bin/freeside-adduser
index c3ee05b9b..424123226 100644
--- a/FS/bin/freeside-adduser
+++ b/FS/bin/freeside-adduser
@@ -1,10 +1,9 @@
#!/usr/bin/perl -w
#
-# $Id: freeside-adduser,v 1.8 2002-09-27 05:36:29 ivan Exp $
+# $Id: freeside-adduser,v 1.7 2002-08-25 01:16:30 ivan Exp $
use strict;
use vars qw($opt_h $opt_b $opt_c $opt_s);
-use Fcntl qw(:flock);
use Getopt::Std;
my $FREESIDE_CONF = "/usr/local/etc/freeside";
@@ -25,8 +24,7 @@ if ( $opt_h ) {
my $secretfile = $opt_s || 'secrets';
open(MAPSECRETS,">>$FREESIDE_CONF/mapsecrets")
- and flock(MAPSECRETS,LOCK_EX)
- or die "can't open $FREESIDE_CONF/mapsecrets: $!";
+ or die "can't open $FREESIDE_CONF/mapsecrets: $!";
print MAPSECRETS "$user $secretfile\n";
close MAPSECRETS or die "can't close $FREESIDE_CONF/mapsecrets: $!";
diff --git a/FS/bin/freeside-cc-receipts-report b/FS/bin/freeside-cc-receipts-report
index 136851aec..f4225d28a 100755
--- a/FS/bin/freeside-cc-receipts-report
+++ b/FS/bin/freeside-cc-receipts-report
@@ -245,7 +245,7 @@ user: From the mapsecrets file - see config.html from the base documentation
=head1 VERSION
-$Id: freeside-cc-receipts-report,v 1.5 2002-09-09 22:57:34 ivan Exp $
+$Id: freeside-cc-receipts-report,v 1.4.4.1 2002-09-09 22:57:32 ivan Exp $
=head1 BUGS
diff --git a/FS/bin/freeside-credit-report b/FS/bin/freeside-credit-report
index 410dabe8f..da01d3bd5 100755
--- a/FS/bin/freeside-credit-report
+++ b/FS/bin/freeside-credit-report
@@ -199,7 +199,7 @@ user: From the mapsecrets file - see config.html from the base documentation
=head1 VERSION
-$Id: freeside-credit-report,v 1.5 2002-09-09 22:57:34 ivan Exp $
+$Id: freeside-credit-report,v 1.4.4.1 2002-09-09 22:57:32 ivan Exp $
=head1 BUGS
diff --git a/FS/bin/freeside-daily b/FS/bin/freeside-daily
index 5fb966665..9ff21d421 100755
--- a/FS/bin/freeside-daily
+++ b/FS/bin/freeside-daily
@@ -120,7 +120,7 @@ the bill and collect methods of a cust_main object. See L<FS::cust_main>.
"pretend date" 15 days from whatever was specified by the -d switch
(or now, if no -d switch was given).
- -p: Only process customers with the specified payby (I<CARD>, I<DCRD>, I<CHEK>, I<DCHK>, I<BILL>, I<COMP>, I<LECB>)
+ -p: Only process customers with the specified payby (I<CARD>, I<CHEK>, I<BILL>, I<COMP>, I<LECB>)
-s: re-charge setup fees
diff --git a/FS/bin/freeside-email b/FS/bin/freeside-email
index 400dc2ac7..c7ff41114 100755
--- a/FS/bin/freeside-email
+++ b/FS/bin/freeside-email
@@ -12,9 +12,11 @@ my $user = shift or die &usage;
adminsuidsetup $user;
my $conf = new FS::Conf;
+my $domain = $conf->config('domain');
my @svc_acct = qsearch('svc_acct', {});
-my @emails = map $_->email, @svc_acct;
+my @usernames = map $_->username, @svc_acct;
+my @emails = map "$_\@$domain", @usernames;
print join("\n", @emails), "\n";
@@ -49,7 +51,7 @@ user: From the mapsecrets file - see config.html from the base documentation
=head1 VERSION
-$Id: freeside-email,v 1.2 2002-09-18 22:50:44 ivan Exp $
+$Id: freeside-email,v 1.1 2001-05-15 07:52:34 ivan Exp $
=head1 BUGS
diff --git a/FS/bin/freeside-expiration-alerter b/FS/bin/freeside-expiration-alerter
index 691fd3aa5..2c89bef20 100755
--- a/FS/bin/freeside-expiration-alerter
+++ b/FS/bin/freeside-expiration-alerter
@@ -97,7 +97,7 @@ foreach my $customer (@customers)
my $expire_time = timelocal(0,0,0,$payday,--$paymonth,$payyear);
#credit cards expire at the end of the month/year of their exp date
- if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ if ($payby eq 'CARD') {
($paymonth < 11) ? $paymonth++ : ($paymonth=0, $payyear++);
$expire_time = timelocal(0,0,0,$payday,$paymonth,$payyear);
$expire_time--;
@@ -127,7 +127,7 @@ foreach my $customer (@customers)
$FS::alerter::_template::first = $first;
$FS::alerter::_template::last = $last;
$FS::alerter::_template::company = $company;
- if ($payby eq 'CARD' || $payby eq 'DCRD') {
+ if ($payby eq 'CARD') {
$FS::alerter::_template::payby = "credit card (" .
substr($payinfo, 0, 2) . "xxxxxxxxxx" .
substr($payinfo, -4) . ")";
@@ -202,7 +202,7 @@ user: From the mapsecrets file - see config.html from the base documentation
=head1 VERSION
-$Id: freeside-expiration-alerter,v 1.5 2003-04-21 20:53:57 ivan Exp $
+$Id: freeside-expiration-alerter,v 1.3.4.1 2002-09-16 09:27:12 ivan Exp $
=head1 BUGS
diff --git a/FS/bin/freeside-overdue b/FS/bin/freeside-overdue
new file mode 100755
index 000000000..116245f9c
--- /dev/null
+++ b/FS/bin/freeside-overdue
@@ -0,0 +1,196 @@
+#!/usr/bin/perl -w
+
+use strict;
+use vars qw( $days_to_pay $cust_main $cust_pkg
+ $cust_svc $svc_acct );
+use Getopt::Std;
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::cust_svc;
+use FS::svc_acct;
+use FS::Record qw(qsearch qsearchs);
+use FS::UID qw(adminsuidsetup);
+
+&untaint_argv;
+my %opt;
+getopts('ed:qpl:scbyoi', \%opt);
+my $user = shift or die &usage;
+
+adminsuidsetup $user;
+
+my $now = time; #eventually take a time option like freeside-bill
+my ($sec,$min,$hour,$mday,$mon,$year) =
+ (localtime($now) )[0,1,2,3,4,5];
+$mon++;
+$year += 1900;
+
+foreach $cust_main ( qsearch('cust_main',{} ) ) {
+
+ my ( $eyear, $emon, $eday ) = ( 2037, 12, 31 );
+ if ( $cust_main->paydate =~ /^(\d{4})\-(\d{1,2})\-(\d{1,2})$/
+ && $cust_main->payby eq 'BILL') {
+ ( $eyear, $emon, $eday ) = ( $1, $2, $3 );
+ }
+
+ if ( ( $opt{d}
+ && $cust_main->balance_date(time - $opt{d} * 86400) > 0
+ && qsearchs( 'cust_pkg', { 'custnum' => $cust_main->custnum,
+ 'susp' => "" } ) )
+ || ( $opt{e}
+ && $cust_main->payby eq 'BILL'
+ && ( $eyear < $year
+ || ( $eyear == $year && $emon < $mon ) ) )
+ ) {
+
+ unless ( $opt{q} ) {
+ print $cust_main->custnum, "\t",
+ $cust_main->last, "\t", $cust_main->first, "\t",
+ $cust_main->balance_date(time-$opt{d} * 86400);
+ }
+
+ if ( $opt{p} && ! grep { $_ eq 'POST' } $cust_main->invoicing_list ) {
+ print "\n\tAdding postal invoicing" unless $opt{q};
+ my @invoicing_list = $cust_main->invoicing_list;
+ push @invoicing_list, 'POST';
+ $cust_main->invoicing_list(\@invoicing_list);
+ }
+
+ if ( $opt{l} ) {
+ print "\n\tCharging late fee of \$$opt{l}" unless $opt{q};
+ my $error = $cust_main->charge($opt{l}, 'Late fee');
+ # comment or plandata with info so we don't redo the same late fee every
+ # day
+ }
+
+ foreach $cust_pkg ( qsearch( 'cust_pkg',
+ { 'custnum' => $cust_main->custnum } ) ) {
+
+ if ($opt{s}) {
+ print "\n\tSuspending pkgnum " . $cust_pkg->pkgnum unless $opt{q};
+ $cust_pkg->suspend;
+ }
+
+ if ($opt{c}) {
+ print "\n\tCancelling pkgnum " . $cust_pkg->pkgnum unless $opt{q};
+ $cust_pkg->cancel;
+ }
+
+ }
+
+ if ( $opt{b} ) {
+ print "\n\tBilling" unless $opt{q};
+ my $error = $cust_main->bill('time'=>$now);
+ warn "Error billing, customer #" . $cust_main->custnum .
+ ":" . $error if $error;
+ }
+
+ if ( $opt{y} ) {
+ print "\n\tApplying outstanding payments and credits" unless $opt{q};
+ $cust_main->apply_payments;
+ $cust_main->apply_credits;
+ }
+
+ if ( $opt{o} ) {
+ print "\n\tCollecting" unless $opt{q};
+ my $error = $cust_main->collect(
+ 'invoice_time' => $now,
+ 'batch_card' => $opt{i} ? 'no' : 'yes',
+ 'force_print' => 'yes',
+ );
+ warn "Error collecting from customer #" . $cust_main->custnum. ":$error"
+ if $error;
+ }
+
+ print "\n" unless $opt{q};
+
+ }
+
+}
+
+sub untaint_argv {
+ foreach $_ ( $[ .. $#ARGV ) {
+ $ARGV[$_] =~ /^([\w\-\/\.]*)$/ || die "Illegal arguement \"$ARGV[$_]\"";
+ $ARGV[$_]=$1;
+ }
+}
+
+sub usage {
+ die "Usage:\n\n freeside-overdue [ -e ] [ -d days ] [ -q ] [ -p ] [ -l amount ] [ -s ] [ -c ] [ -b ] [ -y ] [ -o [ -i ] ] user\n";
+}
+
+
+=head1 NAME
+
+freeside-overdue - Perform actions on overdue and/or expired accounts.
+
+=head1 SYNOPSIS
+
+ freeside-overdue [ -e ] [ -d days ] [ -q ] [ -p ] [ -l amount ] [ -s ] [ -c ] [ -b ] [ -y ] [ -o [ -i ] ] user
+
+=head1 DESCRIPTION
+
+This script is deprecated in 1.4.0. You should use freeside-daily and invoice
+events instead.
+
+Performs actions on overdue and/or expired accounts.
+
+Selection options (at least one selection option is required):
+
+ -d: Customers with a balance due on invoices older than the supplied number
+ of days. Requires an integer argument.
+
+ -e: Customers with a billing expiration date in the past.
+
+Action options:
+
+ -q: Be quiet (by default, selected accounts are printed).
+
+ -p: Add postal invoicing to the relevant customers.
+
+ -l: Add a charge of the given amount to the relevant customers.
+
+ -s: Suspend accounts.
+
+ -c: Cancel accounts.
+
+ -b: Bill customers (create invoices)
+
+ -y: Apply unapplied payments and credits
+
+ -o: Collect from customers (charge cards, print invoices)
+
+ -i: real-time billing (as opposed to batch billing). only relevant
+ for credit cards.
+
+ user: From the mapsecrets file - see config.html from the base documentation
+
+=head1 CRONTAB
+
+Example crontab entries:
+
+# suspend expired accounts
+20 4 * * * freeside-overdue -e -s user
+
+# quietly add postal invoicing to customers over 30 days past due
+20 4 * * * freeside-overdue -d 30 -p -q user
+
+# suspend accounts and charge a $10.23 fee for customers over 60 days past due
+20 4 * * * freeside-overdue -d 60 -s -l 10.23 user
+
+# cancel accounts over 90 days past due
+20 4 * * * freeside-overdue -d 90 -c user
+
+=head1 ORIGINAL AUTHORS
+
+Original disable-overdue version by mw/kwh: Mark W.? and Kristian Hoffmann ?
+
+Ivan seems to be turning it into the "do-everything" CLI.
+
+=head1 BUGS
+
+Hell now that this is the do-everything CLI it should have --longoptions
+
+=cut
+
+1;
+
diff --git a/FS/bin/freeside-setup b/FS/bin/freeside-setup
index 72780e363..213dcb947 100755
--- a/FS/bin/freeside-setup
+++ b/FS/bin/freeside-setup
@@ -7,7 +7,7 @@ use strict;
use vars qw($opt_s);
use Getopt::Std;
use DBI;
-use DBIx::DBSchema 0.21;
+use DBIx::DBSchema 0.20;
use DBIx::DBSchema::Table;
use DBIx::DBSchema::Column;
use DBIx::DBSchema::ColGroup::Unique;
@@ -111,9 +111,8 @@ my($dbdef) = new DBIx::DBSchema ( map {
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 );
+} else { #add indices on ship_last and ship_company
+ push @{$cust_main->index->lol_ref}, ( ['ship_last'], ['ship_company'] )
}
#add radius attributes to svc_acct
@@ -139,6 +138,33 @@ foreach $attribute (@check_attributes) {
));
}
+##make part_svc table (but now as object)
+#
+#my($part_svc)=$dbdef->table('part_svc');
+#
+##because of svc_acct_pop
+##foreach (grep /^svc_/, $dbdef->tables) {
+##foreach (qw(svc_acct svc_acct_sm svc_charge svc_domain svc_wo)) {
+#foreach (qw(svc_acct svc_domain svc_forward svc_www)) {
+# my($table)=$dbdef->table($_);
+# my($col);
+# foreach $col ( $table->columns ) {
+# next if $col =~ /^svcnum$/;
+# $part_svc->addcolumn( new DBIx::DBSchema::Column (
+# $table->name. '__' . $table->column($col)->name,
+# 'varchar', #$table->column($col)->type,
+# 'NULL',
+# $char_d, #$table->column($col)->length,
+# ));
+# $part_svc->addcolumn ( new DBIx::DBSchema::Column (
+# $table->name. '__'. $table->column($col)->name . "_flag",
+# 'char',
+# 'NULL',
+# 1,
+# ));
+# }
+#}
+
#create history tables (false laziness w/create-history-tables)
foreach my $table ( grep { ! /^h_/ } $dbdef->tables ) {
my $tableobj = $dbdef->table($table)
@@ -190,23 +216,7 @@ foreach my $table ( grep { ! /^h_/ } $dbdef->tables ) {
'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
+ map { $tableobj->column($_) } $tableobj->columns
],
} );
$dbdef->addtable($h_tableobj);
@@ -291,8 +301,6 @@ foreach my $aref (
[ 'COMP', 'Comp invoice', '$cust_bill->comp();', 30, 'comp' ],
[ 'CARD', 'Batch card', '$cust_bill->batch_card();', 40, 'batch-card' ],
[ 'BILL', 'Send invoice', '$cust_bill->send();', 50, 'send' ],
- [ 'DCRD', 'Send invoice', '$cust_bill->send();', 50, 'send' ],
- [ 'DCHK', 'Send invoice', '$cust_bill->send();', 50, 'send' ],
) {
my $part_bill_event = new FS::part_bill_event({
@@ -331,23 +339,20 @@ sub tables_hash_hack {
'agent' => {
'columns' => [
- 'agentnum', 'serial', '', '',
+ 'agentnum', 'int', '', '',
'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'] ],
+ 'index' => [ ['typenum'] ],
},
'agent_type' => {
'columns' => [
- 'typenum', 'serial', '', '',
+ 'typenum', 'int', '', '',
'atype', 'varchar', '', $char_d,
],
'primary_key' => 'typenum',
@@ -367,7 +372,7 @@ sub tables_hash_hack {
'cust_bill' => {
'columns' => [
- 'invnum', 'serial', '', '',
+ 'invnum', 'int', '', '',
'custnum', 'int', '', '',
'_date', @date_type,
'charged', @money_type,
@@ -381,7 +386,7 @@ sub tables_hash_hack {
'cust_bill_event' => {
'columns' => [
- 'eventnum', 'serial', '', '',
+ 'eventnum', 'int', '', '',
'invnum', 'int', '', '',
'eventpart', 'int', '', '',
'_date', @date_type,
@@ -396,7 +401,7 @@ sub tables_hash_hack {
'part_bill_event' => {
'columns' => [
- 'eventpart', 'serial', '', '',
+ 'eventpart', 'int', '', '',
'payby', 'char', '', 4,
'event', 'varchar', '', $char_d,
'eventcode', @perl_type,
@@ -408,7 +413,7 @@ sub tables_hash_hack {
],
'primary_key' => 'eventpart',
'unique' => [],
- 'index' => [ ['payby'], ['disabled'], ],
+ 'index' => [ ['payby'] ],
},
'cust_bill_pkg' => {
@@ -419,32 +424,19 @@ sub tables_hash_hack {
'recur', @money_type,
'sdate', @date_type,
'edate', @date_type,
- 'itemdesc', 'varchar', 'NULL', $char_d,
],
'primary_key' => '',
- 'unique' => [],
+ 'unique' => [ ['pkgnum', 'invnum'] ],
'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', '', '',
+ 'crednum', 'int', '', '',
'custnum', 'int', '', '',
'_date', @date_type,
'amount', @money_type,
- 'otaker', 'varchar', '', 32,
+ 'otaker', 'varchar', '', 8,
'reason', 'text', 'NULL', '',
'closed', 'char', 'NULL', 1,
],
@@ -455,7 +447,7 @@ sub tables_hash_hack {
'cust_credit_bill' => {
'columns' => [
- 'creditbillnum', 'serial', '', '',
+ 'creditbillnum', 'int', '', '',
'crednum', 'int', '', '',
'invnum', 'int', '', '',
'_date', @date_type,
@@ -468,13 +460,13 @@ sub tables_hash_hack {
'cust_main' => {
'columns' => [
- 'custnum', 'serial', '', '',
+ 'custnum', 'int', '', '',
'agentnum', 'int', '', '',
# 'titlenum', 'int', 'NULL', '',
'last', 'varchar', '', $char_d,
# 'middle', 'varchar', 'NULL', $char_d,
'first', 'varchar', '', $char_d,
- 'ss', 'varchar', 'NULL', 11,
+ 'ss', 'char', 'NULL', 11,
'company', 'varchar', 'NULL', $char_d,
'address1', 'varchar', '', $char_d,
'address2', 'varchar', 'NULL', $char_d,
@@ -502,12 +494,11 @@ sub tables_hash_hack {
'ship_fax', 'varchar', 'NULL', 12,
'payby', 'char', '', 4,
'payinfo', 'varchar', 'NULL', $char_d,
- 'paycvv', 'varchar', 'NULL', 4,
#'paydate', @date_type,
'paydate', 'varchar', 'NULL', 10,
'payname', 'varchar', 'NULL', $char_d,
'tax', 'char', 'NULL', 1,
- 'otaker', 'varchar', '', 32,
+ 'otaker', 'varchar', '', 8,
'refnum', 'int', '', '',
'referral_custnum', 'int', 'NULL', '',
'comments', 'text', 'NULL', '',
@@ -515,14 +506,12 @@ sub tables_hash_hack {
'primary_key' => 'custnum',
'unique' => [],
#'index' => [ ['last'], ['company'] ],
- 'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ],
- [ 'daytime' ], [ 'night' ], [ 'fax' ],
- ],
+ 'index' => [ ['last'], [ 'company' ], [ 'referral_custnum' ] ],
},
'cust_main_invoice' => {
'columns' => [
- 'destnum', 'serial', '', '',
+ 'destnum', 'int', '', '',
'custnum', 'int', '', '',
'dest', 'varchar', '', $char_d,
],
@@ -535,16 +524,13 @@ sub tables_hash_hack {
#cust_main_county for validation and to provide
# a tax rate.
'columns' => [
- 'taxnum', 'serial', '', '',
+ 'taxnum', 'int', '', '',
'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' => [],
@@ -554,7 +540,7 @@ sub tables_hash_hack {
'cust_pay' => {
'columns' => [
- 'paynum', 'serial', '', '',
+ 'paynum', 'int', '', '',
#now cust_bill_pay #'invnum', 'int', '', '',
'custnum', 'int', '', '',
'paid', @money_type,
@@ -567,12 +553,12 @@ sub tables_hash_hack {
],
'primary_key' => 'paynum',
'unique' => [],
- 'index' => [ [ 'custnum' ], [ 'paybatch' ], [ 'payby' ], [ '_date' ] ],
+ 'index' => [ [ 'custnum' ], [ 'paybatch' ] ],
},
'cust_bill_pay' => {
'columns' => [
- 'billpaynum', 'serial', '', '',
+ 'billpaynum', 'int', '', '',
'invnum', 'int', '', '',
'paynum', 'int', '', '',
'amount', @money_type,
@@ -586,7 +572,7 @@ sub tables_hash_hack {
'cust_pay_batch' => { #what's this used for again? list of customers
#in current CARD batch? (necessarily CARD?)
'columns' => [
- 'paybatchnum', 'serial', '', '',
+ 'paybatchnum', 'int', '', '',
'invnum', 'int', '', '',
'custnum', 'int', '', '',
'last', 'varchar', '', $char_d,
@@ -611,13 +597,12 @@ sub tables_hash_hack {
'cust_pkg' => {
'columns' => [
- 'pkgnum', 'serial', '', '',
+ 'pkgnum', 'int', '', '',
'custnum', 'int', '', '',
'pkgpart', 'int', '', '',
- 'otaker', 'varchar', '', 32,
+ 'otaker', 'varchar', '', 8,
'setup', @date_type,
'bill', @date_type,
- 'last_bill', @date_type,
'susp', @date_type,
'cancel', @date_type,
'expire', @date_type,
@@ -630,12 +615,12 @@ sub tables_hash_hack {
'cust_refund' => {
'columns' => [
- 'refundnum', 'serial', '', '',
+ 'refundnum', 'int', '', '',
#now cust_credit_refund #'crednum', 'int', '', '',
'custnum', 'int', '', '',
'_date', @date_type,
'refund', @money_type,
- 'otaker', 'varchar', '', 32,
+ 'otaker', 'varchar', '', 8,
'reason', 'varchar', '', $char_d,
'payby', 'char', '', 4, # CARD/BILL/COMP, should be index
# into payment type table.
@@ -650,7 +635,7 @@ sub tables_hash_hack {
'cust_credit_refund' => {
'columns' => [
- 'creditrefundnum', 'serial', '', '',
+ 'creditrefundnum', 'int', '', '',
'crednum', 'int', '', '',
'refundnum', 'int', '', '',
'amount', @money_type,
@@ -664,7 +649,7 @@ sub tables_hash_hack {
'cust_svc' => {
'columns' => [
- 'svcnum', 'serial', '', '',
+ 'svcnum', 'int', '', '',
'pkgnum', 'int', 'NULL', '',
'svcpart', 'int', '', '',
],
@@ -675,11 +660,11 @@ sub tables_hash_hack {
'part_pkg' => {
'columns' => [
- 'pkgpart', 'serial', '', '',
+ 'pkgpart', 'int', '', '',
'pkg', 'varchar', '', $char_d,
'comment', 'varchar', '', $char_d,
'setup', @perl_type,
- 'freq', 'varchar', '', $char_d, #billing frequency
+ 'freq', 'int', '', '', #billing frequency (months)
'recur', @perl_type,
'setuptax', 'char', 'NULL', 1,
'recurtax', 'char', 'NULL', 1,
@@ -708,7 +693,6 @@ sub tables_hash_hack {
'pkgpart', 'int', '', '',
'svcpart', 'int', '', '',
'quantity', 'int', '', '',
- 'primary_svc','char', 'NULL', 1,
],
'primary_key' => '',
'unique' => [ ['pkgpart', 'svcpart'] ],
@@ -717,18 +701,17 @@ sub tables_hash_hack {
'part_referral' => {
'columns' => [
- 'refnum', 'serial', '', '',
+ 'refnum', 'int', '', '',
'referral', 'varchar', '', $char_d,
- 'disabled', 'char', 'NULL', 1,
],
'primary_key' => 'refnum',
'unique' => [],
- 'index' => [ ['disabled'] ],
+ 'index' => [],
},
'part_svc' => {
'columns' => [
- 'svcpart', 'serial', '', '',
+ 'svcpart', 'int', '', '',
'svc', 'varchar', '', $char_d,
'svcdb', 'varchar', '', $char_d,
'disabled', 'char', 'NULL', 1,
@@ -740,7 +723,7 @@ sub tables_hash_hack {
'part_svc_column' => {
'columns' => [
- 'columnnum', 'serial', '', '',
+ 'columnnum', 'int', '', '',
'svcpart', 'int', '', '',
'columnname', 'varchar', '', 64,
'columnvalue', 'varchar', 'NULL', $char_d,
@@ -754,7 +737,7 @@ sub tables_hash_hack {
#(this should be renamed to part_pop)
'svc_acct_pop' => {
'columns' => [
- 'popnum', 'serial', '', '',
+ 'popnum', 'int', '', '',
'city', 'varchar', '', $char_d,
'state', 'varchar', '', $char_d,
'ac', 'char', '', 3,
@@ -768,7 +751,7 @@ sub tables_hash_hack {
'part_pop_local' => {
'columns' => [
- 'localnum', 'serial', '', '',
+ 'localnum', 'int', '', '',
'popnum', 'int', '', '',
'city', 'varchar', 'NULL', $char_d,
'state', 'char', 'NULL', 2,
@@ -803,6 +786,18 @@ sub tables_hash_hack {
'index' => [ ['username'], ['domsvc'] ],
},
+# 'svc_acct_sm' => {
+# 'columns' => [
+# 'svcnum', 'int', '', '',
+# 'domsvc', 'int', '', '',
+# 'domuid', 'int', '', '',
+# 'domuser', 'varchar', '', $char_d,
+# ],
+# 'primary_key' => 'svcnum',
+# 'unique' => [ [] ],
+# 'index' => [ ['domsvc'], ['domuid'] ],
+# },
+
#'svc_charge' => {
# 'columns' => [
# 'svcnum', 'int', '', '',
@@ -826,14 +821,12 @@ sub tables_hash_hack {
'domain_record' => {
'columns' => [
- 'recnum', 'serial', '', '',
+ 'recnum', 'int', '', '',
'svcnum', 'int', '', '',
- #'reczone', 'varchar', '', $char_d,
- 'reczone', 'varchar', '', 255,
+ 'reczone', 'varchar', '', $char_d,
'recaf', 'char', '', 2,
- 'rectype', 'varchar', '', 5,
- #'recdata', 'varchar', '', $char_d,
- 'recdata', 'varchar', '', 255,
+ 'rectype', 'char', '', 5,
+ 'recdata', 'varchar', '', $char_d,
],
'primary_key' => 'recnum',
'unique' => [],
@@ -878,7 +871,7 @@ sub tables_hash_hack {
'prepay_credit' => {
'columns' => [
- 'prepaynum', 'serial', '', '',
+ 'prepaynum', 'int', '', '',
'identifier', 'varchar', '', $char_d,
'amount', @money_type,
'seconds', 'int', 'NULL', '',
@@ -890,7 +883,7 @@ sub tables_hash_hack {
'port' => {
'columns' => [
- 'portnum', 'serial', '', '',
+ 'portnum', 'int', '', '',
'ip', 'varchar', 'NULL', 15,
'nasport', 'int', 'NULL', '',
'nasnum', 'int', '', '',
@@ -902,7 +895,7 @@ sub tables_hash_hack {
'nas' => {
'columns' => [
- 'nasnum', 'serial', '', '',
+ 'nasnum', 'int', '', '',
'nas', 'varchar', '', $char_d,
'nasip', 'varchar', '', 15,
'nasfqdn', 'varchar', '', $char_d,
@@ -915,7 +908,7 @@ sub tables_hash_hack {
'session' => {
'columns' => [
- 'sessionnum', 'serial', '', '',
+ 'sessionnum', 'int', '', '',
'portnum', 'int', '', '',
'svcnum', 'int', '', '',
'login', @date_type,
@@ -928,7 +921,7 @@ sub tables_hash_hack {
'queue' => {
'columns' => [
- 'jobnum', 'serial', '', '',
+ 'jobnum', 'int', '', '',
'job', 'text', '', '',
'_date', 'int', '', '',
'status', 'varchar', '', $char_d,
@@ -942,7 +935,7 @@ sub tables_hash_hack {
'queue_arg' => {
'columns' => [
- 'argnum', 'serial', '', '',
+ 'argnum', 'int', '', '',
'jobnum', 'int', '', '',
'arg', 'text', 'NULL', '',
],
@@ -953,7 +946,7 @@ sub tables_hash_hack {
'queue_depend' => {
'columns' => [
- 'dependnum', 'serial', '', '',
+ 'dependnum', 'int', '', '',
'jobnum', 'int', '', '',
'depend_jobnum', 'int', '', '',
],
@@ -964,7 +957,7 @@ sub tables_hash_hack {
'export_svc' => {
'columns' => [
- 'exportsvcnum' => 'serial', '', '',
+ 'exportsvcnum' => 'int', '', '',
'exportnum' => 'int', '', '',
'svcpart' => 'int', '', '',
],
@@ -975,7 +968,7 @@ sub tables_hash_hack {
'part_export' => {
'columns' => [
- 'exportnum', 'serial', '', '',
+ 'exportnum', 'int', '', '',
#'svcpart', 'int', '', '',
'machine', 'varchar', '', $char_d,
'exporttype', 'varchar', '', $char_d,
@@ -988,7 +981,7 @@ sub tables_hash_hack {
'part_export_option' => {
'columns' => [
- 'optionnum', 'serial', '', '',
+ 'optionnum', 'int', '', '',
'exportnum', 'int', '', '',
'optionname', 'varchar', '', $char_d,
'optionvalue', 'text', 'NULL', '',
@@ -1000,7 +993,7 @@ sub tables_hash_hack {
'radius_usergroup' => {
'columns' => [
- 'usergroupnum', 'serial', '', '',
+ 'usergroupnum', 'int', '', '',
'svcnum', 'int', '', '',
'groupname', 'varchar', '', $char_d,
],
@@ -1011,7 +1004,7 @@ sub tables_hash_hack {
'msgcat' => {
'columns' => [
- 'msgnum', 'serial', '', '',
+ 'msgnum', 'int', '', '',
'msgcode', 'varchar', '', $char_d,
'locale', 'varchar', '', 16,
'msg', 'text', '', '',
@@ -1023,7 +1016,7 @@ sub tables_hash_hack {
'cust_tax_exempt' => {
'columns' => [
- 'exemptnum', 'serial', '', '',
+ 'exemptnum', 'int', '', '',
'custnum', 'int', '', '',
'taxnum', 'int', '', '',
'year', 'int', '', '',
@@ -1035,102 +1028,7 @@ sub tables_hash_hack {
'index' => [],
},
- 'router' => {
- 'columns' => [
- 'routernum', 'serial', '', '',
- 'routername', 'varchar', '', $char_d,
- 'svcnum', 'int', 'NULL', '',
- ],
- 'primary_key' => 'routernum',
- 'unique' => [],
- 'index' => [],
- },
- 'part_svc_router' => {
- 'columns' => [
- 'svcpart', 'int', '', '',
- 'routernum', 'int', '', '',
- ],
- 'primary_key' => '',
- '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' => [
- 'recnum', 'int', '', '',
- 'vfieldpart', 'int', '', '',
- 'value', 'varchar', '', 128,
- ],
- 'primary_key' => '',
- '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', '', '',
- 'title', 'varchar', 'NULL', $char_d,
- ],
- 'primary_key' => 'svcnum',
- 'unique' => [],
- 'index' => [],
- },
);
diff --git a/FS/bin/freeside-tax-report b/FS/bin/freeside-tax-report
index 240f3ad37..d48da87a6 100755
--- a/FS/bin/freeside-tax-report
+++ b/FS/bin/freeside-tax-report
@@ -267,7 +267,7 @@ user: From the mapsecrets file - see config.html from the base documentation
=head1 VERSION
-$Id: freeside-tax-report,v 1.5 2002-09-09 22:57:34 ivan Exp $
+$Id: freeside-tax-report,v 1.4.4.1 2002-09-09 22:57:32 ivan Exp $
=head1 BUGS
diff --git a/FS/t/cust_bill_pkg_detail.t b/FS/t/cust_bill_pkg_detail.t
deleted file mode 100644
index ea6e3d125..000000000
--- a/FS/t/cust_bill_pkg_detail.t
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::cust_bill_pkg_detail;
-$loaded=1;
-print "ok 1\n";
diff --git a/FS/t/svc_external.t b/FS/t/svc_acct_sm.t
index 20a676784..1082f2cdb 100644
--- a/FS/t/svc_external.t
+++ b/FS/t/svc_acct_sm.t
@@ -1,5 +1,5 @@
BEGIN { $| = 1; print "1..1\n" }
END {print "not ok 1\n" unless $loaded;}
-use FS::svc_external;
+use FS::svc_acct_sm;
$loaded=1;
print "ok 1\n";
diff --git a/FS/t/svc_broadband.t b/FS/t/svc_broadband.t
deleted file mode 100644
index 02dc1124a..000000000
--- a/FS/t/svc_broadband.t
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN { $| = 1; print "1..1\n" }
-END {print "not ok 1\n" unless $loaded;}
-use FS::svc_broadband;
-$loaded=1;
-print "ok 1\n";