Richard Siddall <richard.siddall@elirion.net> sent in Mason fixes and other
things I'm probably forgetting.
+Contains "JS Calendar" v0.9.3 <http://dynarch.com/mishoo/calendar.epl>
+by Mihai Bazon <mishoo@infoiasi.ro> licensed under the terms of the GNU LGPL.
+
+Latex invoice template based on a template from eBills
+<http://ebills.sourceforge.net/> by Mark Asplen-Taylor <mark@asplen.co.uk>,
+licensed under the terms fo the GNU GPL.
+
Everything else is my (Ivan Kohler <ivan@420.am>) fault.
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
L<FS::svc_www> - Web virtual host class.
+L<FS::svc_broadband> - DSL, wireless and other broadband class.
+
+L<FS::svc_external> - Externally tracked service class.
+
L<FS::part_svc> - Service definition class
L<FS::part_svc_column> - Column constraint class
=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;
=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);
sub popurl {
my($up)=@_;
my $cgi = &FS::UID::cgi;
- my $url = new URI::URL ( $cgi->isa('Apache') ? $cgi->uri : $cgi->url );
+ my $url_string = $cgi->isa('Apache') ? $cgi->uri : $cgi->url;
+ $url_string =~ s/\?.*//;
+ my $url = new URI::URL ( $url_string );
my(@path)=$url->path_components;
splice @path, 0-$up;
$url->path_components(@path);
=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">!;
use FS::cust_main;
use FS::cust_bill;
use FS::cust_main_county;
+use FS::cust_pkg;
use FS::ClientAPI; #hmm
FS::ClientAPI->register_handlers(
'MyAccount/login' => \&login,
'MyAccount/customer_info' => \&customer_info,
+ '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,
+ 'MyAccount/charge' => \&charge,
+);
+
+use vars qw( @cust_main_editable_fields );
+@cust_main_editable_fields = qw(
+ first last company address1 address2 city
+ county state zip country daytime night fax
+ ship_first ship_last ship_company ship_address1 ship_address2 ship_city
+ ship_state ship_zip ship_country ship_daytime ship_night ship_fax
);
#store in db?
-my $cache = new Cache::SharedMemoryCache();
+my $cache = new Cache::SharedMemoryCache( {
+ 'namespace' => 'FS::ClientAPI::MyAccount',
+} );
#false laziness w/FS::ClientAPI::passwd::passwd (needs to handle encrypted pw)
sub login {
$return{name} = $cust_main->first. ' '. $cust_main->get('last');
+ for (@cust_main_editable_fields) {
+ $return{$_} = $cust_main->get($_);
+ }
+
} else { #no customer record
my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $session->{'svcnum'} } )
}
+sub edit_info {
+ my $p = shift;
+ my $session = $cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'}
+ or return { 'error' => "no customer record" };
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $new = new FS::cust_main { $cust_main->hash };
+ $new->set( $_ => $p->{$_} )
+ foreach grep { exists $p->{$_} } @cust_main_editable_fields;
+ my $error = $new->replace($cust_main);
+ return { 'error' => $error } if $error;
+ #$cust_main = $new;
+
+ return { 'error' => '' };
+}
+
sub payment_info {
my $p = shift;
my $session = $cache->get($p->{'session_id'})
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 )
+ qw( payname address1 address2 city state zip payinfo paybatch )
);
return { 'error' => $error } if $error;
my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
or return { 'error' => "unknown custnum $custnum" };
- my @errors = $cust_main->cancel;
+ my @errors = $cust_main->cancel( 'quiet'=>1 );
my $error = scalar(@errors) ? join(' / ', @errors) : '';
}
+sub list_pkgs {
+ my $p = shift;
+ my $session = $cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ return { 'cust_pkg' => [ map { $_->hashref } $cust_main->ncancelled_pkgs ] };
+
+}
+
+sub order_pkg {
+ my $p = shift;
+ my $session = $cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ #false laziness w/ClientAPI/Signup.pm
+
+ my $cust_pkg = new FS::cust_pkg ( {
+ 'custnum' => $custnum,
+ 'pkgpart' => $p->{'pkgpart'},
+ } );
+ my $error = $cust_pkg->check;
+ return { 'error' => $error } if $error;
+
+ my $svc_acct = new FS::svc_acct ( {
+ 'svcpart' => $p->{'svcpart'} || $cust_pkg->part_pkg->svcpart('svc_acct'),
+ map { $_ => $p->{$_} }
+ qw( username _password sec_phrase popnum ),
+ } );
+
+ my @acct_snarf;
+ my $snarfnum = 1;
+ while ( length($p->{"snarf_machine$snarfnum"}) ) {
+ my $acct_snarf = new FS::acct_snarf ( {
+ 'machine' => $p->{"snarf_machine$snarfnum"},
+ 'protocol' => $p->{"snarf_protocol$snarfnum"},
+ 'username' => $p->{"snarf_username$snarfnum"},
+ '_password' => $p->{"snarf_password$snarfnum"},
+ } );
+ $snarfnum++;
+ push @acct_snarf, $acct_snarf;
+ }
+ $svc_acct->child_objects( \@acct_snarf );
+
+ my $y = $svc_acct->setdefault; # arguably should be in new method
+ return { 'error' => $y } if $y && !ref($y);
+
+ $error = $svc_acct->check;
+ return { 'error' => $error } if $error;
+
+ use Tie::RefHash;
+ tie my %hash, 'Tie::RefHash';
+ %hash = ( $cust_pkg => [ $svc_acct ] );
+ #msgcat
+ $error = $cust_main->order_pkgs( \%hash, '', 'noexport' => 1 );
+ return { 'error' => $error } if $error;
+
+ my $conf = new FS::Conf;
+ if ( $conf->exists('signup_server-realtime') ) {
+
+ my $old_balance = $cust_main->balance;
+
+ my $bill_error = $cust_main->bill;
+ $cust_main->apply_payments;
+ $cust_main->apply_credits;
+ $bill_error = $cust_main->collect;
+
+ if ( $cust_main->balance > $old_balance ) {
+ $cust_pkg->cancel('quiet'=>1);
+ return { 'error' => '_decline' };
+ } else {
+ $cust_pkg->reexport;
+ }
+
+ } else {
+ $cust_pkg->reexport;
+ }
+
+ return { error => '' };
+
+}
+
+sub cancel_pkg {
+ my $p = shift;
+ my $session = $cache->get($p->{'session_id'})
+ or return { 'error' => "Can't resume session" }; #better error message
+
+ my $custnum = $session->{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or return { 'error' => "unknown custnum $custnum" };
+
+ my $pkgnum = $session->{'pkgnum'};
+
+ my $cust_pkg = qsearchs('cust_pkg', { 'custnum' => $custnum,
+ 'pkgnum' => $pkgnum, } )
+ or return { 'error' => "unknown pkgnum $pkgnum" };
+
+ my $error = $cust_main->cancel( 'quiet'=>1 );
+ return { 'error' => $error };
+
+}
+
1;
--- /dev/null
+package FS::ClientAPI::Signup;
+
+use strict;
+use Tie::RefHash;
+use FS::Conf;
+use FS::Record qw(qsearch qsearchs dbdef);
+use FS::agent;
+use FS::cust_main_county;
+use FS::part_pkg;
+use FS::svc_acct_pop;
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::svc_acct;
+use FS::acct_snarf;
+use FS::Msgcat qw(gettext);
+
+use FS::ClientAPI; #hmm
+FS::ClientAPI->register_handlers(
+ 'Signup/signup_info' => \&signup_info,
+ 'Signup/new_customer' => \&new_customer,
+);
+
+sub signup_info {
+ #my $packet = shift;
+
+ my $conf = new FS::Conf;
+
+ use vars qw($signup_info); #cache for performance;
+ $signup_info ||= {
+
+ 'cust_main_county' =>
+ [ map { $_->hashref } qsearch('cust_main_county', {}) ],
+
+ 'agent' =>
+ [
+ map { $_->hashref }
+ qsearch('agent', dbdef->table('agent')->column('disabled')
+ ? { 'disabled' => '' }
+ : {}
+ )
+ ],
+
+ 'part_referral' =>
+ [
+ map { $_->hashref }
+ qsearch('part_referral',
+ dbdef->table('part_referral')->column('disabled')
+ ? { 'disabled' => '' }
+ : {}
+ )
+ ],
+
+ 'agentnum2part_pkg' =>
+ {
+ map {
+ my $href = $_->pkgpart_hashref;
+ $_->agentnum =>
+ [
+ map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
+ grep { $_->svcpart('svc_acct') && $href->{ $_->pkgpart } }
+ qsearch( 'part_pkg', { 'disabled' => '' } )
+ ];
+ } qsearch('agent', dbdef->table('agent')->column('disabled')
+ ? { 'disabled' => '' }
+ : {}
+ )
+ },
+
+ 'svc_acct_pop' => [ map { $_->hashref } qsearch('svc_acct_pop',{} ) ],
+
+ 'security_phrase' => $conf->exists('security_phrase'),
+
+ 'payby' => [ $conf->config('signup_server-payby') ],
+
+ 'cvv_enabled' => defined dbdef->table('cust_main')->column('paycvv'),
+
+ 'msgcat' => { map { $_=>gettext($_) } qw(
+ passwords_dont_match invalid_card unknown_card_type not_a
+ ) },
+
+ 'statedefault' => $conf->config('statedefault') || 'CA',
+
+ 'countrydefault' => $conf->config('countrydefault') || 'US',
+
+ 'refnum' => $conf->config('signup_server-default_refnum'),
+
+ };
+
+ if (
+ $conf->config('signup_server-default_agentnum')
+ && !exists $signup_info->{'part_pkg'} #cache for performance
+ ) {
+ my $agentnum = $conf->config('signup_server-default_agentnum');
+ my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } )
+ or die "fatal: signup_server-default_agentnum $agentnum not found\n";
+ my $pkgpart_href = $agent->pkgpart_hashref;
+
+ $signup_info->{'part_pkg'} = [
+ #map { $_->hashref }
+ map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
+ grep { $_->svcpart('svc_acct') && $pkgpart_href->{ $_->pkgpart } }
+ qsearch( 'part_pkg', { 'disabled' => '' } )
+ ];
+ }
+
+ $signup_info;
+
+}
+
+sub new_customer {
+ my $packet = shift;
+
+ my $conf = new FS::Conf;
+
+ #things that aren't necessary in base class, but are for signup server
+ #return "Passwords don't match"
+ # if $hashref->{'_password'} ne $hashref->{'_password2'}
+ return { 'error' => gettext('empty_password') }
+ unless $packet->{'_password'};
+ # a bit inefficient for large numbers of pops
+ return { 'error' => gettext('no_access_number_selected') }
+ unless $packet->{'popnum'} || !scalar(qsearch('svc_acct_pop',{} ));
+
+ #shares some stuff with htdocs/edit/process/cust_main.cgi... take any
+ # common that are still here and library them.
+ my $cust_main = new FS::cust_main ( {
+ #'custnum' => '',
+ 'agentnum' => $packet->{agentnum}
+ || $conf->config('signup_server-default_agentnum'),
+ 'refnum' => $packet->{refnum}
+ || $conf->config('signup_server-default_refnum'),
+
+ map { $_ => $packet->{$_} } qw(
+ last first ss company address1 address2 city county state zip country
+ daytime night fax payby payinfo paycvv paydate payname referral_custnum
+ comments
+ ),
+
+ } );
+
+ return { 'error' => "Illegal payment type" }
+ unless grep { $_ eq $packet->{'payby'} }
+ $conf->config('signup_server-payby');
+
+ $cust_main->payinfo($cust_main->daytime)
+ if $cust_main->payby eq 'LECB' && ! $cust_main->payinfo;
+
+ my @invoicing_list = split( /\s*\,\s*/, $packet->{'invoicing_list'} );
+
+ $packet->{'pkgpart'} =~ /^(\d+)$/ or '' =~ /^()$/;
+ my $pkgpart = $1;
+ return { 'error' => 'Please select a package' } unless $pkgpart; #msgcat
+
+ my $part_pkg =
+ qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } )
+ or return { 'error' => "WARNING: unknown pkgpart: $pkgpart" };
+ my $svcpart = $part_pkg->svcpart('svc_acct');
+
+ my $cust_pkg = new FS::cust_pkg ( {
+ #later#'custnum' => $custnum,
+ 'pkgpart' => $packet->{'pkgpart'},
+ } );
+ my $error = $cust_pkg->check;
+ return { 'error' => $error } if $error;
+
+ my $svc_acct = new FS::svc_acct ( {
+ 'svcpart' => $svcpart,
+ map { $_ => $packet->{$_} }
+ qw( username _password sec_phrase popnum ),
+ } );
+
+ my @acct_snarf;
+ my $snarfnum = 1;
+ while ( length($packet->{"snarf_machine$snarfnum"}) ) {
+ my $acct_snarf = new FS::acct_snarf ( {
+ 'machine' => $packet->{"snarf_machine$snarfnum"},
+ 'protocol' => $packet->{"snarf_protocol$snarfnum"},
+ 'username' => $packet->{"snarf_username$snarfnum"},
+ '_password' => $packet->{"snarf_password$snarfnum"},
+ } );
+ $snarfnum++;
+ push @acct_snarf, $acct_snarf;
+ }
+ $svc_acct->child_objects( \@acct_snarf );
+
+ my $y = $svc_acct->setdefault; # arguably should be in new method
+ return { 'error' => $y } if $y && !ref($y);
+
+ $error = $svc_acct->check;
+ return { 'error' => $error } if $error;
+
+ use Tie::RefHash;
+ tie my %hash, 'Tie::RefHash';
+ %hash = ( $cust_pkg => [ $svc_acct ] );
+ #msgcat
+ $error = $cust_main->insert( \%hash, \@invoicing_list, 'noexport' => 1 );
+ return { 'error' => $error } if $error;
+
+ if ( $conf->exists('signup_server-realtime') ) {
+
+ #warn "[fs_signup_server] Billing customer...\n" if $Debug;
+
+ my $bill_error = $cust_main->bill;
+ #warn "[fs_signup_server] error billing new customer: $bill_error"
+ # if $bill_error;
+
+ $cust_main->apply_payments;
+ $cust_main->apply_credits;
+
+ $bill_error = $cust_main->collect;
+ #warn "[fs_signup_server] error collecting from new customer: $bill_error"
+ # if $bill_error;
+
+ if ( $cust_main->balance > 0 ) {
+
+ #this makes sense. credit is "un-doing" the invoice
+ $cust_main->credit( $cust_main->balance, 'signup server decline' );
+ $cust_main->apply_credits;
+
+ #should check list for errors...
+ #$cust_main->suspend;
+ local $FS::svc_Common::noexport_hack = 1;
+ $cust_main->cancel('quiet'=>1);
+
+ return { 'error' => '_decline' };
+ }
+
+ }
+ $cust_main->reexport;
+
+ return { error => '' };
+
+}
+
+1;
my $self = shift;
#quelle kludge
@config_items,
- map {
+ ( map {
my $basename = basename($_);
$basename =~ /^(.*)$/;
$basename = $1;
'type' => 'textarea',
}
} glob($self->dir. '/invoice_template_*')
- ;
+ ),
+ ( map {
+ my $basename = basename($_);
+ $basename =~ /^(.*)$/;
+ $basename = $1;
+ new FS::ConfItem {
+ 'key' => $basename,
+ 'section' => 'billing',
+ 'description' => 'Alternate LaTeX template for invoices. See the <a href="../docs/billing.html">billing documentation</a> for details.',
+ 'type' => 'textarea',
+ }
+ } glob($self->dir. '/invoice_latex_*')
+ );
}
=back
{
'key' => 'deletepayments',
'section' => 'UI',
- 'description' => 'Enable deletion of unclosed payments. Be very careful! Only delete payments that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a payment is deleted.',
+ 'description' => 'Enable deletion of unclosed payments. Be very careful! Only delete payments that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a payment is deleted.',
+ 'type' => [qw( checkbox text )],
+ },
+
+ {
+ 'key' => 'deletecredits',
+ 'section' => 'UI',
+ 'description' => 'Enable deletion of unclosed credits. Be very careful! Only delete credits that were data-entry errors, not adjustments. Optionally specify one or more comma-separated email addresses to be notified when a credit is deleted.',
'type' => [qw( checkbox text )],
},
},
{
+ 'key' => 'invoice_latex',
+ 'section' => 'billing',
+ 'description' => 'Optional LaTeX template for typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexnotes',
+ 'section' => 'billing',
+ 'description' => 'Notes section for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexfooter',
+ 'section' => 'billing',
+ 'description' => 'Footer for LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_latexsmallfooter',
+ 'section' => 'billing',
+ 'description' => 'Optional small footer for multi-page LaTeX typeset PostScript invoices.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'invoice_default_terms',
+ 'section' => 'billing',
+ 'description' => 'Optional default invoice term, used to calculate a due date printed on invoices.',
+ 'type' => 'select',
+ 'select_enum' => [ '', 'Payable upon receipt', 'Net 0', 'Net 10', 'Net 15', 'Net 30', 'Net 45', 'Net 60' ],
+ },
+
+ {
+ 'key' => 'invoice_send_receipts',
+ 'section' => 'billing',
+ 'description' => 'Send receipts for payments and credits.',
+ 'type' => 'checkbox',
+ },
+
+ {
'key' => 'lpr',
'section' => 'required',
'description' => 'Print command for paper invoices, for example `lpr -h\'',
{
'key' => 'username-ampersand',
'section' => 'username',
- 'description' => 'Allow the ampersand character (&) in usernames. Be careful when using this option in conjunction with <a href="#shellmachine-useradd">shellmachine-useradd</a> and other configuration options which execute shell commands, as the ampersand will be interpreted by the shell if not quoted.',
+ 'description' => 'Allow the ampersand character (&) in usernames. Be careful when using this option in conjunction with <a href="../browse/part_export.cgi">exports</a> which execute shell commands, as the ampersand will be interpreted by the shell if not quoted.',
'type' => 'checkbox',
},
{
'key' => 'selfservice_server-quiet',
- 'section' => '',
- 'description' => 'Disable decline and cancel emails generated by transactions initiated by the selfservice server. Not recommended, unless the customer will get instant feedback from a customer service UI, and receiving an email would be confusing/overkill.',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, the self-service server no longer sends superfluous decline and cancel emails. Used to disable decline and cancel emails generated by transactions initiated by the selfservice server.',
'type' => 'checkbox',
},
{
'key' => 'signup_server-quiet',
- 'section' => '',
- 'description' => 'Disable decline and cancel emails generated by transactions initiated by the signup server. Not recommended, unless the customer will get instant feedback from a customer service UI, and receiving an email would be confusing/overkill. Does not disable welcome emails.',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, the signup server is now part of the self-service server and no longer sends superfluous decline and cancel emails. Used to disable decline and cancel emails generated by transactions initiated by the signup server. Does not disable welcome emails.',
'type' => 'checkbox',
},
{
'key' => 'signup_server-email',
+ 'section' => 'deprecated',
+ 'description' => '<b>DEPRECATED</b>, this feature is no longer available. See the ***fill me in*** report instead. Used to contain a comma-separated list of email addresses to receive notification of signups via the signup server.',
+ 'type' => 'text',
+ },
+
+ {
+ 'key' => 'signup_server-default_agentnum',
'section' => '',
- 'description' => 'Comma-separated list of email addresses to receive notification of signups via the signup server.',
+ 'description' => 'Default agentnum for the signup server',
'type' => 'text',
},
+ {
+ 'key' => 'signup_server-default_refnum',
+ 'section' => '',
+ 'description' => 'Default advertising source number for the signup server',
+ 'type' => 'text',
+ },
{
'key' => 'show-msgcat-codes',
},
{
+ 'key' => 'emaildecline-exclude',
+ 'section' => 'billing',
+ 'description' => 'List of error messages that should not trigger email decline notices, one per line.',
+ 'type' => 'textarea',
+ },
+
+ {
'key' => 'cancelmessage',
'section' => 'billing',
'description' => 'Template file for cancellation emails.',
'type' => 'text',
},
+ {
+ 'key' => 'users-allow_comp',
+ 'section' => '',
+ 'description' => 'Usernames (Freeside users, created with <a href="../docs/man/bin/freeside-adduser.html">freeside-adduser</a>) which can create complimentary customers, one per line. If no usernames are entered, all users can create complimentary accounts.',
+ 'type' => 'textarea',
+ },
+
+ {
+ 'key' => 'cvv-save',
+ 'section' => 'billing',
+ 'description' => 'Save CVV2 information after the initial transaction for the selected credit card types. Enabling this option may be in violation of your merchant agreement(s), so please check them carefully before enabling this option for any credit card types.',
+ 'type' => 'selectmultiple',
+ 'select_enum' => [ "VISA card",
+ "MasterCard",
+ "Discover card",
+ "American Express card",
+ "Diner's Club/Carte Blanche",
+ "enRoute",
+ "JCB",
+ "BankCard",
+ ],
+ },
+
+ {
+ 'key' => 'allow_negative_charges',
+ 'section' => 'billing',
+ 'description' => 'Allow negative charges. Normally not used unless importing data from a legacy system that requires this.',
+ 'type' => 'checkbox',
+ },
+
+ {
+ 'key' => 'system_usernames',
+ 'section' => 'username',
+ 'description' => 'A list of system usernames that cannot be edited or removed, one per line. Use a bare username to prohibit modification/deletion of the username in any domain, or username@domain to prohibit modification/deletetion of a specific username and domain.',
+ 'type' => 'textarea',
+ },
);
1;
use strict;
use vars qw( $dbdef_file $dbdef $setup_hack $AUTOLOAD @ISA @EXPORT_OK $DEBUG
- $me %dbdef_cache );
+ $me %dbdef_cache %virtual_fields_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 DBIx::DBSchema 0.23;
use FS::UID qw(dbh getotaker datasrc driver_name);
use FS::SearchCache;
use FS::Msgcat qw(gettext);
@ISA = qw(Exporter);
@EXPORT_OK = qw(dbh fields hfields qsearch qsearchs dbdef jsearch);
-$DEBUG = 2;
+$DEBUG = 0;
$me = '[FS::Record]';
#ask FS::UID to run this stuff for us later
if ( ! defined( $record->{$_} ) || $record->{$_} eq '' ) {
if ( $op eq '=' ) {
if ( driver_name eq 'Pg' ) {
- if ( $dbdef->table($table)->column($column)->type =~ /(int)/i ) {
+ my $type = $dbdef->table($table)->column($column)->type;
+ if ( $type =~ /(int|serial)/i ) {
qq-( $column IS NULL )-;
} else {
qq-( $column IS NULL OR $column = '' )-;
}
} elsif ( $op eq '!=' ) {
if ( driver_name eq 'Pg' ) {
- if ( $dbdef->table($table)->column($column)->type =~ /(int)/i ) {
+ my $type = $dbdef->table($table)->column($column)->type;
+ if ( $type =~ /(int|serial)/i ) {
qq-( $column IS NOT NULL )-;
} else {
qq-( $column IS NOT NULL AND $column != '' )-;
grep defined( $record->{$_} ) && $record->{$_} ne '', @real_fields
) {
if ( $record->{$field} =~ /^\d+(\.\d+)?$/
- && $dbdef->table($table)->column($field)->type =~ /(int)/i
+ && $dbdef->table($table)->column($field)->type =~ /(int|serial)/i
) {
$sth->bind_param($bind++, $record->{$field}, { TYPE => SQL_INTEGER } );
} else {
if (@virtual_fields) {
my %v_values = map { $_, $self->getfield($_) } @virtual_fields;
- my $vfieldpart = vfieldpart_hashref($table);
+ my $vfieldpart = $self->vfieldpart_hashref;
my $v_statement = "INSERT INTO virtual_field(recnum, vfieldpart, value) ".
"VALUES (?, ?, ?)";
my $primary_key = $self->dbdef_table->primary_key;
my $v_sth;
my @del_vfields;
- my $vfp = vfieldpart_hashref($self->table);
+ my $vfp = $self->vfieldpart_hashref;
foreach($self->virtual_fields) {
next if $self->getfield($_) eq '';
unless(@del_vfields) {
=cut
sub replace {
- my ( $new, $old ) = ( shift, shift );
+ my $new = shift;
+
+ my $old;
+ if ( @_ ) {
+ $old = shift;
+ } else {
+ warn "[debug]$me replace called with no arguments; autoloading old record\n"
+ if $DEBUG;
+ my $primary_key = $new->dbdef_table->primary_key;
+ if ( $primary_key ) {
+ $old = qsearchs($new->table, { $primary_key => $new->$primary_key() } )
+ or croak "can't find ". $new->table. ".$primary_key ".
+ $new->$primary_key();
+ } else {
+ croak $new->table. " has no primary key; pass old record as argument";
+ }
+ }
+
warn "[debug]$me $new ->replace $old\n" if $DEBUG;
return "Records not in same table!" unless $new->table eq $old->table;
$old->getfield($_) eq ''
#? "( $_ IS NULL OR $_ = \"\" )"
? ( driver_name eq 'Pg'
- ? "$_ IS NULL"
+ ? "( $_ IS NULL OR $_ = '' )"
: "( $_ IS NULL OR $_ = \"\" )"
)
: "$_ = ". _quote($old->getfield($_),$old->table,$_)
my $v_rep_sth;
my $v_del_sth;
my (@add_vfields, @rep_vfields, @del_vfields);
- my $vfp = vfieldpart_hashref($old->table);
+ my $vfp = $old->vfieldpart_hashref;
foreach(grep { exists($diff{$_}) } $new->virtual_fields) {
if($diff{$_} eq '') {
# Delete
'';
}
+=item ut_snumber COLUMN
+
+Check/untaint signed numeric data (whole numbers). May not be null. If there
+is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub ut_snumber {
+ my($self, $field) = @_;
+ $self->getfield($field) =~ /^(-?)\s*(\d+)$/
+ or return "Illegal or empty (numeric) $field: ". $self->getfield($field);
+ $self->setfield($field, "$1$2");
+ '';
+}
+
=item ut_number COLUMN
Check/untaint simple numeric data (whole numbers). May not be null. If there
=cut
sub virtual_fields {
- my $something = shift;
+ my $self = shift;
my $table;
- $table = $something->table or confess "virtual_fields called on non-table";
+ $table = $self->table or confess "virtual_fields called on non-table";
confess "Unknown table $table" unless $dbdef->table($table);
- # This should be smart enough to cache results.
+ 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}};
- 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;
- return @$result;
}
=cut
sub vfieldpart_hashref {
- my ($table) = @_;
+ my $self = shift;
+ my $table = $self->table;
+
+ return {} unless $self->dbdef->table('part_virtual_field');
- return () unless $table;
my $dbh = dbh;
my $statement = "SELECT vfieldpart, name FROM part_virtual_field WHERE ".
"dbtable = '$table'";
getsecrets;
$dbh = DBI->connect($datasrc,$db_user,$db_pass, {
'AutoCommit' => 0,
- #'ChopBlanks' => 1,
+ 'ChopBlanks' => 1,
} ) or die "DBI->connect error: $DBI::errstr\n";
foreach ( keys %callback ) {
--- /dev/null
+package FS::acct_snarf;
+
+use strict;
+use vars qw( @ISA );
+use FS::Record;
+
+@ISA = qw( FS::Record );
+
+=head1 NAME
+
+FS::acct_snarf - Object methods for acct_snarf records
+
+=head1 SYNOPSIS
+
+ use FS::acct_snarf;
+
+ $record = new FS::acct_snarf \%hash;
+ $record = new FS::acct_snarf { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::svc_acct object represents an external mail account, typically for
+download of mail. FS::acct_snarf inherits from FS::Record. The following
+fields are currently supported:
+
+=over 4
+
+=item snarfnum - primary key
+
+=item svcnum - Account (see L<FS::svc_acct>)
+
+=item machine - external machine to download mail from
+
+=item protocol - protocol (pop3, imap, etc.)
+
+=item username - external login username
+
+=item _password - external login password
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record 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 { 'acct_snarf'; }
+
+=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 external mail account. 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('snarfnum')
+ || $self->ut_number('svcnum')
+ || $self->ut_foreign_key('svcnum', 'svc_acct', 'svcnum')
+ || $self->ut_domain('machine')
+ || $self->ut_alphan('protocol')
+ || $self->ut_textn('username')
+ ;
+ return $error if $error;
+
+ $self->_password =~ /^[^\t\n]*$/ or return "illegal password";
+ $self->_password($1);
+
+ ''; #no error
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
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 @used =
+( (map { $_->NetAddr->addr }
+ ($self,
+ qsearch('svc_broadband', { blocknum => $self->blocknum }))
+ ), @excludeaddr
+);
my @free = $self->NetAddr->hostenum;
while (my $ip = shift @free) {
=item freq - For future use.
+=item disabled - Disabled flag, empty or 'Y'
+
+=item username - Username for the Agent interface
+
+=item _password - Password for the Agent interface
+
=back
=head1 METHODS
;
return $error if $error;
+ if ( $self->dbdef_table->column('disabled') ) {
+ $error = $self->ut_enum('disabled', [ '', 'Y' ] );
+ return $error if $error;
+ }
+
+ if ( $self->dbdef_table->column('username') ) {
+ $error = $self->ut_alphan('username');
+ return $error if $error;
+ if ( length($self->username) ) {
+ my $conflict = qsearchs('agent', { 'username' => $self->username } );
+ return 'duplicate agent username (with '. $conflict->agent. ')'
+ if $conflict;
+ $error = $self->ut_text('password'); # ut_text... arbitrary choice
+ } else {
+ $self->_password('');
+ }
+ }
+
return "Unknown typenum!"
unless $self->agent_type;
=head1 VERSION
-$Id: agent.pm,v 1.4 2003-08-05 00:20:40 khoff Exp $
+$Id: agent.pm,v 1.6 2003-09-30 15:01:46 ivan Exp $
=head1 BUGS
}
+ if ( $conf->config('invoice_latex') ) {
+ @print_text = $self->print_ps('', $template);
+ }
+
if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal
my $lpr = $conf->config('lpr');
open(LPR, "|$lpr")
my( $self, $method ) = @_;
my $cust_main = $self->cust_main;
- my $amount = $self->owed;
+ my $balance = $cust_main->balance;
+ my $amount = ( $balance < $self->owed ) ? $balance : $self->owed;
+ $amount = sprintf("%.2f", $amount);
+ return "not run (balance $balance)" unless $amount > 0;
my $description = 'Internet Services';
if ( $conf->exists('business-onlinepayment-description') ) {
'state' => $cust_main->getfield('state'),
'zip' => $cust_main->getfield('zip'),
'country' => $cust_main->getfield('country'),
- 'trancode' => 77,
'cardnum' => $cust_main->getfield('payinfo'),
'exp' => $cust_main->getfield('paydate'),
'payname' => $cust_main->getfield('payname'),
'';
}
-=item print_text [TIME];
+=item print_text [ TIME [ , TEMPLATE ] ]
Returns an text invoice, as a list of lines.
my $pkg = $part_pkg->pkg;
if ( $cust_bill_pkg->setup != 0 ) {
- push @buf, [ "$pkg Setup",
+ my $description = $pkg;
+ $description .= ' Setup' if $cust_bill_pkg->recur != 0;
+ push @buf, [ $description,
$money_char. sprintf("%10.2f", $cust_bill_pkg->setup) ];
push @buf,
map { [ " ". $_->[0]. ": ". $_->[1], '' ] } $cust_pkg->labels;
}
#balance due
+ my $balance_due_msg = $self->balance_due_msg;
+
push @buf,['','-----------'];
- push @buf,['Balance Due', $money_char.
+ push @buf,[$balance_due_msg, $money_char.
sprintf("%10.2f", $balance_due ) ];
#create the template
}
+=item print_latex [ TIME [ , TEMPLATE ] ]
+
+Internal method - returns a filename of a filled-in LaTeX template for this
+invoice (Note: add ".tex" to get the actual filename).
+
+See print_ps and print_pdf for methods that return PostScript and PDF output.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+#still some false laziness w/print_text
+sub print_latex {
+
+ my( $self, $today, $template ) = @_;
+ $today ||= time;
+
+# my $invnum = $self->invnum;
+ my $cust_main = $self->cust_main;
+ $cust_main->payname( $cust_main->first. ' '. $cust_main->getfield('last') )
+ unless $cust_main->payname && $cust_main->payby ne 'CHEK';
+
+ my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
+# my( $cr_total, @cr_cust_credit ) = $self->cust_credit; #credits
+ #my $balance_due = $self->owed + $pr_total - $cr_total;
+ my $balance_due = $self->owed + $pr_total;
+
+ #my @collect = ();
+ #my($description,$amount);
+ @buf = ();
+
+ #create the template
+ my $templatefile = 'invoice_latex';
+ $templatefile .= "_$template" if $template;
+ my @invoice_template = $conf->config($templatefile)
+ or die "cannot load config file $templatefile";
+
+ my %invoice_data = (
+ 'invnum' => $self->invnum,
+ 'date' => time2str('%b %o, %Y', $self->_date),
+ 'agent' => _latex_escape($cust_main->agent->agent),
+ 'payname' => _latex_escape($cust_main->payname),
+ 'company' => _latex_escape($cust_main->company),
+ 'address1' => _latex_escape($cust_main->address1),
+ 'address2' => _latex_escape($cust_main->address2),
+ 'city' => _latex_escape($cust_main->city),
+ 'state' => _latex_escape($cust_main->state),
+ 'zip' => _latex_escape($cust_main->zip),
+ 'country' => _latex_escape($cust_main->country),
+ 'footer' => join("\n", $conf->config('invoice_latexfooter') ),
+ 'smallfooter' => join("\n", $conf->config('invoice_latexsmallfooter') ),
+ 'quantity' => 1,
+ 'terms' => $conf->config('invoice_default_terms') || 'Payable upon receipt',
+ #'notes' => join("\n", $conf->config('invoice_latexnotes') ),
+ );
+
+ my $countrydefault = $conf->config('countrydefault') || 'US';
+ $invoice_data{'country'} = '' if $invoice_data{'country'} eq $countrydefault;
+
+ #do variable substitutions in notes
+ $invoice_data{'notes'} =
+ join("\n",
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+ $conf->config('invoice_latexnotes')
+ );
+
+ $invoice_data{'footer'} =~ s/\n+$//;
+ $invoice_data{'smallfooter'} =~ s/\n+$//;
+ $invoice_data{'notes'} =~ s/\n+$//;
+
+ $invoice_data{'po_line'} =
+ ( $cust_main->payby eq 'BILL' && $cust_main->payinfo )
+ ? _latex_escape("Purchase Order #". $cust_main->payinfo)
+ : '~';
+
+ my @line_item = ();
+ my @total_item = ();
+ my @filled_in = ();
+ while ( @invoice_template ) {
+ my $line = shift @invoice_template;
+
+ if ( $line =~ /^%%Detail\s*$/ ) {
+
+ while ( ( my $line_item_line = shift @invoice_template )
+ !~ /^%%EndDetail\s*$/ ) {
+ push @line_item, $line_item_line;
+ }
+ foreach my $line_item ( $self->_items ) {
+ #foreach my $line_item ( $self->_items_pkg ) {
+ $invoice_data{'ref'} = $line_item->{'pkgnum'};
+ $invoice_data{'description'} = _latex_escape($line_item->{'description'});
+ if ( exists $line_item->{'ext_description'} ) {
+ $invoice_data{'description'} .=
+ "\\tabularnewline\n~~".
+ join("\\tabularnewline\n~~", map { _latex_escape($_) } @{$line_item->{'ext_description'}} );
+ }
+ $invoice_data{'amount'} = $line_item->{'amount'};
+ $invoice_data{'product_code'} = $line_item->{'pkgpart'} || 'N/A';
+ push @filled_in,
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b } @line_item;
+ }
+
+ } elsif ( $line =~ /^%%TotalDetails\s*$/ ) {
+
+ while ( ( my $total_item_line = shift @invoice_template )
+ !~ /^%%EndTotalDetails\s*$/ ) {
+ push @total_item, $total_item_line;
+ }
+
+ my @total_fill = ();
+
+ my $taxtotal = 0;
+ foreach my $tax ( $self->_items_tax ) {
+ $invoice_data{'total_item'} = _latex_escape($tax->{'description'});
+ $taxtotal += ( $invoice_data{'total_amount'} = $tax->{'amount'} );
+ push @total_fill,
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+ @total_item;
+ }
+
+ if ( $taxtotal ) {
+ $invoice_data{'total_item'} = 'Sub-total';
+ $invoice_data{'total_amount'} =
+ '\dollar '. sprintf('%.2f', $self->charged - $taxtotal );
+ unshift @total_fill,
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+ @total_item;
+ }
+
+ $invoice_data{'total_item'} = '\textbf{Total}';
+ $invoice_data{'total_amount'} =
+ '\textbf{\dollar '. sprintf('%.2f', $self->charged + $pr_total ). '}';
+ push @total_fill,
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+ @total_item;
+
+ #foreach my $thing ( sort { $a->_date <=> $b->_date } $self->_items_credits, $self->_items_payments
+
+ # credits
+ foreach my $credit ( $self->_items_credits ) {
+ $invoice_data{'total_item'} = _latex_escape($credit->{'description'});
+ #$credittotal
+ $invoice_data{'total_amount'} = '-\dollar '. $credit->{'amount'};
+ push @total_fill,
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+ @total_item;
+ }
+
+ # payments
+ foreach my $payment ( $self->_items_payments ) {
+ $invoice_data{'total_item'} = _latex_escape($payment->{'description'});
+ #$paymenttotal
+ $invoice_data{'total_amount'} = '-\dollar '. $payment->{'amount'};
+ push @total_fill,
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+ @total_item;
+ }
+
+ $invoice_data{'total_item'} = '\textbf{'. $self->balance_due_msg. '}';
+ $invoice_data{'total_amount'} =
+ '\textbf{\dollar '. sprintf('%.2f', $self->owed + $pr_total ). '}';
+ push @total_fill,
+ map { my $b=$_; $b =~ s/\$(\w+)/$invoice_data{$1}/eg; $b }
+ @total_item;
+
+ push @filled_in, @total_fill;
+
+ } else {
+ #$line =~ s/\$(\w+)/$invoice_data{$1}/eg;
+ $line =~ s/\$(\w+)/exists($invoice_data{$1}) ? $invoice_data{$1} : nounder($1)/eg;
+ push @filled_in, $line;
+ }
+
+ }
+
+ sub nounder {
+ my $var = $1;
+ $var =~ s/_/\-/g;
+ $var;
+ }
+
+ my $dir = '/tmp'; #! /usr/local/etc/freeside/invoices.datasrc/
+ my $unique = int(rand(2**31)); #UGH... use File::Temp or something
+
+ chdir($dir);
+ my $file = $self->invnum. ".$unique";
+
+ open(TEX,">$file.tex") or die "can't open $file.tex: $!\n";
+ print TEX join("\n", @filled_in ), "\n";
+ close TEX;
+
+ return $file;
+
+}
+
+=item print_ps [ TIME [ , TEMPLATE ] ]
+
+Returns an postscript invoice, as a scalar.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub print_ps {
+ my $self = shift;
+
+ my $file = $self->print_latex(@_);
+
+ #error checking!!
+ system('pslatex', "$file.tex");
+ system('pslatex', "$file.tex");
+ system('dvips', '-q', '-t', 'letter', "$file.dvi", '-o', "$file.ps" );
+
+ open(POSTSCRIPT, "<$file.ps")
+ or die "can't open $file.ps (probable error in LaTeX template): $!\n";
+
+ unlink("$file.dvi", "$file.log", "$file.aux", "$file.ps", "$file.tex");
+
+ my $ps = '';
+ while (<POSTSCRIPT>) {
+ $ps .= $_;
+ }
+
+ close POSTSCRIPT;
+
+ return $ps;
+
+}
+
+=item print_pdf [ TIME [ , TEMPLATE ] ]
+
+Returns an PDF invoice, as a scalar.
+
+TIME an optional value used to control the printing of overdue messages. The
+default is now. It isn't the date of the invoice; that's the `_date' field.
+It is specified as a UNIX timestamp; see L<perlfunc/"time">. Also see
+L<Time::Local> and L<Date::Parse> for conversion functions.
+
+=cut
+
+sub print_pdf {
+ my $self = shift;
+
+ my $file = $self->print_latex(@_);
+
+ #system('pdflatex', "$file.tex");
+ #system('pdflatex', "$file.tex");
+ #! LaTeX Error: Unknown graphics extension: .eps.
+
+ #error checking!!
+ system('pslatex', "$file.tex");
+ system('pslatex', "$file.tex");
+
+ #system('dvipdf', "$file.dvi", "$file.pdf" );
+ system("dvips -q -t letter -f $file.dvi | gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=$file.pdf -c save pop -");
+
+ open(PDF, "<$file.pdf")
+ or die "can't open $file.pdf (probably error in LaTeX tempalte: $!\n";
+
+ unlink("$file.dvi", "$file.log", "$file.aux", "$file.pdf", "$file.tex");
+
+ my $pdf = '';
+ while (<PDF>) {
+ $pdf .= $_;
+ }
+
+ close PDF;
+
+ return $pdf;
+
+}
+
+# quick subroutine for print_latex
+#
+# There are ten characters that LaTeX treats as special characters, which
+# means that they do not simply typeset themselves:
+# # $ % & ~ _ ^ \ { }
+#
+# TeX ignores blanks following an escaped character; if you want a blank (as
+# in "10% of ..."), you have to "escape" the blank as well ("10\%\ of ...").
+
+sub _latex_escape {
+ my $value = shift;
+ $value =~ s/([#\$%&~_\^{}])( )?/"\\$1". ( length($2) ? "\\$2" : '' )/ge;
+ $value;
+}
+
+#utility methods for print_*
+
+sub balance_due_msg {
+ my $self = shift;
+ my $msg = 'Balance Due';
+ return $msg unless $conf->exists('invoice_default_terms');
+ if ( $conf->config('invoice_default_terms') =~ /^\s*Net\s*(\d+)\s*$/ ) {
+ $msg .= ' - Please pay by '. time2str("%x", $self->_date + ($1*86400) );
+ } elsif ( $conf->config('invoice_default_terms') ) {
+ $msg .= ' - '. $conf->config('invoice_default_terms');
+ }
+ $msg;
+}
+
+sub _items {
+ my $self = shift;
+ my @display = scalar(@_)
+ ? @_
+ : qw( _items_previous _items_pkg );
+ #: qw( _items_pkg );
+ #: qw( _items_previous _items_pkg _items_tax _items_credits _items_payments );
+ my @b = ();
+ foreach my $display ( @display ) {
+ push @b, $self->$display(@_);
+ }
+ @b;
+}
+
+sub _items_previous {
+ my $self = shift;
+ my $cust_main = $self->cust_main;
+ my( $pr_total, @pr_cust_bill ) = $self->previous; #previous balance
+ my @b = ();
+ foreach ( @pr_cust_bill ) {
+ push @b, {
+ 'description' => 'Previous Balance, Invoice #'. $_->invnum.
+ ' ('. time2str('%x',$_->_date). ')',
+ #'pkgpart' => 'N/A',
+ 'pkgnum' => 'N/A',
+ 'amount' => sprintf("%10.2f", $_->owed),
+ };
+ }
+ @b;
+
+ #{
+ # 'description' => 'Previous Balance',
+ # #'pkgpart' => 'N/A',
+ # 'pkgnum' => 'N/A',
+ # 'amount' => sprintf("%10.2f", $pr_total ),
+ # 'ext_description' => [ map {
+ # "Invoice ". $_->invnum.
+ # " (". time2str("%x",$_->_date). ") ".
+ # sprintf("%10.2f", $_->owed)
+ # } @pr_cust_bill ],
+
+ #};
+}
+
+sub _items_pkg {
+ my $self = shift;
+ my @cust_bill_pkg = grep { $_->pkgnum } $self->cust_bill_pkg;
+ $self->_items_cust_bill_pkg(\@cust_bill_pkg, @_);
+}
+
+sub _items_tax {
+ my $self = shift;
+ my @cust_bill_pkg = grep { ! $_->pkgnum } $self->cust_bill_pkg;
+ $self->_items_cust_bill_pkg(\@cust_bill_pkg, @_);
+}
+
+sub _items_cust_bill_pkg {
+ my $self = shift;
+ my $cust_bill_pkg = shift;
+
+ my @b = ();
+ foreach my $cust_bill_pkg ( @$cust_bill_pkg ) {
+
+ if ( $cust_bill_pkg->pkgnum ) {
+
+ my $cust_pkg = qsearchs('cust_pkg', { pkgnum =>$cust_bill_pkg->pkgnum } );
+ my $part_pkg = qsearchs('part_pkg', { pkgpart=>$cust_pkg->pkgpart } );
+ my $pkg = $part_pkg->pkg;
+
+ my %labels;
+ #tie %labels, 'Tie::IxHash';
+ push @{ $labels{$_->[0]} }, $_->[1] foreach $cust_pkg->labels;
+ my @ext_description;
+ foreach my $label ( keys %labels ) {
+ my @values = @{ $labels{$label} };
+ my $num = scalar(@values);
+ if ( $num > 5 ) {
+ push @ext_description, "$label ($num)";
+ } else {
+ push @ext_description, map { "$label: $_" } @values;
+ }
+ }
+
+ if ( $cust_bill_pkg->setup != 0 ) {
+ my $description = $pkg;
+ $description .= ' Setup' if $cust_bill_pkg->recur != 0;
+ my @d = @ext_description;
+ push @d, $cust_bill_pkg->details if $cust_bill_pkg->recur == 0;
+ push @b, {
+ 'description' => $description,
+ #'pkgpart' => $part_pkg->pkgpart,
+ 'pkgnum' => $cust_pkg->pkgnum,
+ 'amount' => sprintf("%10.2f", $cust_bill_pkg->setup),
+ 'ext_description' => \@d,
+ };
+ }
+
+ if ( $cust_bill_pkg->recur != 0 ) {
+ push @b, {
+ 'description' => "$pkg (" .
+ time2str('%x', $cust_bill_pkg->sdate). ' - '.
+ time2str('%x', $cust_bill_pkg->edate). ')',
+ #'pkgpart' => $part_pkg->pkgpart,
+ 'pkgnum' => $cust_pkg->pkgnum,
+ 'amount' => sprintf("%10.2f", $cust_bill_pkg->recur),
+ 'ext_description' => [ @ext_description,
+ $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' )
+ : 'Tax';
+ if ( $cust_bill_pkg->setup != 0 ) {
+ push @b, {
+ 'description' => $itemdesc,
+ 'amount' => sprintf("%10.2f", $cust_bill_pkg->setup),
+ };
+ }
+ if ( $cust_bill_pkg->recur != 0 ) {
+ push @b, {
+ 'description' => "$itemdesc (".
+ time2str("%x", $cust_bill_pkg->sdate). ' - '.
+ time2str("%x", $cust_bill_pkg->edate). ')',
+ 'amount' => sprintf("%10.2f", $cust_bill_pkg->recur),
+ };
+ }
+
+ }
+
+ }
+
+ @b;
+
+}
+
+sub _items_credits {
+ my $self = shift;
+
+ my @b;
+ #credits
+ foreach ( $self->cust_credited ) {
+
+ #something more elaborate if $_->amount ne $_->cust_credit->credited ?
+
+ my $reason = $_->cust_credit->reason;
+ #my $reason = substr($_->cust_credit->reason,0,32);
+ #$reason .= '...' if length($reason) < length($_->cust_credit->reason);
+ $reason = " ($reason) " if $reason;
+ push @b, {
+ #'description' => 'Credit ref\#'. $_->crednum.
+ # " (". time2str("%x",$_->cust_credit->_date) .")".
+ # $reason,
+ 'description' => 'Credit applied'.
+ time2str("%x",$_->cust_credit->_date). $reason,
+ 'amount' => sprintf("%10.2f",$_->amount),
+ };
+ }
+ #foreach ( @cr_cust_credit ) {
+ # push @buf,[
+ # "Credit #". $_->crednum. " (" . time2str("%x",$_->_date) .")",
+ # $money_char. sprintf("%10.2f",$_->credited)
+ # ];
+ #}
+
+ @b;
+
+}
+
+sub _items_payments {
+ my $self = shift;
+
+ my @b;
+ #get & print payments
+ foreach ( $self->cust_bill_pay ) {
+
+ #something more elaborate if $_->amount ne ->cust_pay->paid ?
+
+ push @b, {
+ 'description' => "Payment received ".
+ time2str("%x",$_->cust_pay->_date ),
+ 'amount' => sprintf("%10.2f", $_->amount )
+ };
+ }
+
+ @b;
+
+}
+
=back
=head1 BUGS
package FS::cust_bill_pay;
use strict;
-use vars qw( @ISA );
+use vars qw( @ISA $conf );
use FS::Record qw( qsearch qsearchs dbh );
use FS::cust_bill;
use FS::cust_pay;
@ISA = qw( FS::Record );
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+} );
+
=head1 NAME
FS::cust_bill_pay - Object methods for cust_bill_pay records
" greater than cust_pay.paid ". $cust_pay->paid;
}
- my $cust_bill = qsearchs('cust_bill', { 'invnum' => $self->invnum } ) or do {
+ my $cust_bill = $self->cust_bill;
+ unless ( $cust_bill ) {
$dbh->rollback if $oldAutoCommit;
return "unknown cust_bill.invnum: ". $self->invnum;
};
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ if ( $conf->exists('invoice_send_receipts') ) {
+ my $send_error = $cust_bill->send;
+ warn "Error sending receipt: $send_error\n" if $send_error;
+ }
+
'';
}
=back
-=head1 VERSION
-
-$Id: cust_bill_pay.pm,v 1.13 2003-08-05 00:20:41 khoff Exp $
-
=head1 BUGS
Delete and replace methods.
use strict;
use vars qw( @ISA $conf $unsuspendauto );
+use Date::Format;
use FS::UID qw( dbh getotaker );
use FS::Record qw( qsearch qsearchs );
+use FS::Misc qw(send_email);
use FS::cust_main;
use FS::cust_refund;
use FS::cust_credit_bill;
sub delete {
my $self = shift;
return "Can't delete closed credit" if $self->closed =~ /^Y/i;
- $self->SUPER::delete(@_);
+
+ 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;
+
+ foreach my $cust_credit_bill ( $self->cust_credit_bill ) {
+ my $error = $cust_credit_bill->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ my $error = $self->SUPER::delete(@_);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ if ( $conf->config('deletecredits') 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('deletecredits'),
+ 'subject' => 'FREESIDE NOTIFICATION: Credit deleted',
+ 'body' => [
+ "This is an automatic message from your Freeside installation\n",
+ "informing you that the following credit has been deleted:\n",
+ "\n",
+ 'crednum: '. $self->crednum. "\n",
+ 'custnum: '. $self->custnum.
+ " (". $cust_main->last. ", ". $cust_main->first. ")\n",
+ 'amount: $'. sprintf("%.2f", $self->amount). "\n",
+ 'date: '. time2str("%a %b %e %T %Y", $self->_date). "\n",
+ 'reason: '. $self->reason. "\n",
+ ],
+ );
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't send credit deletion notification: $error";
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
}
=item replace OLD_RECORD
=cut
sub replace {
- return "Can't modify credit!"
+ #return "Can't modify credit!"
+ my $self = shift;
+ return "Can't modify closed credit" if $self->closed =~ /^Y/i;
+ $self->SUPER::replace(@_);
}
=item check
=back
-=head1 VERSION
-
-$Id: cust_credit.pm,v 1.17 2003-08-05 00:20:41 khoff Exp $
-
=head1 BUGS
-The delete method.
+The delete method. The replace method.
=head1 SEE ALSO
package FS::cust_credit_bill;
use strict;
-use vars qw( @ISA );
+use vars qw( @ISA $conf );
use FS::UID qw( getotaker );
use FS::Record qw( qsearch qsearchs );
use FS::cust_main;
@ISA = qw( FS::Record );
+#ask FS::UID to run this stuff for us later
+FS::UID->install_callback( sub {
+ $conf = new FS::Conf;
+} );
+
=head1 NAME
FS::cust_credit_bill - Object methods for cust_credit_bill records
Adds this cust_credit_bill to the database ("Posts" all or part of a credit).
If there is an error, returns the error, otherwise returns false.
+=cut
+
+sub insert {
+ my $self = shift;
+ my $error = $self->SUPER::insert(@_);
+ return $error if $error;
+
+ if ( $conf->exists('invoice_send_receipts') ) {
+ my $send_error = $self->cust_bill->send;
+ warn "Error sending receipt: $send_error\n" if $send_error;
+ }
+
+ '';
+}
+
=item delete
Currently unimplemented.
=cut
sub delete {
- return "Can't unapply credit!"
+ my $self = shift;
+ return "Can't delete application for closed credit"
+ if $self->cust_credit->closed =~ /^Y/i;
+ $self->SUPER::delete(@_);
}
=item replace OLD_RECORD
qsearchs( 'cust_credit', { 'crednum' => $self->crednum } );
}
-=back
+=item cust_bill
+
+Returns the invoice (see L<FS::cust_bill>)
+
+=cut
-=head1 VERSION
+sub cust_bill {
+ my $self = shift;
+ qsearchs( 'cust_bill', { 'invnum' => $self->invnum } );
+}
-$Id: cust_credit_bill.pm,v 1.8 2003-08-05 00:20:41 khoff Exp $
+=back
=head1 BUGS
use strict;
use vars qw( @ISA $conf $Debug $import );
+use vars qw( $realtime_bop_decline_quiet ); #ugh
use Safe;
use Carp;
BEGIN {
eval "use Time::Local;";
- die "Time::Local version 1.05 required with Perl versions before 5.6"
+ die "Time::Local minimum version 1.05 required with Perl versions before 5.6"
if $] < 5.006 && !defined($Time::Local::VERSION);
eval "use Time::Local qw(timelocal timelocal_nocheck);";
}
use FS::cust_bill_pkg;
use FS::cust_pay;
use FS::cust_credit;
+use FS::cust_refund;
use FS::part_referral;
use FS::cust_main_county;
use FS::agent;
@ISA = qw( FS::Record );
-$Debug = 1;
+$realtime_bop_decline_quiet = 0;
+
+$Debug = 0;
#$Debug = 1;
$import = 0;
=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
sub table { 'cust_main'; }
-=item insert [ CUST_PKG_HASHREF [ , INVOICING_LIST_ARYREF ] ]
+=item insert [ CUST_PKG_HASHREF [ , INVOICING_LIST_ARYREF ] [ , OPTION => VALUE ... ] ]
Adds this customer to the database. If there is an error, returns the error,
otherwise returns false.
$cust_main->insert( {}, [ $email, 'POST' ] );
+Currently available options are: I<noexport>
+
+If I<noexport> is set true, no provisioning jobs (exports) are scheduled.
+(You can schedule them later with the B<reexport> method.)
+
=cut
sub insert {
my $self = shift;
my $cust_pkgs = @_ ? shift : {};
my $invoicing_list = @_ ? shift : '';
+ my %options = @_;
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
}
# packages
- $error = $self->order_pkgs($cust_pkgs, \$seconds);
+ #local $FS::svc_Common::noexport_hack = 1 if $options{'noexport'};
+ $error = $self->order_pkgs($cust_pkgs, \$seconds, %options);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
}
-=item order_pkgs
+=item order_pkgs HASHREF, [ , OPTION => VALUE ... ] ]
+
+Like the insert method on an existing record, this method orders a package
+and included services atomicaly. Pass a Tie::RefHash data structure to this
+method containing FS::cust_pkg and FS::svc_I<tablename> objects. There should
+be a better explanation of this, but until then, here's an example:
+
+ use Tie::RefHash;
+ tie %hash, 'Tie::RefHash'; #this part is important
+ %hash = (
+ $cust_pkg => [ $svc_acct ],
+ ...
+ );
+ $cust_main->order_pkgs( \%hash, 'noexport'=>1 );
+
+Currently available options are: I<noexport>
-document me. like ->insert(%cust_pkg) on an existing record
+If I<noexport> is set true, no provisioning jobs (exports) are scheduled.
+(You can schedule them later with the B<reexport> method for each
+cust_pkg object. Using the B<reexport> method on the cust_main object is not
+recommended, as existing services will also be reexported.)
=cut
my $self = shift;
my $cust_pkgs = shift;
my $seconds = shift;
+ my %options = @_;
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ local $FS::svc_Common::noexport_hack = 1 if $options{'noexport'};
+
foreach my $cust_pkg ( keys %$cust_pkgs ) {
$cust_pkg->custnum( $self->custnum );
my $error = $cust_pkg->insert;
''; #no error
}
+=item reexport
+
+Re-schedules all exports by calling the B<reexport> method of all associated
+packages (see L<FS::cust_pkg>). If there is an error, returns the error;
+otherwise returns false.
+
+=cut
+
+sub reexport {
+ 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;
+
+ foreach my $cust_pkg ( $self->ncancelled_pkgs ) {
+ my $error = $cust_pkg->reexport;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
=item delete NEW_CUSTNUM
This deletes the customer. If there is an error, returns the error, otherwise
This will completely remove all traces of the customer record. This is not
what you want when a customer cancels service; for that, cancel all of the
-customer's packages (see L<FS::cust_pkg/cancel>).
+customer's packages (see L</cancel>).
If the customer has any uncancelled packages, you need to pass a new (valid)
customer number for those packages to be transferred to. Cancelled packages
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- if ( qsearch( 'cust_bill', { 'custnum' => $self->custnum } ) ) {
+ if ( $self->cust_bill ) {
$dbh->rollback if $oldAutoCommit;
return "Can't delete a customer with invoices";
}
- if ( qsearch( 'cust_credit', { 'custnum' => $self->custnum } ) ) {
+ if ( $self->cust_credit ) {
$dbh->rollback if $oldAutoCommit;
return "Can't delete a customer with credits";
}
- if ( qsearch( 'cust_pay', { 'custnum' => $self->custnum } ) ) {
+ if ( $self->cust_pay ) {
$dbh->rollback if $oldAutoCommit;
return "Can't delete a customer with payments";
}
- if ( qsearch( 'cust_refund', { 'custnum' => $self->custnum } ) ) {
+ if ( $self->cust_refund ) {
$dbh->rollback if $oldAutoCommit;
return "Can't delete a customer with refunds";
}
local $SIG{TSTP} = 'IGNORE';
local $SIG{PIPE} = 'IGNORE';
+ if ( $self->payby eq 'COMP' && $self->payby ne $old->payby
+ && $conf->config('users-allow_comp') ) {
+ return "You are not permitted to create complimentary accounts."
+ unless grep { $_ eq getotaker } $conf->config('users-allow_comp');
+ }
+
my $oldAutoCommit = $FS::UID::AutoCommit;
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
|| $self->ut_numbern('referral_custnum')
;
#barf. need message catalogs. i18n. etc.
- $error .= "Please select a advertising source."
+ $error .= "Please select an advertising source."
if $error =~ /^Illegal or empty \(numeric\) refnum: /;
return $error if $error;
or return gettext('invalid_card'); # . ": ". $self->payinfo;
return gettext('unknown_card_type')
if cardtype($self->payinfo) eq "Unknown";
+ if ( defined $self->dbdef_table->column('paycvv') ) {
+ if ( length($self->paycvv) ) {
+ if ( cardtype($self->payinfo) eq 'American Express card' ) {
+ $self->paycvv =~ /^(\d{4})$/
+ or return "CVV2 (CID) for American Express cards is four digits.";
+ $self->paycvv($1);
+ } else {
+ $self->paycvv =~ /^(\d{3})$/
+ or return "CVV2 (CVC2/CID) is three digits.";
+ $self->paycvv($1);
+ }
+ } else {
+ $self->paycvv('');
+ }
+ }
} elsif ( $self->payby eq 'CHEK' || $self->payby eq 'DCHK' ) {
$payinfo =~ /^(\d+)\@(\d{9})$/ or return 'invalid echeck account@aba';
$payinfo = "$1\@$2";
$self->payinfo($payinfo);
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'LECB' ) {
$payinfo =~ /^1?(\d{10})$/ or return 'invalid btn billing telephone number';
$payinfo = $1;
$self->payinfo($payinfo);
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'BILL' ) {
$error = $self->ut_textn('payinfo');
return "Illegal P.O. number: ". $self->payinfo if $error;
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'COMP' ) {
+ if ( !$self->custnum && $conf->config('users-allow_comp') ) {
+ return "You are not permitted to create complimentary accounts."
+ unless grep { $_ eq getotaker } $conf->config('users-allow_comp');
+ }
+
$error = $self->ut_textn('payinfo');
return "Illegal comp account issuer: ". $self->payinfo if $error;
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
} elsif ( $self->payby eq 'PREPAY' ) {
return "Illegal prepayment identifier: ". $self->payinfo if $error;
return "Unknown prepayment identifier"
unless qsearchs('prepay_credit', { 'identifier' => $self->payinfo } );
+ $self->paycvv('') if $self->dbdef_table->column('paycvv');
}
if !$import && ( $y<$nowy || ( $y==$nowy && $1<$nowm ) );
}
- if ( $self->payname eq '' && $self->payby ne 'CHEK' &&
+ if ( $self->payname eq '' && $self->payby !~ /^(CHEK|DCHK)$/ &&
( ! $conf->exists('require_cardname')
|| $self->payby !~ /^(CARD|DCRD)$/ )
) {
$self->tax =~ /^(Y?)$/ or return "Illegal tax: ". $self->tax;
$self->tax($1);
- $self->otaker(getotaker);
+ $self->otaker(getotaker) unless $self->otaker;
#warn "AFTER: \n". $self->_dump;
grep { $_->suspend } $self->unsuspended_pkgs;
}
-=item cancel
+=item cancel [ OPTION => VALUE ... ]
Cancels all uncancelled packages (see L<FS::cust_pkg>) for this customer.
+
+Available options are: I<quiet>
+
+I<quiet> can be set true to supress email cancellation notices.
+
Always returns a list: an empty list on success or a list of errors.
=cut
sub cancel {
my $self = shift;
- grep { $_->cancel } $self->ncancelled_pkgs;
+ grep { $_ } map { $_->cancel(@_) } $self->ncancelled_pkgs;
}
=item agent
Options are passed as name-value pairs.
-The only currently available option is `time', which bills the customer as if
-it were that time. It is specified as a UNIX timestamp; see
-L<perlfunc/"time">). Also see L<Time::Local> and L<Date::Parse> for conversion
-functions. For example:
+Currently available options are:
+
+resetup - if set true, re-charges setup fees.
+
+time - bills the customer as if it were that time. Specified as a UNIX
+timestamp; see L<perlfunc/"time">). Also see L<Time::Local> and
+L<Date::Parse> for conversion functions. For example:
use Date::Parse;
...
$cust_main->bill( 'time' => str2time('April 20th, 2001') );
+
If there is an error, returns the error, otherwise returns false.
=cut
# bill setup
my $setup = 0;
- unless ( $cust_pkg->setup ) {
+ if ( !$cust_pkg->setup || $options{'resetup'} ) {
my $setup_prog = $part_pkg->getfield('setup');
$setup_prog =~ /^(.*)$/ or do {
$dbh->rollback if $oldAutoCommit;
return "Error eval-ing part_pkg->setup pkgpart ". $part_pkg->pkgpart.
"(expression $setup_prog): $@";
}
- $cust_pkg->setfield('setup',$time);
+ $cust_pkg->setfield('setup', $time) unless $cust_pkg->setup;
$cust_pkg_mod_flag=1;
}
#bill recurring fee
my $recur = 0;
my $sdate;
- if ( $part_pkg->getfield('freq') > 0 &&
+ if ( $part_pkg->getfield('freq') ne '0' &&
! $cust_pkg->getfield('susp') &&
( $cust_pkg->getfield('bill') || 0 ) <= $time
) {
$cust_pkg->last_bill($sdate)
if $cust_pkg->dbdef_table->column('last_bill');
- $mon += $part_pkg->freq;
- until ( $mon < 12 ) { $mon -= 12; $year++; }
+ if ( $part_pkg->freq =~ /^\d+$/ ) {
+ $mon += $part_pkg->freq;
+ until ( $mon < 12 ) { $mon -= 12; $year++; }
+ } elsif ( $part_pkg->freq =~ /^(\d+)w$/ ) {
+ my $weeks = $1;
+ $mday += $weeks * 7;
+ } elsif ( $part_pkg->freq =~ /^(\d+)d$/ ) {
+ my $days = $1;
+ $mday += $days;
+ } else {
+ $dbh->rollback if $oldAutoCommit;
+ return "unparsable frequency: ". $part_pkg->freq;
+ }
$cust_pkg->setfield('bill',
timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year));
$cust_pkg_mod_flag = 1;
warn "\$recur is undefined" unless defined($recur);
warn "\$cust_pkg->bill is undefined" unless defined($cust_pkg->bill);
- my $taxable_charged = 0;
if ( $cust_pkg_mod_flag ) {
$error=$cust_pkg->replace($old_cust_pkg);
if ( $error ) { #just in case
}
$setup = sprintf( "%.2f", $setup );
$recur = sprintf( "%.2f", $recur );
- if ( $setup < 0 ) {
+ if ( $setup < 0 && ! $conf->exists('allow_negative_charges') ) {
$dbh->rollback if $oldAutoCommit;
return "negative setup $setup for pkgnum ". $cust_pkg->pkgnum;
}
- if ( $recur < 0 ) {
+ if ( $recur < 0 && ! $conf->exists('allow_negative_charges') ) {
$dbh->rollback if $oldAutoCommit;
return "negative recur $recur for pkgnum ". $cust_pkg->pkgnum;
}
- if ( $setup > 0 || $recur > 0 ) {
+ if ( $setup != 0 || $recur != 0 ) {
my $cust_bill_pkg = new FS::cust_bill_pkg ({
'pkgnum' => $cust_pkg->pkgnum,
'setup' => $setup,
push @cust_bill_pkg, $cust_bill_pkg;
$total_setup += $setup;
$total_recur += $recur;
- $taxable_charged += $setup
- unless $part_pkg->setuptax =~ /^Y$/i;
- $taxable_charged += $recur
- unless $part_pkg->recurtax =~ /^Y$/i;
-
- unless ( $self->tax =~ /Y/i
- || $self->payby eq 'COMP'
- || $taxable_charged == 0 ) {
-
- my $cust_main_county = qsearchs('cust_main_county',{
- 'state' => $self->state,
- 'county' => $self->county,
- 'country' => $self->country,
- 'taxclass' => $part_pkg->taxclass,
- } );
- $cust_main_county ||= qsearchs('cust_main_county',{
- 'state' => $self->state,
- 'county' => $self->county,
- 'country' => $self->country,
- 'taxclass' => '',
- } );
- unless ( $cust_main_county ) {
+
+ unless ( $self->tax =~ /Y/i || $self->payby eq 'COMP' ) {
+
+ my @taxes = qsearch( 'cust_main_county', {
+ 'state' => $self->state,
+ 'county' => $self->county,
+ 'country' => $self->country,
+ 'taxclass' => $part_pkg->taxclass,
+ } );
+ unless ( @taxes ) {
+ @taxes = qsearch( 'cust_main_county', {
+ 'state' => $self->state,
+ 'county' => $self->county,
+ 'country' => $self->country,
+ 'taxclass' => '',
+ } );
+ }
+
+ #one more try at a whole-country tax rate
+ unless ( @taxes ) {
+ @taxes = qsearch( 'cust_main_county', {
+ 'state' => '',
+ 'county' => '',
+ 'country' => $self->country,
+ 'taxclass' => '',
+ } );
+ }
+
+ # maybe eliminate this entirely, along with all the 0% records
+ unless ( @taxes ) {
$dbh->rollback if $oldAutoCommit;
return
"fatal: can't find tax rate for state/county/country/taxclass ".
join('/', ( map $self->$_(), qw(state county country) ),
$part_pkg->taxclass ). "\n";
}
+
+ foreach my $tax ( @taxes ) {
+
+ my $taxable_charged = 0;
+ $taxable_charged += $setup
+ unless $part_pkg->setuptax =~ /^Y$/i
+ || $tax->setuptax =~ /^Y$/i;
+ $taxable_charged += $recur
+ unless $part_pkg->recurtax =~ /^Y$/i
+ || $tax->recurtax =~ /^Y$/i;
+ next unless $taxable_charged;
+
+ if ( $tax->exempt_amount > 0 ) {
+ my ($mon,$year) = (localtime($sdate) )[4,5];
+ $mon++;
+ my $freq = $part_pkg->freq || 1;
+ if ( $freq !~ /(\d+)$/ ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "daily/weekly package definitions not (yet?)".
+ " compatible with monthly tax exemptions";
+ }
+ my $taxable_per_month = sprintf("%.2f", $taxable_charged / $freq );
+ foreach my $which_month ( 1 .. $freq ) {
+ my %hash = (
+ 'custnum' => $self->custnum,
+ 'taxnum' => $tax->taxnum,
+ 'year' => 1900+$year,
+ 'month' => $mon++,
+ );
+ #until ( $mon < 12 ) { $mon -= 12; $year++; }
+ until ( $mon < 13 ) { $mon -= 12; $year++; }
+ my $cust_tax_exempt =
+ qsearchs('cust_tax_exempt', \%hash)
+ || new FS::cust_tax_exempt( { %hash, 'amount' => 0 } );
+ my $remaining_exemption = sprintf("%.2f",
+ $tax->exempt_amount - $cust_tax_exempt->amount );
+ if ( $remaining_exemption > 0 ) {
+ my $addl = $remaining_exemption > $taxable_per_month
+ ? $taxable_per_month
+ : $remaining_exemption;
+ $taxable_charged -= $addl;
+ my $new_cust_tax_exempt = new FS::cust_tax_exempt ( {
+ $cust_tax_exempt->hash,
+ 'amount' =>
+ sprintf("%.2f", $cust_tax_exempt->amount + $addl),
+ } );
+ $error = $new_cust_tax_exempt->exemptnum
+ ? $new_cust_tax_exempt->replace($cust_tax_exempt)
+ : $new_cust_tax_exempt->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "fatal: can't update cust_tax_exempt: $error";
+ }
+
+ } # if $remaining_exemption > 0
+
+ } #foreach $which_month
+
+ } #if $tax->exempt_amount
+
+ $taxable_charged = sprintf( "%.2f", $taxable_charged);
+
+ #$tax += $taxable_charged * $cust_main_county->tax / 100
+ $tax{ $tax->taxname || 'Tax' } +=
+ $taxable_charged * $tax->tax / 100
+
+ } #foreach my $tax ( @taxes )
+
+ } #unless $self->tax =~ /Y/i || $self->payby eq 'COMP'
- if ( $cust_main_county->exempt_amount ) {
- my ($mon,$year) = (localtime($sdate) )[4,5];
- $mon++;
- my $freq = $part_pkg->freq || 1;
- my $taxable_per_month = sprintf("%.2f", $taxable_charged / $freq );
- foreach my $which_month ( 1 .. $freq ) {
- my %hash = (
- 'custnum' => $self->custnum,
- 'taxnum' => $cust_main_county->taxnum,
- 'year' => 1900+$year,
- 'month' => $mon++,
- );
- #until ( $mon < 12 ) { $mon -= 12; $year++; }
- until ( $mon < 13 ) { $mon -= 12; $year++; }
- my $cust_tax_exempt =
- qsearchs('cust_tax_exempt', \%hash)
- || new FS::cust_tax_exempt( { %hash, 'amount' => 0 } );
- my $remaining_exemption = sprintf("%.2f",
- $cust_main_county->exempt_amount - $cust_tax_exempt->amount );
- if ( $remaining_exemption > 0 ) {
- my $addl = $remaining_exemption > $taxable_per_month
- ? $taxable_per_month
- : $remaining_exemption;
- $taxable_charged -= $addl;
- my $new_cust_tax_exempt = new FS::cust_tax_exempt ( {
- $cust_tax_exempt->hash,
- 'amount' => sprintf("%.2f", $cust_tax_exempt->amount + $addl),
- } );
- $error = $new_cust_tax_exempt->exemptnum
- ? $new_cust_tax_exempt->replace($cust_tax_exempt)
- : $new_cust_tax_exempt->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "fatal: can't update cust_tax_exempt: $error";
- }
-
- } # if $remaining_exemption > 0
-
- } #foreach $which_month
-
- } #if $cust_main_county->exempt_amount
-
- $taxable_charged = sprintf( "%.2f", $taxable_charged);
-
- #$tax += $taxable_charged * $cust_main_county->tax / 100
- $tax{ $cust_main_county->taxname || 'Tax' } +=
- $taxable_charged * $cust_main_county->tax / 100
-
- } #unless $self->tax =~ /Y/i
- # || $self->payby eq 'COMP'
- # || $taxable_charged == 0
-
- } #if $setup > 0 || $recur > 0
+ } #if $setup != 0 || $recur != 0
} #if $cust_pkg_mod_flag
# $taxable_charged * ( $cust_main_county->getfield('tax') / 100 )
# );
- foreach my $taxname ( grep { $tax{$_} > 0 } keys %tax ) {
- my $tax = sprintf("%.2f", $tax{$taxname} );
- $charged = sprintf( "%.2f", $charged+$tax );
+ if ( dbdef->table('cust_bill_pkg')->column('itemdesc') ) { #1.5 schema
+
+ foreach my $taxname ( grep { $tax{$_} > 0 } keys %tax ) {
+ my $tax = sprintf("%.2f", $tax{$taxname} );
+ $charged = sprintf( "%.2f", $charged+$tax );
+
+ my $cust_bill_pkg = new FS::cust_bill_pkg ({
+ 'pkgnum' => 0,
+ 'setup' => $tax,
+ 'recur' => 0,
+ 'sdate' => '',
+ 'edate' => '',
+ 'itemdesc' => $taxname,
+ });
+ push @cust_bill_pkg, $cust_bill_pkg;
+ }
+
+ } else { #1.4 schema
+
+ my $tax = 0;
+ foreach ( values %tax ) { $tax += $_ };
+ $tax = sprintf("%.2f", $tax);
+ if ( $tax > 0 ) {
+ $charged = sprintf( "%.2f", $charged+$tax );
+
+ my $cust_bill_pkg = new FS::cust_bill_pkg ({
+ 'pkgnum' => 0,
+ 'setup' => $tax,
+ 'recur' => 0,
+ 'sdate' => '',
+ 'edate' => '',
+ });
+ push @cust_bill_pkg, $cust_bill_pkg;
+ }
- my $cust_bill_pkg = new FS::cust_bill_pkg ({
- 'pkgnum' => 0,
- 'setup' => $tax,
- 'recur' => 0,
- 'sdate' => '',
- 'edate' => '',
- 'itemdesc' => $taxname,
- });
- push @cust_bill_pkg, $cust_bill_pkg;
}
-# }
my $cust_bill = new FS::cust_bill ( {
'custnum' => $self->custnum,
force_print - This option is deprecated; see the invoice events web interface.
+quiet - set true to surpress email card/ACH decline notices.
+
=cut
sub collect {
}
}
- foreach my $cust_bill ( $self->cust_bill ) {
-
- #this has to be before next's
- my $amount = sprintf( "%.2f", $balance < $cust_bill->owed
- ? $balance
- : $cust_bill->owed
- );
- $balance = sprintf( "%.2f", $balance - $amount );
-
- next unless $cust_bill->owed > 0;
+ foreach my $cust_bill ( $self->open_cust_bill ) {
# don't try to charge for the same invoice if it's already in a batch
#next if qsearchs( 'cust_pay_batch', { 'invnum' => $cust_bill->invnum } );
- warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ", amount $amount, balance $balance)" if $Debug;
-
- next unless $amount > 0;
+ last if $self->balance <= 0;
+ warn "invnum ". $cust_bill->invnum. " (owed ". $cust_bill->owed. ")"
+ if $Debug;
foreach my $part_bill_event (
sort { $a->seconds <=> $b->seconds
'disabled' => '', } )
) {
- last unless $cust_bill->owed > 0; #don't run subsequent events if owed=0
+ last if $cust_bill->owed <= 0 # don't run subsequent events if owed<=0
+ || $self->balance <= 0; # or if balance<=0
warn "calling invoice event (". $part_bill_event->eventcode. ")\n"
if $Debug;
my $cust_main = $self; #for callback
- my $error = eval $part_bill_event->eventcode;
+
+ my $error;
+ {
+ local $realtime_bop_decline_quiet = 1 if $options{'quiet'};
+ $error = eval $part_bill_event->eventcode;
+ }
my $status = '';
my $statustext = '';
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} ) =
}
+ #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 $perror = "$processor error: ". $transaction->error_message;
- if ( !$options{'quiet'} && $conf->exists('emaildecline')
+ 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 (
grep { $_->owed > 0 } $self->cust_bill;
}
+=item cust_credit
+
+Returns all the credits (see L<FS::cust_credit>) for this customer.
+
+=cut
+
+sub cust_credit {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_credit', { 'custnum' => $self->custnum } )
+}
+
+=item cust_pay
+
+Returns all the payments (see L<FS::cust_pay>) for this customer.
+
+=cut
+
+sub cust_pay {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_pay', { 'custnum' => $self->custnum } )
+}
+
+=item cust_refund
+
+Returns all the refunds (see L<FS::cust_refund>) for this customer.
+
+=cut
+
+sub cust_refund {
+ my $self = shift;
+ sort { $a->_date <=> $b->_date }
+ qsearch( 'cust_refund', { 'custnum' => $self->custnum } )
+}
+
=back
=head1 SUBROUTINES
=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
+
=back
=head1 METHODS
|| $self->ut_textn('taxclass') # ...
|| $self->ut_money('exempt_amount')
|| $self->ut_textn('taxname')
+ || $self->ut_enum('setuptax', [ '', 'Y' ] )
+ || $self->ut_enum('recurtax', [ '', 'Y' ] )
|| $self->SUPER::check
;
+}
+
+sub taxname {
+ my $self = shift;
+ if ( $self->dbdef_table->column('taxname') ) {
+ return $self->setfield('taxname', $_[0]) if @_;
+ return $self->getfield('taxname');
+ }
+ return '';
+}
+
+sub setuptax {
+ my $self = shift;
+ if ( $self->dbdef_table->column('setuptax') ) {
+ return $self->setfield('setuptax', $_[0]) if @_;
+ return $self->getfield('setuptax');
+ }
+ return '';
+}
+sub recurtax {
+ my $self = shift;
+ if ( $self->dbdef_table->column('recurtax') ) {
+ return $self->setfield('recurtax', $_[0]) if @_;
+ return $self->getfield('recurtax');
+ }
+ return '';
}
=back
sprintf("%.2f", $amount );
}
+=item cust_main
+
+Returns the parent customer object (see L<FS::cust_main>).
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
+
=back
=head1 VERSION
-$Id: cust_pay.pm,v 1.25 2003-08-05 00:20:42 khoff Exp $
+$Id: cust_pay.pm,v 1.26 2003-09-10 10:54:46 ivan Exp $
=head1 BUGS
use strict;
use vars qw( @ISA );
-use FS::Record;
+use FS::Record qw(dbh qsearchs);
use Business::CreditCard;
@ISA = qw( FS::Record );
$self->SUPER::check;
}
+=item cust_main
+
+Returns the customer (see L<FS::cust_main>) for this batched credit card
+payment.
+
+=cut
+
+sub cust_main {
+ my $self = shift;
+ qsearchs( 'cust_main', { 'custnum' => $self->custnum } );
+}
+
=back
-=head1 VERSION
+=head1 SUBROUTINES
+
+=over 4
+
+=item import_results
+
+=cut
+
+sub import_results {
+ use Time::Local;
+ use FS::cust_pay;
+ eval "use Text::CSV_XS;";
+ die $@ if $@;
+#
+ my $param = shift;
+ my $fh = $param->{'filehandle'};
+ my $format = $param->{'format'};
+ my $paybatch = $param->{'paybatch'};
+
+ my @fields;
+ my $end_condition;
+ my $end_hook;
+ my $hook;
+ my $approved_condition;
+ my $declined_condition;
+
+ if ( $format eq 'csv-td_canada_trust-merchant_pc_batch' ) {
+
+ @fields = (
+ 'paybatchnum', # Reference#: Invoice number of the transaction
+ 'paid', # Amount: Amount of the transaction. Dollars and cents
+ # with no decimal entered.
+ '', # Card Type: 0 - MCrd, 1 - Visa, 2 - AMEX, 3 - Discover,
+ # 4 - Insignia, 5 - Diners/EnRoute, 6 - JCB
+ '_date', # Transaction Date: Date the Transaction was processed
+ 'time', # Transaction Time: Time the transaction was processed
+ 'payinfo', # Card Number: Card number for the transaction
+ '', # Expiry Date: Expiry date of the card
+ '', # Auth#: Authorization number entered for force post
+ # transaction
+ 'type', # Transaction Type: 0 - purchase, 40 - refund,
+ # 20 - force post
+ 'result', # Processing Result: 3 - Approval,
+ # 4 - Declined/Amount over limit,
+ # 5 - Invalid/Expired/stolen card,
+ # 6 - Comm Error
+ '', # Terminal ID: Terminal ID used to process the transaction
+ );
+
+ $end_condition = sub {
+ my $hash = shift;
+ $hash->{'type'} eq '0BC';
+ };
+
+ $end_hook = sub {
+ my( $hash, $total) = @_;
+ $total = sprintf("%.2f", $total);
+ my $batch_total = sprintf("%.2f", $hash->{'paybatchnum'} / 100 );
+ return "Our total $total does not match bank total $batch_total!"
+ if $total != $batch_total;
+ '';
+ };
+
+ $hook = sub {
+ my $hash = shift;
+ $hash->{'paid'} = sprintf("%.2f", $hash->{'paid'} / 100 );
+ $hash->{'_date'} = timelocal( substr($hash->{'time'}, 4, 2),
+ substr($hash->{'time'}, 2, 2),
+ substr($hash->{'time'}, 0, 2),
+ substr($hash->{'_date'}, 6, 2),
+ substr($hash->{'_date'}, 4, 2)-1,
+ substr($hash->{'_date'}, 0, 4)-1900, );
+ };
+
+ $approved_condition = sub {
+ my $hash = shift;
+ $hash->{'type'} eq '0' && $hash->{'result'} == 3;
+ };
+
+ $declined_condition = sub {
+ my $hash = shift;
+ $hash->{'type'} eq '0' && ( $hash->{'result'} == 4
+ || $hash->{'result'} == 5 );
+ };
+
+
+ } else {
+ return "Unknown format $format";
+ }
+
+ my $csv = new Text::CSV_XS;
+
+ 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 $total = 0;
+ my $line;
+ while ( defined($line=<$fh>) ) {
+
+ next if $line =~ /^\s*$/; #skip blank lines
-$Id: cust_pay_batch.pm,v 1.7 2003-08-05 00:20:42 khoff Exp $
+ $csv->parse($line) or do {
+ $dbh->rollback if $oldAutoCommit;
+ return "can't parse: ". $csv->error_input();
+ };
+
+ my @values = $csv->fields();
+ my %hash;
+ foreach my $field ( @fields ) {
+ my $value = shift @values;
+ next unless $field;
+ $hash{$field} = $value;
+ }
+
+ if ( &{$end_condition}(\%hash) ) {
+ my $error = &{$end_hook}(\%hash, $total);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ last;
+ }
+
+ my $cust_pay_batch =
+ qsearchs('cust_pay_batch', { 'paybatchnum' => $hash{'paybatchnum'} } );
+ unless ( $cust_pay_batch ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "unknown paybatchnum $hash{'paybatchnum'}\n";
+ }
+ my $custnum = $cust_pay_batch->custnum,
+
+ my $error = $cust_pay_batch->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error removing paybatchnum $hash{'paybatchnum'}: $error\n";
+ }
+
+ &{$hook}(\%hash);
+
+ if ( &{$approved_condition}(\%hash) ) {
+
+ my $cust_pay = new FS::cust_pay ( {
+ 'custnum' => $custnum,
+ 'payby' => 'CARD',
+ 'paybatch' => $paybatch,
+ map { $_ => $hash{$_} } (qw( paid _date payinfo )),
+ } );
+ $error = $cust_pay->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "error adding payment paybatchnum $hash{'paybatchnum'}: $error\n";
+ }
+ $total += $hash{'paid'};
+
+ $cust_pay->cust_main->apply_payments;
+
+ } elsif ( &{$declined_condition}(\%hash) ) {
+
+ #this should be configurable... if anybody else ever uses batches
+ $cust_pay_batch->cust_main->suspend;
+
+ }
+
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
+=back
=head1 BUGS
package FS::cust_pkg;
use strict;
-use vars qw(@ISA $disable_agentcheck);
-use vars qw( $quiet );
+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 );
@ISA = qw( FS::Record );
+$DEBUG = 0;
+
$disable_agentcheck = 0;
sub _cache {
return $error if $error;
my $cust_main = $self->cust_main;
- return "Unknown customer ". $self->custnum unless $cust_main;
+ return "Unknown custnum: ". $self->custnum unless $cust_main;
unless ( $disable_agentcheck ) {
my $agent = qsearchs( 'agent', { 'agentnum' => $cust_main->agentnum } );
$self->otaker($1);
if ( $self->dbdef_table->column('manual_flag') ) {
- $self->manual_flag =~ /^([01]?)$/ or return "Illegal manual_flag";
+ $self->manual_flag('') if $self->manual_flag eq ' ';
+ $self->manual_flag =~ /^([01]?)$/
+ or return "Illegal manual_flag ". $self->manual_flag;
$self->manual_flag($1);
}
$self->SUPER::check;
}
-=item cancel
+=item cancel [ OPTION => VALUE ... ]
Cancels and removes all services (see L<FS::cust_svc> and L<FS::part_svc>)
in this package, then cancels the package itself (sets the cancel field to
now).
+Available options are: I<quiet>
+
+I<quiet> can be set true to supress email cancellation notices.
+
If there is an error, returns the error, otherwise returns false.
=cut
sub cancel {
- my $self = shift;
+ my( $self, %options ) = @_;
my $error;
local $SIG{HUP} = 'IGNORE';
my $conf = new FS::Conf;
my @invoicing_list = grep { $_ ne 'POST' } $self->cust_main->invoicing_list;
- if ( !$quiet && $conf->exists('emailcancel') && @invoicing_list ) {
+ if ( !$options{'quiet'} && $conf->exists('emailcancel') && @invoicing_list ) {
my $conf = new FS::Conf;
my $error = send_email(
'from' => $conf->config('invoice_from'),
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).
+the destination allows services with the correct I<svcpart> (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
return $remaining;
}
+=item reexport
+
+=cut
+
+sub reexport {
+ 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;
+
+ foreach my $cust_svc ( $self->cust_svc ) {
+ #false laziness w/svc_Common::insert
+ my $svc_x = $cust_svc->svc_x;
+ foreach my $part_export ( $cust_svc->part_svc->part_export ) {
+ my $error = $part_export->export_insert($svc_x);
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ '';
+
+}
+
=back
=head1 SUBROUTINES
=cut
sub order {
-
- # 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
+ 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;
});
return "Already ". scalar(@cust_svc). " ". $part_svc->svc.
" services for pkgnum ". $self->pkgnum
- if scalar(@cust_svc) >= $quantity && (!$ignore_quantity || !$quantity);
+ if scalar(@cust_svc) >= $quantity && !$ignore_quantity;
}
$self->SUPER::check;
if ( $svcdb eq 'svc_acct' ) {
$tag = $svc_x->email;
} elsif ( $svcdb eq 'svc_forward' ) {
- my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->srcsvc } );
- $tag = $svc_acct->email. '->';
+ if ( $svc_x->srcsvc ) {
+ my $svc_acct = $svc_x->srcsvc_acct;
+ $tag = $svc_acct->email;
+ } else {
+ $tag = $svc_x->src;
+ }
+ $tag .= '->';
if ( $svc_x->dstsvc ) {
- $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->dstsvc } );
+ my $svc_acct = $svc_x->dstsvc_acct;
$tag .= $svc_acct->email;
} else {
$tag .= $svc_x->dst;
$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');
sub seconds_since_sqlradacct {
my($self, $start, $end) = @_;
- my $username = $self->svc_x->username;
+ my $svc_x = $self->svc_x;
- my @part_export = $self->part_svc->part_export('sqlradius')
- or die "no sqlradius export configured for this service type";
+ my @part_export = $self->part_svc->part_export('sqlradius');
+ push @part_export, $self->part_svc->part_export('sqlradius_withdomain');
+ die "no sqlradius or sqlradius_withdomain export configured for this".
+ "service type"
+ unless @part_export;
#or return undef;
my $seconds = 0;
foreach my $part_export ( @part_export ) {
+ next if $part_export->option('ignore_accounting');
+
my $dbh = DBI->connect( map { $part_export->option($_) }
qw(datasrc username password) )
or die "can't connect to sqlradius database: ". $DBI::errstr;
$str2time = 'extract(epoch from ';
}
+ my $username;
+ if ( $part_export->exporttype eq 'sqlradius' ) {
+ $username = $svc_x->username;
+ } elsif ( $part_export->exporttype eq 'sqlradius_withdomain' ) {
+ $username = $svc_x->email;
+ } else {
+ die 'unknown exporttype '. $part_export->exporttype;
+ }
+
my $query;
#find closed sessions completely within the given range
sub attribute_since_sqlradacct {
my($self, $start, $end, $attrib) = @_;
- my $username = $self->svc_x->username;
+ my $svc_x = $self->svc_x;
- my @part_export = $self->part_svc->part_export('sqlradius')
- or die "no sqlradius export configured for this service type";
+ my @part_export = $self->part_svc->part_export('sqlradius');
+ push @part_export, $self->part_svc->part_export('sqlradius_withdomain');
+ die "no sqlradius or sqlradius_withdomain export configured for this".
+ "service type"
+ unless @part_export;
#or return undef;
my $sum = 0;
foreach my $part_export ( @part_export ) {
+ next if $part_export->option('ignore_accounting');
+
my $dbh = DBI->connect( map { $part_export->option($_) }
qw(datasrc username password) )
or die "can't connect to sqlradius database: ". $DBI::errstr;
$str2time = 'extract(epoch from ';
}
+ my $username;
+ if ( $part_export->exporttype eq 'sqlradius' ) {
+ $username = $svc_x->username;
+ } elsif ( $part_export->exporttype eq 'sqlradius_withdomain' ) {
+ $username = $svc_x->email;
+ } else {
+ die 'unknown exporttype '. $part_export->exporttype;
+ }
+
my $sth = $dbh->prepare("SELECT SUM($attrib)
FROM radacct
WHERE UserName = ?
}
+=item get_session_history_sqlradacct TIMESTAMP_START TIMESTAMP_END
+
+See L<FS::svc_acct/get_session_history_sqlradacct>. Equivalent to
+$cust_svc->svc_x->get_session_history_sqlradacct, but more efficient.
+Meaningless for records where B<svcdb> is not "svc_acct".
+
+=cut
+
+sub get_session_history {
+ my($self, $start, $end, $attrib) = @_;
+
+ my $username = $self->svc_x->username;
+
+ my @part_export = $self->part_svc->part_export('sqlradius')
+ or die "no sqlradius export configured for this service type";
+ #or return undef;
+
+ my @sessions = ();
+
+ foreach my $part_export ( @part_export ) {
+
+ my $dbh = DBI->connect( map { $part_export->option($_) }
+ qw(datasrc username password) )
+ or die "can't connect to sqlradius database: ". $DBI::errstr;
+
+ #select a unix time conversion function based on database type
+ my $str2time;
+ if ( $dbh->{Driver}->{Name} eq 'mysql' ) {
+ $str2time = 'UNIX_TIMESTAMP(';
+ } elsif ( $dbh->{Driver}->{Name} eq 'Pg' ) {
+ $str2time = 'EXTRACT( EPOCH FROM ';
+ } else {
+ warn "warning: unknown database type ". $dbh->{Driver}->{Name}.
+ "; guessing how to convert to UNIX timestamps";
+ $str2time = 'extract(epoch from ';
+ }
+
+ my @fields = qw( acctstarttime acctstoptime acctsessiontime
+ acctinputoctets acctoutputoctets framedipaddress );
+
+ my $sth = $dbh->prepare('SELECT '. join(', ', @fields).
+ " FROM radacct
+ WHERE UserName = ?
+ AND $str2time AcctStopTime ) >= ?
+ AND $str2time AcctStopTime ) <= ?
+ ORDER BY AcctStartTime DESC
+ ") or die $dbh->errstr;
+ $sth->execute($username, $start, $end) or die $sth->errstr;
+
+ push @sessions, map { { %$_ } } @{ $sth->fetchall_arrayref({}) };
+
+ }
+ \@sessions
+
+}
+
=back
=head1 BUGS
join("\n", $conf->config('invoice_template') )
);
}
+ unless ( $conf->exists("invoice_latex_$name") ) {
+ $conf->set(
+ "invoice_latex_$name" =>
+ join("\n", $conf->config('invoice_latex') )
+ );
+ }
}
$self->SUPER::check;
;
return $error if $error;
- $self->machine =~ /^([\w\-\.]*)$/
- or return "Illegal machine: ". $self->machine;
- $self->machine($1);
-
$self->nodomain =~ /^(Y?)$/ or return "Illegal nodomain: ". $self->nodomain;
$self->nodomain($1);
return "_export_delete: unknown export type ". $self->exporttype;
}
-#fallbacks providing null operations
+#call svcdb-specific fallbacks
sub _export_suspend {
my $self = shift;
#warn "warning: _export_suspened unimplemented for". ref($self);
- '';
+ my $svc_x = shift;
+ my $new = $svc_x->clone_suspended;
+ $self->_export_replace( $new, $svc_x );
}
sub _export_unsuspend {
my $self = shift;
#warn "warning: _export_unsuspend unimplemented for ". ref($self);
- '';
+ my $svc_x = shift;
+ my $old = $svc_x->clone_kludge_unsuspend;
+ $self->_export_replace( $svc_x, $old );
}
=back
type =>'checkbox',
},
'suspend' => { label=>'Suspension command',
- default=>'',
+ default=>'usermod -L $username',
},
'suspend_stdin' => { label=>'Suspension command STDIN',
default=>'',
},
'unsuspend' => { label=>'Unsuspension command',
- default=>'',
+ default=>'usermod -U $username',
},
'unsuspend_stdin' => { label=>'Unsuspension command STDIN',
default=>'',
;
tie my %domain_shellcommands_options, 'Tie::IxHash',
- 'user' => { lable=>'Remote username', default=>'root' },
+ 'user' => { label=>'Remote username', default=>'root' },
'useradd' => { label=>'Insert command',
default=>'',
},
'datasrc' => { label=>'DBI data source ' },
'username' => { label=>'Database username' },
'password' => { label=>'Database password' },
+ 'ignore_accounting' => {
+ type => 'checkbox',
+ label=>'Ignore accounting records from this database'
+ },
;
tie my %sqlradius_withdomain_options, 'Tie::IxHash',
'datasrc' => { label=>'DBI data source ' },
'username' => { label=>'Database username' },
'password' => { label=>'Database password' },
+ 'ignore_accounting' => {
+ type => 'checkbox',
+ label=>'Ignore accounting records from this database'
+ },
;
tie my %cyrus_options, 'Tie::IxHash',
},
;
+tie my %communigate_pro_options, 'Tie::IxHash',
+ 'port' => { label=>'Port number', default=>'106', },
+ 'login' => { label=>'The administrator account name. The name can contain a domain part.', },
+ 'password' => { label=>'The administrator account password.', },
+ 'accountType' => { label=>'Type for newly-created accounts',
+ type=>'select',
+ options=>[qw( MultiMailbox TextMailbox MailDirMailbox )],
+ default=>'MultiMailbox',
+ },
+ 'externalFlag' => { label=> 'Create accounts with an external (visible for legacy mailers) INBOX.',
+ type=>'checkbox',
+ },
+ 'AccessModes' => { label=>'Access modes',
+ default=>'Mail POP IMAP PWD WebMail WebSite',
+ },
+;
+
+tie my %communigate_pro_singledomain_options, 'Tie::IxHash',
+ 'port' => { label=>'Port number', default=>'106', },
+ 'login' => { label=>'The administrator account name. The name can contain a domain part.', },
+ 'password' => { label=>'The administrator account password.', },
+ 'domain' => { label=>'Domain', },
+ 'accountType' => { label=>'Type for newly-created accounts',
+ type=>'select',
+ options=>[qw( MultiMailbox TextMailbox MailDirMailbox )],
+ default=>'MultiMailbox',
+ },
+ 'externalFlag' => { label=> 'Create accounts with an external (visible for legacy mailers) INBOX.',
+ type=>'checkbox',
+ },
+ 'AccessModes' => { label=>'Access modes',
+ default=>'Mail POP IMAP PWD WebMail WebSite',
+ },
+;
+
tie my %bind_options, 'Tie::IxHash',
#'machine' => { label=>'named machine' },
'named_conf' => { label => 'named.conf location',
;
tie my %forward_shellcommands_options, 'Tie::IxHash',
- 'user' => { lable=>'Remote username', default=>'root' },
+ 'user' => { label=>'Remote username', default=>'root' },
'useradd' => { label=>'Insert command',
default=>'',
},
},
;
+tie my %postfix_options, 'Tie::IxHash',
+ 'user' => { label=>'Remote username', default=>'root' },
+ 'aliases' => { label=>'aliases file location', default=>'/etc/aliases' },
+ 'virtual' => { label=>'virtual file location', default=>'/etc/postfix/virtual' },
+ 'mydomain' => { label=>'local domain', default=>'' },
+;
+
#export names cannot have dashes...
%exports = (
'svc_acct' => {
'desc' => 'Real-time export via remote SSH (i.e. useradd, userdel, etc.)',
'options' => \%shellcommands_options,
'nodomain' => 'Y',
- 'notes' => 'Run remote commands via SSH. Usernames are considered unique (also see shellcommands_withdomain). You probably want this if the commands you are running will not accept a domain as a parameter. You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="Linux/NetBSD" onClick=\'this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "userdel -r $username"; this.form.userdel_stdin.value=""; this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username"; this.form.usermod_stdin.value = "";\'><LI><INPUT TYPE="button" VALUE="FreeBSD" onClick=\'this.form.useradd.value = "pw useradd $username -d $dir -m -s $shell -u $uid -g $gid -c $finger -h 0"; this.form.useradd_stdin.value = "$_password\n"; this.form.userdel.value = "pw userdel $username -r"; this.form.userdel_stdin.value=""; this.form.usermod.value = "pw usermod $old_username -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -c $new_finger -h 0"; this.form.usermod_stdin.value = "$new__password\n";\'><LI><INPUT TYPE="button" VALUE="Just maintain directories (use with sysvshell or bsdshell)" onClick=\'this.form.useradd.value = "cp -pr /etc/skel $dir; chown -R $uid.$gid $dir"; this.form.useradd_stdin.value = ""; this.form.usermod.value = "[ -d $old_dir ] && mv $old_dir $new_dir || ( chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; find . -depth -print | cpio -pdm $new_dir; chmod u-t $new_dir; chown -R $new_uid.$new_gid $new_dir; rm -rf $old_dir )"; this.form.usermod_stdin.value = ""; this.form.userdel.value = "rm -rf $dir"; this.form.userdel_stdin.value="";\'></UL>The following variables are available for interpolation (prefixed with new_ or old_ for replace operations): <UL><LI><code>$username</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
+ 'notes' => 'Run remote commands via SSH. Usernames are considered unique (also see shellcommands_withdomain). You probably want this if the commands you are running will not accept a domain as a parameter. You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="Linux" onClick=\'this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "userdel -r $username"; this.form.userdel_stdin.value=""; this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username"; this.form.usermod_stdin.value = ""; this.form.suspend.value = "usermod -L $username"; this.form.suspend_stdin.value=""; this.form.unsuspend.value = "usermod -U $username"; this.form.unsuspend_stdin.value="";\'><LI><INPUT TYPE="button" VALUE="FreeBSD" onClick=\'this.form.useradd.value = "lockf /etc/passwd.lock pw useradd $username -d $dir -m -s $shell -u $uid -g $gid -c $finger -h 0"; this.form.useradd_stdin.value = "$_password\n"; this.form.userdel.value = "lockf /etc/passwd.lock pw userdel $username -r"; this.form.userdel_stdin.value=""; this.form.usermod.value = "lockf /etc/passwd.lock pw usermod $old_username -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -c $new_finger -h 0"; this.form.usermod_stdin.value = "$new__password\n"; this.form.suspend.value = "lockf /etc/passwd.lock pw lock $username"; this.form.suspend_stdin.value=""; this.form.unsuspend.value = "lockf /etc/passwd.lock pw unlock $username"; this.form.unsuspend_stdin.value="";\'> Note: On FreeBSD, due to deficient locking in pw(1), you must disable the chpass(1), chsh(1), chfn(1), passwd(1), and vipw(1) commands, or replace them with wrappers that prepend "lockf /etc/passwd.lock". Alternatively, apply the patch in <A HREF="http://www.freebsd.org/cgi/query-pr.cgi?pr=23501">FreeBSD PR#23501</A> and remove the "lockf /etc/passwd.lock" from these default commands.<LI><INPUT TYPE="button" VALUE="NetBSD/OpenBSD" onClick=\'this.form.useradd.value = "useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username"; this.form.useradd_stdin.value = ""; this.form.userdel.value = "userdel -r $username"; this.form.userdel_stdin.value=""; this.form.usermod.value = "usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username"; this.form.usermod_stdin.value = ""; this.form.suspend.value = ""; this.form.suspend_stdin.value=""; this.form.unsuspend.value = ""; this.form.unsuspend_stdin.value="";\'><LI><INPUT TYPE="button" VALUE="Just maintain directories (use with sysvshell or bsdshell)" onClick=\'this.form.useradd.value = "cp -pr /etc/skel $dir; chown -R $uid.$gid $dir"; this.form.useradd_stdin.value = ""; this.form.usermod.value = "[ -d $old_dir ] && mv $old_dir $new_dir || ( chmod u+t $old_dir; mkdir $new_dir; cd $old_dir; find . -depth -print | cpio -pdm $new_dir; chmod u-t $new_dir; chown -R $new_uid.$new_gid $new_dir; rm -rf $old_dir )"; this.form.usermod_stdin.value = ""; this.form.userdel.value = "rm -rf $dir"; this.form.userdel_stdin.value=""; this.form.suspend.value = ""; this.form.suspend_stdin.value=""; this.form.unsuspend.value = ""; this.form.unsuspend_stdin.value="";\'></UL>The following variables are available for interpolation (prefixed with new_ or old_ for replace operations): <UL><LI><code>$username</code><LI><code>$_password</code><LI><code>$quoted_password</code> - unencrypted password quoted for the shell<LI><code>$crypt_password</code> - encrypted password<LI><code>$uid</code><LI><code>$gid</code><LI><code>$finger</code> - GECOS, already quoted for the shell (do not add additional quotes)<LI><code>$dir</code> - home directory<LI><code>$shell</code><LI><code>$quota</code><LI>All other fields in <a href="../docs/schema.html#svc_acct">svc_acct</a> are also available.</UL>',
},
'shellcommands_withdomain' => {
},
'sqlradius' => {
- 'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS)',
+ 'desc' => 'Real-time export to SQL-backed RADIUS (FreeRADIUS, ICRADIUS, Radiator)',
'options' => \%sqlradius_options,
'nodomain' => 'Y',
- 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>. This export does not export RADIUS realms (see also sqlradius_withdomain). An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.',
+ 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a>, <a href="http://radius.innercite.com/">ICRADIUS</a> or <a href="http://www.open.com.au/radiator/">Radiator</a>. This export does not export RADIUS realms (see also sqlradius_withdomain). An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm#connect">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.<ul><li>Using FreeRADIUS 0.9.0 with the PostgreSQL backend, the db_postgresql.sql schema and postgresql.conf queries contain incompatible changes. This is fixed in 0.9.1. Only new installs with 0.9.0 and PostgreSQL are affected - upgrades and other database backends and versions are unaffected.<li>Using ICRADIUS, add a dummy "op" column to your database: <blockquote><code>ALTER TABLE radcheck ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'<br>ALTER TABLE radreply ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'<br>ALTER TABLE radgroupcheck ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'<br>ALTER TABLE radgroupreply ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'</code></blockquote><li>Using Radiator, see the <a href="http://www.open.com.au/radiator/faq.html#38">Radiator FAQ</a> for configuration information.</ul>',
},
'sqlradius_withdomain' => {
- 'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS) with realms',
+ 'desc' => 'Real-time export to SQL-backed RADIUS (FreeRADIUS, ICRADIUS, Radiator) with realms',
'options' => \%sqlradius_withdomain_options,
'nodomain' => '',
- 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>. This export exports domains to RADIUS realms (see also sqlradius). An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.',
+ 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a>, <a href="http://radius.innercite.com/">ICRADIUS</a> or <a href="http://www.open.com.au/radiator/">Radiator</a>. This export exports domains to RADIUS realms (see also sqlradius). An existing RADIUS database will be updated in realtime, but you can use <a href="../docs/man/bin/freeside-sqlradius-reset">freeside-sqlradius-reset</a> to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the <a href="http://search.cpan.org/doc/TIMB/DBI/DBI.pm#connect">DBI documentation</a> and the <a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> for the exact syntax of a DBI data source.<ul><li>Using FreeRADIUS 0.9.0 with the PostgreSQL backend, the db_postgresql.sql schema and postgresql.conf queries contain incompatible changes. This is fixed in 0.9.1. Only new installs with 0.9.0 and PostgreSQL are affected - upgrades and other database backends and versions are unaffected.<li>Using ICRADIUS, add a dummy "op" column to your database: <blockquote><code>ALTER TABLE radcheck ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'<br>ALTER TABLE radreply ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'<br>ALTER TABLE radgroupcheck ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'<br>ALTER TABLE radgroupreply ADD COLUMN op VARCHAR(2) NOT NULL DEFAULT \'==\'</code></blockquote><li>Using Radiator, see the <a href="http://www.open.com.au/radiator/faq.html#38">Radiator FAQ</a> for configuration information.</ul>',
},
'sqlmail' => {
'notes' => 'Real time export to <a href="http://inter7.com/vpopmail/">vpopmail</a> text files. <a href="http://search.cpan.org/search?dist=File-Rsync">File::Rsync</a> must be installed, and you will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a> to <b>vpopmail</b>@<i>export.host</i>.',
},
+ 'communigate_pro' => {
+ 'desc' => 'Real-time export to a CommuniGate Pro mail server',
+ 'options' => \%communigate_pro_options,
+ 'notes' => 'Real time export to a <a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a> mail server. The <a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a> must be installed as CGP::CLI.',
+ },
+
+ 'communigate_pro_singledomain' => {
+ 'desc' => 'Real-time export to a CommuniGate Pro mail server, one domain only',
+ 'options' => \%communigate_pro_singledomain_options,
+ 'nodomain' => 'Y',
+ 'notes' => 'Real time export to a <a href="http://www.stalker.com/CommuniGatePro/">CommuniGate Pro</a> mail server. This is an unusual export to CommuniGate Pro that forces all accounts into a single domain. As CommuniGate Pro supports multiple domains, unless you have a specific reason for using this export, you probably want to use the communigate_pro export instead. The <a href="http://www.stalker.com/CGPerl/">CommuniGate Pro Perl Interface</a> must be installed as CGP::CLI.',
+ },
+
},
'svc_domain' => {
'desc' => 'Real-time export to SQL-backed mail server',
'options' => \%sqlmail_options,
#'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?...)',
+ 'notes' => 'Database schema can be made to work with Courier IMAP and Exim. Others could work but are untested. (...extended description from fire2wire?...)',
},
'forward_shellcommands' => {
'options' => \%forward_shellcommands_options,
'notes' => 'Run remote commands via SSH, for forwards. You will need to <a href="../docs/ssh.html">setup SSH for unattended operation</a>.<BR><BR>Use these buttons for some useful presets:<UL><LI><INPUT TYPE="button" VALUE="text vpopmail maintenance" onClick=\'this.form.useradd.value = "[ -d /home/vpopmail/domains/$domain/$username ] && { echo \"$destination\" > /home/vpopmail/domains/$domain/$username/.qmail; chown vpopmail:vchkpw /home/vpopmail/domains/$domain/$username/.qmail; }"; this.form.userdel.value = "rm /home/vpopmail/domains/$domain/$username/.qmail"; this.form.usermod.value = "mv /home/vpopmail/domains/$old_domain/$old_username/.qmail /home/vpopmail/domains/$new_domain/$new_username; [ \"$old_destination\" != \"$new_destination\" ] && { echo \"$new_destination\" > /home/vpopmail/domains/$new_domain/$new_username/.qmail; chown vpopmail:vchkpw /home/vpopmail/domains/$new_domain/$new_username/.qmail; }";\'></UL>The following variables are available for interpolation (prefixed with <code>new_</code> or <code>old_</code> for replace operations): <UL><LI><code>$username</code><LI><code>$domain</code><LI><code>$destination</code> - forward destination<LI>All other fields in <a href="../docs/schema.html#svc_forward">svc_forward</a> are also available.</UL>',
},
+
+ 'postfix' => {
+ 'desc' => 'Real-time export to Postfix text files',
+ 'options' => \%postfix_options,
+ #'nodomain' => 'Y',
+ 'notes' => 'Batch export of Postfix aliases and virtual files. <a href="http://search.cpan.org/search?dist=File-Rsync">File::Rsync</a> must be installed. Run bin/postfix.export to export the files.',
+ },
+
},
'svc_www' => {
'notes' => '',
},
},
+
+ 'svc_external' => {
+ },
+
);
=back
--- /dev/null
+package FS::part_export::communigate_pro;
+
+use vars qw(@ISA);
+use FS::part_export;
+use FS::queue;
+
+@ISA = qw(FS::part_export);
+
+sub rebless { shift; }
+
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ $svc_acct->email;
+}
+
+sub _export_insert {
+ my( $self, $svc_acct ) = (shift, shift);
+ my @options = ( $svc_acct->svcnum, 'CreateAccount',
+ 'accountName' => $self->export_username($svc_acct),
+ 'accountType' => $self->option('accountType'),
+ 'AccessModes' => $self->option('AccessModes'),
+ 'RealName' => $svc_acct->finger,
+ 'Password' => $svc_acct->_password,
+ );
+ push @options, 'MaxAccountSize' => $svc_acct->quota if $svc_acct->quota;
+ push @options, 'externalFlag' => $self->option('externalFlag')
+ if $self->option('externalFlag');
+
+ $self->communigate_pro_queue( @options );
+}
+
+sub _export_replace {
+ my( $self, $new, $old ) = (shift, shift, shift);
+ return "can't (yet) change username with CommuniGate Pro"
+ if $old->username ne $new->username;
+ return "can't (yet) change domain with CommuniGate Pro"
+ if $self->export_username($old) ne $self->export_username($new);
+ return "can't (yet) change GECOS with CommuniGate Pro"
+ if $old->finger ne $new->finger;
+ return "can't (yet) change quota with CommuniGate Pro"
+ if $old->quota ne $new->quota;
+ return '' unless $old->username ne $new->username
+ || $old->_password ne $new->_password
+ || $old->finger ne $new->finger
+ || $old->quota ne $new->quota;
+
+ return '' if '*SUSPENDED* '. $old->_password eq $new->_password;
+
+ #my $err_or_queue = $self->communigate_pro_queue( $new->svcnum,'RenameAccount',
+ # $old->email, $new->email );
+ #return $err_or_queue unless ref($err_or_queue);
+ #my $jobnum = $err_or_queue->jobnum;
+
+ $self->communigate_pro_queue( $new->svcnum, 'SetAccountPassword',
+ $self->export_username($new), $new->_password )
+ if $new->_password ne $old->_password;
+
+}
+
+sub _export_delete {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->communigate_pro_queue( $svc_acct->svcnum, 'DeleteAccount',
+ $self->export_username($svc_acct),
+ );
+}
+
+sub _export_suspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->communigate_pro_queue( $svc_acct->svcnum, 'UpdateAccountSettings',
+ 'accountName' => $self->export_username($svc_acct),
+ 'AccessModes' => 'Mail',
+ );
+}
+
+sub _export_unsuspend {
+ my( $self, $svc_acct ) = (shift, shift);
+ $self->communigate_pro_queue( $svc_acct->svcnum, 'UpdateAccountSettings',
+ 'accountName' => $self->export_username($svc_acct),
+ 'AccessModes' => $self->option('AccessModes'),
+ );
+}
+
+sub communigate_pro_queue {
+ my( $self, $svcnum, $method ) = (shift, shift, shift);
+ my @kludge_methods = qw(CreateAccount UpdateAccountSettings);
+ my $sub = 'communigate_pro_command';
+ $sub = $method if grep { $method eq $_ } @kludge_methods;
+ my $queue = new FS::queue {
+ 'svcnum' => $svcnum,
+ 'job' => "FS::part_export::communigate_pro::$sub",
+ };
+ $queue->insert(
+ $self->machine,
+ $self->option('port'),
+ $self->option('login'),
+ $self->option('password'),
+ $method,
+ @_,
+ );
+
+}
+
+sub CreateAccount {
+ my( $machine, $port, $login, $password, $method, %args ) = @_;
+ my $accountName = delete $args{'accountName'};
+ my $accountType = delete $args{'accountType'};
+ my $externalFlag = delete $args{'externalFlag'};
+ $args{'AccessModes'} = [ split(' ', $args{'AccessModes'}) ];
+ my @args = ( accountName => $accountName,
+ accountType => $accountType,
+ settings => \%args,
+ );
+ #externalFlag => $externalFlag,
+ push @args, externalFlag => $externalFlag if $externalFlag;
+
+ communigate_pro_command( $machine, $port, $login, $password, $method, @args );
+
+}
+
+sub UpdateAccountSettings {
+ my( $machine, $port, $login, $password, $method, %args ) = @_;
+ my $accountName = delete $args{'accountName'};
+ $args{'AccessModes'} = [ split(' ', $args{'AccessModes'}) ];
+ @args = ( $accountName, \%args );
+ communigate_pro_command( $machine, $port, $login, $password, $method, @args );
+}
+
+sub communigate_pro_command { #subroutine, not method
+ my( $machine, $port, $login, $password, $method, @args ) = @_;
+
+ eval "use CGP::CLI";
+
+ my $cli = new CGP::CLI( {
+ 'PeerAddr' => $machine,
+ 'PeerPort' => $port,
+ 'login' => $login,
+ 'password' => $password,
+ } ) or die "Can't login to CGPro: $CGP::ERR_STRING\n";
+
+ $cli->$method(@args) or die "CGPro error: ". $cli->getErrMessage;
+
+ $cli->Logout or die "Can't logout of CGPro: $CGP::ERR_STRING\n";
+
+}
--- /dev/null
+package FS::part_export::communigate_pro_singledomain;
+
+use vars qw(@ISA);
+use FS::part_export::communigate_pro;
+
+@ISA = qw(FS::part_export::communigate_pro);
+
+sub export_username {
+ my($self, $svc_acct) = (shift, shift);
+ $svc_acct->username. '@'. $self->option('domain');
+}
}
sub ssh_cmd { #subroutine, not method
- use Net::SSH '0.07';
+ use Net::SSH '0.08';
&Net::SSH::ssh_cmd( { @_ } );
}
}
sub ssh_cmd { #subroutine, not method
- use Net::SSH '0.07';
+ use Net::SSH '0.08';
&Net::SSH::ssh_cmd( { @_ } );
}
--- /dev/null
+package FS::part_export::postfix;
+
+use vars qw(@ISA);
+use FS::part_export::null;
+
+@ISA = qw(FS::part_export::null);
+
--- /dev/null
+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.08';
+ &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
+#}
+
{
no strict 'refs';
${$_} = $svc_acct->getfield($_) foreach $svc_acct->fields;
+
+ my $count = 1;
+ foreach my $acct_snarf ( $svc_acct->acct_snarf ) {
+ ${"snarf_$_$count"} = shell_quote( $acct_snarf->get($_) )
+ foreach qw( machine username _password );
+ $count++;
+ }
}
my $cust_pkg = $svc_acct->cust_svc->cust_pkg;
if ( $old_domain ne $new_domain ) {
$error ||= "can't change domain";
}
+ if ( $old_uid != $new_uid ) {
+ $error ||= "can't change uid";
+ }
+ if ( $old_dir ne $new_dir ) {
+ $error ||= "can't change dir";
+ }
return $error. ' ('. $self->exporttype. ' to '. $self->machine. ')'
if $error;
}
}
sub ssh_cmd { #subroutine, not method
- use Net::SSH '0.07';
+ use Net::SSH '0.08';
&Net::SSH::ssh_cmd( { @_ } );
}
}
sub ssh_cmd { #subroutine, not method
- use Net::SSH '0.07';
+ use Net::SSH '0.08';
&Net::SSH::ssh_cmd( { @_ } );
}
use strict;
use vars qw( @ISA );
-use FS::Record qw( qsearch dbh );
+use FS::Record qw( qsearch dbh dbdef );
use FS::pkg_svc;
use FS::agent_type;
use FS::type_pkgs;
}
+ if ( $self->dbdef_table->column('freq')->type =~ /(int)/i ) {
+ my $error = $self->ut_number('freq');
+ return $error if $error;
+ } else {
+ $self->freq =~ /^(\d+[dw]?)$/
+ or return "Illegal or empty freq: ". $self->freq;
+ $self->freq($1);
+ }
+
$self->ut_numbern('pkgpart')
|| $self->ut_text('pkg')
|| $self->ut_text('comment')
|| $self->ut_anything('setup')
- || $self->ut_number('freq')
|| $self->ut_anything('recur')
|| $self->ut_alphan('plan')
|| $self->ut_anything('plandata')
=item svcpart [ SVCDB ]
-Returns the svcpart of a single service definition (see L<FS::part_svc>)
+Returns the svcpart of the primary service definition (see L<FS::part_svc>)
associated with this billing item definition (see L<FS::pkg_svc>). Returns
-false if there not exactly one service definition with quantity 1, or if
-SVCDB is specified and does not match the svcdb of the service definition,
+false if there not a primary service definition or exactly one service
+definition with quantity 1, or if SVCDB is specified and does not match the
+svcdb of the service definition,
=cut
sub svcpart {
my $self = shift;
my $svcdb = scalar(@_) ? shift : '';
- my @pkg_svc = grep {
- $_->quantity == 1
- && ( $svcdb eq $_->part_svc->svcdb || !$svcdb )
- } $self->pkg_svc;
+ my @svcdb_pkg_svc =
+ grep { ( $svcdb eq $_->part_svc->svcdb || !$svcdb ) } $self->pkg_svc;
+ my @pkg_svc = ();
+ @pkg_svc = grep { $_->primary_svc =~ /^Y/i } @svcdb_pkg_svc
+ if dbdef->table('pkg_svc')->column('primary_svc');
+ @pkg_svc = grep {$_->quantity == 1 } @svcdb_pkg_svc
+ unless @pkg_svc;
return '' if scalar(@pkg_svc) != 1;
$pkg_svc[0]->svcpart;
}
=item referral - Text name of this advertising source
+=item disabled - Disabled flag, empty or 'Y'
+
=back
=head1 NOTE
sub check {
my $self = shift;
- $self->ut_numbern('refnum')
+ my $error = $self->ut_numbern('refnum')
|| $self->ut_text('referral')
- || $self->SUPER::check
;
+ return $error if $error;
+
+ if ( $self->dbdef_table->column('disabled') ) {
+ $error = $self->ut_enum('disabled', [ '', 'Y' ] );
+ return $error if $error;
+ }
+
+ $self->SUPER::check;
}
=back
=item quantity - Quantity of this service definition that this billing item
definition includes
+=item primary_svc - primary flag, empty or 'Y'
+
=back
=head1 METHODS
return "Unknown pkgpart!" unless $self->part_pkg;
return "Unknown svcpart!" unless $self->part_svc;
+ if ( $self->dbdef_table->column('primary_svc') ) {
+ $error = $self->ut_enum('primary_svc', [ '', 'Y' ] );
+ return $error if $error;
+ }
+
$self->SUPER::check;
}
=back
-=head1 VERSION
-
-$Id: pkg_svc.pm,v 1.4 2003-08-05 00:20:45 khoff Exp $
-
=head1 BUGS
=head1 SEE ALSO
%attrib = (
'usr_at_zip_output_filter' => 'USR-AT-Zip-Output-Filter',
'ms_filter' => 'MS-Filter',
+ 'annex_compression_protoc' => 'Annex-Compression-Protocol',
+ 'xedia_ssh_privileges' => 'Xedia-SSH-Privileges',
'usr_blocks_received' => 'USR-Blocks-Received',
'shiva_called_number' => 'Shiva-Called-Number',
'annex_filter' => 'Annex-Filter',
'usr_channel_expansion' => 'USR-Channel-Expansion',
+ 'erx_tunnel_tos' => 'ERX-Tunnel-Tos',
'session_timeout' => 'Session-Timeout',
- 'usr_simplified_mnp_level' => 'USR-Simplified-MNP-Levels',
'ascend_route_ipx' => 'Ascend-Route-IPX',
- 'annex_user_server_locati' => 'Annex-User-Server-Location',
+ 'annex_error_correction_p' => 'Annex-Error-Correction-Prot',
'acc_callback_mode' => 'Acc-Callback-Mode',
'usr_filter_zones' => 'USR-Filter-Zones',
+ 'erx_input_gigapkts' => 'ERX-Input-Gigapkts',
'ascend_session_svr_key' => 'Ascend-Session-Svr-Key',
- 'le_nat_tcp_session_timeo' => 'LE-NAT-TCP-Session-Timeout',
+ 'bind_l2tp_tunnel_namf' => 'Bind_L2TP_Tunnel_Name',
+ 'ascend_dsl_cir_recv_limi' => 'Ascend-Dsl-CIR-Recv-Limit',
+ 'altiga_secondary_wins_g' => 'Altiga-Secondary-WINS-G',
'ascend_ts_idle_limit' => 'Ascend-TS-Idle-Limit',
'usr_port_tap_priority' => 'USR-Port-Tap-Priority',
+ 'cvpn3000_ipsec_client_fw' => 'CVPN3000-IPSec-Client-Fw-Filter-Name',
+ 'ascend_private_route_req' => 'Ascend-Private-Route-Required',
'ascend_private_route' => 'Ascend-Private-Route',
'prompt' => 'Prompt',
'acct_link_count' => 'Acct-Link-Count',
+ 'bind_auth_service_grq' => 'Bind_Auth_Service_Grp',
+ 'itk_tunnel_ip' => 'ITK-Tunnel-IP',
'login_lat_node' => 'Login-LAT-Node',
'usr_mbi_ct_pri_card_slot' => 'USR-Mbi_Ct_PRI_Card_Slot',
+ 'lac_real_poru' => 'LAC_Real_Port',
'erx_ingress_statistics' => 'ERX-Ingress-Statistics',
+ 'digest_nonce' => 'Digest-Nonce',
'annex_system_disc_reason' => 'Annex-System-Disc-Reason',
+ 'pool_name' => 'Pool-Name',
+ 'altiga_use_client_addres' => 'Altiga-Use-Client-Address-G/U',
+ 'police_bursu' => 'Police_Burst',
'usr_call_arrival_time' => 'USR-Call-Arrival-Time',
'ascend_disconnect_cause' => 'Ascend-Disconnect-Cause',
'ascend_user_acct_time' => 'Ascend-User-Acct-Time',
- 'ascend_appletalk_peer_mo' => 'Ascend-Appletalk-Peer-Mode',
'chap_challenge' => 'CHAP-Challenge',
'ascend_mpp_idle_percent' => 'Ascend-MPP-Idle-Percent',
'ascend_user_acct_port' => 'Ascend-User-Acct-Port',
+ 'ldap_group' => 'Ldap-Group',
'ascend_numbering_plan_id' => 'Ascend-Numbering-Plan-ID',
- 'ascend_access_intercept_' => 'Ascend-Access-Intercept-LEA',
- 'pvc_encapsulation_type' => 'PVC_Encapsulation_Type',
+ 'usr_last_number_dialed_o' => 'USR-Last-Number-Dialed-Out',
+ 'pvc_encapsulation_type' => 'PVC-Encapsulation-Type',
'ascend_bir_bridge_group' => 'Ascend-BIR-Bridge-Group',
'ascend_atm_group' => 'Ascend-ATM-Group',
'ascend_fr_svc_addr' => 'Ascend-FR-SVC-Addr',
'x_ascend_send_auth' => 'X-Ascend-Send-Auth',
'le_ip_pool' => 'LE-IP-Pool',
- 'annex_addr_resolution_se' => 'Annex-Addr-Resolution-Servers',
- 'usr_last_callers_number_' => 'USR-Last-Callers-Number-ANI',
+ 'post_proxy_type' => 'Post-Proxy-Type',
+ 'wispr_session_terminate_' => 'WISPr-Session-Terminate-Time',
+ 'bintec_pppextiftable' => 'BinTec-pppExtIfTable',
+ 'nomadix_subnet' => 'Nomadix-Subnet',
'login_port' => 'Login-Port',
'ms_chap2_response' => 'MS-CHAP2-Response',
- 'annex_secondary_dns_serv' => 'Annex-Secondary-DNS-Server',
'ascend_ipsec_profile' => 'Ascend-IPSEC-Profile',
+ 'usr_compression_algorith' => 'USR-Compression-Algorithm',
'usr_accm_type' => 'USR-ACCM-Type',
'simultaneous_use' => 'Simultaneous-Use',
+ 'cisco_account_info' => 'Cisco-Account-Info',
'framed_protocol' => 'Framed-Protocol',
+ 'erx_tunnel_maximum_sessi' => 'ERX-Tunnel-Maximum-Sessions',
+ 'redcreek_tunneled_wins_t' => 'RedCreek-Tunneled-WINS-Server2',
'ascend_recv_name' => 'Ascend-Recv-Name',
'usr_call_connecting_time' => 'USR-Call-Connecting-Time',
- 'tunnel_remote_name' => 'Tunnel_Remote_Name',
+ 'quintum_h323_gw_id' => 'Quintum-h323-gw-id',
+ 'acct_dyn_ac_ent' => 'Acct-Dyn-Ac-Ent',
+ 'tunnel_remote_name' => 'Tunnel-Remote-Name',
+ 'annex_ppp_trace_level' => 'Annex-PPP-Trace-Level',
+ 'cisco_call_type' => 'Cisco-Call-Type',
+ 'cisco_fax_recipient_coun' => 'Cisco-Fax-Recipient-Count',
+ 'altiga_ipsec_authenticat' => 'Altiga-IPSec-Authentication-G',
+ 'wispr_location_id' => 'WISPr-Location-ID',
+ 'itk_start_delay' => 'ITK-Start-Delay',
+ 'ascend_pre_output_packet' => 'Ascend-Pre-Output-Packets',
+ 'usr_rmmie_firmware_versi' => 'USR-RMMIE-Firmware-Version',
'usr_vts_session_key' => 'USR-VTS-Session-Key',
'ascend_fr_dce_n393' => 'Ascend-FR-DCE-N393',
'login_host' => 'Login-Host',
'usr_reply_script3' => 'USR-Reply-Script3',
+ 'cvpn3000_ipsec_split_tuo' => 'CVPN3000-IPSec-Split-Tunneling-Policy',
'ascend_pppoe_enable' => 'Ascend-PPPoE-Enable',
'annex_primary_dns_server' => 'Annex-Primary-DNS-Server',
'x_ascend_bridge_address' => 'X-Ascend-Bridge-Address',
'usr_number_of_link_naks' => 'USR-Number-of-Link-NAKs',
+ 'altiga_priority_on_sep_g' => 'Altiga-Priority-on-SEP-G/U',
'annex_cli_command' => 'Annex-CLI-Command',
'usr_pw_framed_routing_v2' => 'USR-PW_Framed_Routing_V2',
- 'usr_tunnel_switch_endpoi' => 'USR-Tunnel-Switch-Endpoint',
+ 'session_error_codf' => 'Session_Error_Code',
+ 'annex_user_server_locati' => 'Annex-User-Server-Location',
+ 'cisco_fax_mdn_address' => 'Cisco-Fax-Mdn-Address',
+ 'ascend_calling_subaddres' => 'Ascend-Calling-Subaddress',
'ascend_call_by_call' => 'Ascend-Call-By-Call',
'ascend_first_dest' => 'Ascend-First-Dest',
- 'usr_appletalk_network_ra' => 'USR-Appletalk-Network-Range',
'annex_tunnel_authen_type' => 'Annex-Tunnel-Authen-Type',
+ 'acct_type' => 'Acct-Type',
'sql_user_name' => 'SQL-User-Name',
'erx_secondary_dns' => 'ERX-Secondary-Dns',
+ 'bridge_grouq' => 'Bridge_Group',
'h323_return_code' => 'h323-return-code',
'annex_host_allow' => 'Annex-Host-Allow',
+ 'cvx_modem_end_recv_line_' => 'CVX-Modem-End-Recv-Line-Lvl',
+ 'sip_method' => 'Sip-Method',
'x_ascend_require_auth' => 'X-Ascend-Require-Auth',
+ 'cvpn3000_sep_card_assign' => 'CVPN3000-SEP-Card-Assignment',
'le_ipsec_deny_action' => 'LE-IPSec-Deny-Action',
'annex_edo' => 'Annex-EDO',
'acct_delay_time' => 'Acct-Delay-Time',
- 'ascend_call_block_durati' => 'Ascend-Call-Block-Duration',
'login_tcp_port' => 'Login-TCP-Port',
'ascend_temporary_rtes' => 'Ascend-Temporary-Rtes',
+ 'versanet_termination_cau' => 'Versanet-Termination-Cause',
'ascend_dialed_number' => 'Ascend-Dialed-Number',
- 'x_ascend_dec_channel_cou' => 'X-Ascend-Dec-Channel-Count',
+ 'cvpn3000_ipsec_authentic' => 'CVPN3000-IPSec-Authentication',
'ascend_fr_dlci' => 'Ascend-FR-DLCI',
'annex_modem_disc_reason' => 'Annex-Modem-Disc-Reason',
'x_ascend_receive_secret' => 'X-Ascend-Receive-Secret',
+ 'usr_ospf_addressless_ind' => 'USR-OSPF-Addressless-Index',
+ 'usr_ip_default_route_opt' => 'USR-IP-Default-Route-Option',
'char_noecho' => 'Char-Noecho',
+ 'redcreek_tunneled_search' => 'RedCreek-Tunneled-Search-List',
'ascend_pri_number_type' => 'Ascend-PRI-Number-Type',
- 'ascend_dsl_upstream_limi' => 'Ascend-Dsl-Upstream-Limit',
+ 'aat_ip_tos_apply_to' => 'AAT-IP-TOS-Apply-To',
'x_ascend_modem_shelfno' => 'X-Ascend-Modem-ShelfNo',
'prefix' => 'Prefix',
'usr_rad_dvmrp_metric' => 'USR-Rad-Dvmrp-Metric',
+ 'x_ascend_call_attempt_li' => 'X-Ascend-Call-Attempt-Limit',
'usr_ip_saa_filter' => 'USR-IP-SAA-Filter',
- 'ms_link_utilization_thre' => 'MS-Link-Utilization-Threshold',
+ 'itk_prompt' => 'ITK-Prompt',
+ 'ascend_port_redir_protoc' => 'Ascend-Port-Redir-Protocol',
+ 'cvx_modem_tx_packets' => 'CVX-Modem-Tx-Packets',
+ 'usr_tunnel_switch_endpoi' => 'USR-Tunnel-Switch-Endpoint',
'ascend_home_network_name' => 'Ascend-Home-Network-Name',
'acc_customer_id' => 'Acc-Customer-Id',
'message_authenticator' => 'Message-Authenticator',
- 'ascend_secondary_home_ag' => 'Ascend-Secondary-Home-Agent',
- 'x_ascend_pre_output_octe' => 'X-Ascend-Pre-Output-Octets',
+ 'cisco_fax_coverpage_flag' => 'Cisco-Fax-Coverpage-Flag',
'usr_multicast_forwarding' => 'USR-Multicast-Forwarding',
+ 'cvpn3000_allow_network_e' => 'CVPN3000-Allow-Network-Extension-Mode',
'ascend_call_direction' => 'Ascend-Call-Direction',
'acc_connect_rx_speed' => 'Acc-Connect-Rx-Speed',
'ascend_force_56' => 'Ascend-Force-56',
+ 'st_service_domain' => 'ST-Service-Domain',
'usr_harc_disconnect_code' => 'USR-HARC-Disconnect-Code',
'shasta_service_profile' => 'Shasta-Service-Profile',
'cisco_maximum_time' => 'Cisco-Maximum-Time',
'usr_tunnel_auth_hostname' => 'USR-Tunnel-Auth-Hostname',
- 'ascend_client_assign_win' => 'Ascend-Client-Assign-WINS',
- 'acc_modem_modulation_typ' => 'Acc-Modem-Modulation-Type',
'acc_ip_gateway_pri' => 'Acc-Ip-Gateway-Pri',
'ascend_bridge_address' => 'Ascend-Bridge-Address',
+ 'altiga_pptp_min_authenti' => 'Altiga-PPTP-Min-Authentication-G/U',
+ 'ns_secondary_wins' => 'NS-Secondary-WINS',
+ 'cbbsm_bandwidth' => 'CBBSM-Bandwidth',
'x_ascend_fr_link_mgt' => 'X-Ascend-FR-Link-Mgt',
+ 'altiga_ipsec_banner_g' => 'Altiga-IPSec-Banner-G',
'ascend_handle_ipx' => 'Ascend-Handle-IPX',
'ascend_x25_pad_alias_2' => 'Ascend-X25-Pad-Alias-2',
+ 'st_policy_name' => 'ST-Policy-Name',
'ascend_group' => 'Ascend-Group',
'ascend_dsl_rate_type' => 'Ascend-Dsl-Rate-Type',
+ 'tunnel_contexu' => 'Tunnel_Context',
'ascend_require_auth' => 'Ascend-Require-Auth',
+ 'cvx_modem_local_retrains' => 'CVX-Modem-Local-Retrains',
+ 'cvpn5000_echo' => 'CVPN5000-Echo',
+ 'cvx_secondary_dns' => 'CVX-Secondary-DNS',
'x_ascend_billing_number' => 'X-Ascend-Billing-Number',
'usr_orig_nas_type' => 'USR-Orig-NAS-Type',
'ascend_remote_fw' => 'Ascend-Remote-FW',
'acct_output_packets' => 'Acct-Output-Packets',
'lm_password' => 'LM-Password',
- 'tunnel_window' => 'Tunnel_Window',
- 'x_ascend_link_compressio' => 'X-Ascend-Link-Compression',
- 'x_ascend_base_channel_co' => 'X-Ascend-Base-Channel-Count',
+ 'tunnel_window' => 'Tunnel-Window',
'cisco_avpair' => 'Cisco-AVPair',
+ 'st_service_name' => 'ST-Service-Name',
'shiva_event_flags' => 'Shiva-Event-Flags',
- 'usr_number_of_rings_limi' => 'USR-Number-of-Rings-Limit',
+ 'annex_retrain_requests_s' => 'Annex-Retrain-Requests-Sent',
'ascend_ts_idle_mode' => 'Ascend-TS-Idle-Mode',
- 'ascend_bi_directional_au' => 'Ascend-Bi-Directional-Auth',
+ 'usr_ip_rip_simple_auth_p' => 'USR-IP-RIP-Simple-Auth-Password',
+ 'tunnel_deadtimf' => 'Tunnel_Deadtime',
'state' => 'State',
'usr_keypress_timeout' => 'USR-Keypress-Timeout',
'usr_pw_vpn_neighbor' => 'USR-PW_VPN_Neighbor',
+ 'erx_pppoe_description' => 'ERX-Pppoe-Description',
'ldap_userdn' => 'Ldap-UserDn',
'x_ascend_fr_n391' => 'X-Ascend-FR-N391',
- 'ascend_tunneling_protoco' => 'Ascend-Tunneling-Protocol',
+ 'ascend_calling_id_presen' => 'Ascend-Calling-Id-Presentatn',
+ 'erx_local_loopback_inter' => 'ERX-Local-Loopback-Interface',
'x_ascend_fr_direct' => 'X-Ascend-FR-Direct',
'nas_ip_address' => 'NAS-IP-Address',
'usr_call_end_time' => 'USR-Call-End-Time',
- 'tunnel_algorithm' => 'Tunnel_Algorithm',
+ 'acct_mcast_out_packett' => 'Acct_Mcast_Out_Packets',
+ 'tunnel_algorithm' => 'Tunnel-Algorithm',
'usr_vpn_encrypter' => 'USR-VPN-Encrypter',
+ 'tunnel_grouq' => 'Tunnel_Group',
'ascend_atm_connect_group' => 'Ascend-ATM-Connect-Group',
'x_ascend_ft1_caller' => 'X-Ascend-FT1-Caller',
+ 'usr_dnis_reauthenticatio' => 'USR-DNIS-ReAuthentication',
'login_callback_number' => 'Login-Callback-Number',
'usr_ip_rip_input_filter' => 'USR-IP-RIP-Input-Filter',
- 'usr_rmmie_last_update_ev' => 'USR-RMMIE-Last-Update-Event',
+ 'usr_rmmie_rcv_pwrlvl_330' => 'USR-RMMIE-Rcv-PwrLvl-3300Hz',
'h323_disconnect_cause' => 'h323-disconnect-cause',
'x_ascend_handle_ipx' => 'X-Ascend-Handle-IPX',
'usr_igmp_version' => 'USR-IGMP-Version',
'usr_imsi' => 'USR-IMSI',
'group_name' => 'Group-Name',
'usr_nas_type' => 'USR-NAS-Type',
+ 'context_namf' => 'Context-Name',
'ascend_ip_tos' => 'Ascend-IP-TOS',
'x_ascend_token_immediate' => 'X-Ascend-Token-Immediate',
- 'ascend_private_route_tab' => 'Ascend-Private-Route-Table-ID',
+ 'tunnel_session_auth_serw' => 'Tunnel_Session_Auth_Service_Grp',
'ms_chap2_cpw' => 'MS-CHAP2-CPW',
- 'tunnel_session_auth_ctx' => 'Tunnel_Session_Auth_Ctx',
+ 'tunnel_session_auth_ctx' => 'Tunnel-Session-Auth-Ctx',
'usr_mobile_numbytes_rxed' => 'USR-Mobile-NumBytes-Rxed',
'usr_mbi_ct_tdm_time_slot' => 'USR-Mbi_Ct_TDM_Time_Slot',
'ascend_x25_nui' => 'Ascend-X25-Nui',
'x_ascend_first_dest' => 'X-Ascend-First-Dest',
- 'x_ascend_num_in_multilin' => 'X-Ascend-Num-In-Multilink',
'usr_send_password' => 'USR-Send-Password',
+ 'x_ascend_fr_direct_profi' => 'X-Ascend-FR-Direct-Profile',
'x_ascend_fr_t391' => 'X-Ascend-FR-T391',
+ 'altiga_ipsec_sec_associa' => 'Altiga-IPSec-Sec-Association-G/U',
+ 'ip_address_pool_namf' => 'Ip_Address_Pool_Name',
'acct_input_octets' => 'Acct-Input-Octets',
- 'bridge_group' => 'Bridge_Group',
+ 'cvx_modem_begin_modulati' => 'CVX-Modem-Begin-Modulation',
+ 'wispr_session_terminatea' => 'WISPr-Session-Terminate-End-Of-Day',
+ 'cvpn3000_use_client_addr' => 'CVPN3000-Use-Client-Address',
+ 'bridge_group' => 'Bridge-Group',
'annex_sec_profile_index' => 'Annex-Sec-Profile-Index',
'acc_dns_server_pri' => 'Acc-Dns-Server-Pri',
'ms_acct_auth_type' => 'MS-Acct-Auth-Type',
+ 'x_ascend_maximum_call_du' => 'X-Ascend-Maximum-Call-Duration',
'tunnel_password' => 'Tunnel-Password',
+ 'framed_ipv6_prefix' => 'Framed-IPv6-Prefix',
'usr_reply_script5' => 'USR-Reply-Script5',
'shiva_links_in_bundle' => 'Shiva-Links-In-Bundle',
'ascend_fr_profile_name' => 'Ascend-FR-Profile-Name',
'ascend_mtu' => 'Ascend-MTU',
+ 'nokia_charging_id' => 'Nokia-Charging-Id',
+ 'cvpn3000_ms_client_subne' => 'CVPN3000-MS-Client-Subnet-Mask',
+ 'cvpn3000_ipsec_sec_assoc' => 'CVPN3000-IPSec-Sec-Association',
'cisco_ppp_async_map' => 'Cisco-PPP-Async-Map',
+ 'cvpn3000_user_auth_servf' => 'CVPN3000-User-Auth-Server-Port',
'cisco_num_in_multilink' => 'Cisco-Num-In-Multilink',
+ 'wispr_logoff_url' => 'WISPr-Logoff-URL',
'usr_mobile_ip_address' => 'USR-Mobile-IP-Address',
+ 'usr_final_tx_link_data_r' => 'USR-Final-Tx-Link-Data-Rate',
+ 'itk_ppp_compression_prot' => 'ITK-PPP-Compression-Prot',
'ascend_bridge' => 'Ascend-Bridge',
'x_ascend_presession_time' => 'X-Ascend-PreSession-Time',
- 'tunnel_cmd_timeout' => 'Tunnel_Cmd_Timeout',
+ 'aat_client_primary_dns' => 'AAT-Client-Primary-DNS',
+ 'cvpn3000_strip_realm' => 'CVPN3000-Strip-Realm',
+ 'tunnel_cmd_timeout' => 'Tunnel-Cmd-Timeout',
'ascend_multicast_client' => 'Ascend-Multicast-Client',
+ 'cvx_modem_remote_rate_ne' => 'CVX-Modem-Remote-Rate-Negs',
'tunnel_private_group_id' => 'Tunnel-Private-Group-Id',
'usr_rmmie_rcv_tot_pwrlvl' => 'USR-RMMIE-Rcv-Tot-PwrLvl',
'calling_station_id' => 'Calling-Station-Id',
- 'tunnel_rate_limit_burst' => 'Tunnel_Rate_Limit_Burst',
+ 'tunnel_rate_limit_burst' => 'Tunnel-Rate-Limit-Burst',
'usr_device_connected_to' => 'USR-Device-Connected-To',
+ 'aat_source_ip_check' => 'AAT-Source-IP-Check',
'login_lat_service' => 'Login-LAT-Service',
- 'x_ascend_home_network_na' => 'X-Ascend-Home-Network-Name',
'ascend_h323_fegw_address' => 'Ascend-H323-Fegw-Address',
'usr_called_party_number' => 'USR-Called-Party-Number',
+ 'bintec_ipnatpresettable' => 'BinTec-ipNatPresetTable',
'ascend_remove_seconds' => 'Ascend-Remove-Seconds',
'shiva_user_attributes' => 'Shiva-User-Attributes',
+ 'cisco_fax_dsn_flag' => 'Cisco-Fax-Dsn-Flag',
'x_ascend_route_ipx' => 'X-Ascend-Route-IPX',
'acc_route_policy' => 'Acc-Route-Policy',
+ 'bind_l2tp_flow_controm' => 'Bind_L2TP_Flow_Control',
+ 'erx_qos_profile_name' => 'ERX-Qos-Profile-Name',
'x_ascend_client_gateway' => 'X-Ascend-Client-Gateway',
- 'ms_mppe_encryption_polic' => 'MS-MPPE-Encryption-Policy',
+ 'pre_proxy_type' => 'Pre-Proxy-Type',
+ 'smb_account_ctrl_text' => 'SMB-Account-CTRL-TEXT',
'x_ascend_data_filter' => 'X-Ascend-Data-Filter',
+ 'usr_rmmie_last_update_ti' => 'USR-RMMIE-Last-Update-Time',
'ascend_atm_direct' => 'Ascend-ATM-Direct',
'ascend_session_type' => 'Ascend-Session-Type',
'x_ascend_fr_linkup' => 'X-Ascend-FR-LinkUp',
'ascend_metric' => 'Ascend-Metric',
+ 'x_ascend_assign_ip_clien' => 'X-Ascend-Assign-IP-Client',
'usr_speed_of_connection' => 'USR-Speed-Of-Connection',
+ 'cvpn3000_require_hw_clie' => 'CVPN3000-Require-HW-Client-Auth',
+ 'session_type' => 'Session-Type',
+ 'acct_input_octets_65' => 'Acct_Input_Octets_64',
'le_nat_outsource_outmap' => 'LE-NAT-Outsource-Outmap',
- 'pppoe_url' => 'PPPOE_URL',
- 'acct_mcast_out_octets' => 'Acct_Mcast_Out_Octets',
+ 'cvx_modem_local_rate_neg' => 'CVX-Modem-Local-Rate-Negs',
+ 'mcast_sene' => 'Mcast_Send',
+ 'pppoe_url' => 'PPPOE-URL',
+ 'erx_service_bundle' => 'ERX-Service-Bundle',
+ 'altiga_secondary_dns_g' => 'Altiga-Secondary-DNS-G',
+ 'bg_trans_bpdv' => 'BG_Trans_BPDU',
+ 'cvx_data_filter' => 'CVX-Data-Filter',
+ 'acct_mcast_out_octets' => 'Acct-Mcast-Out-Octets',
'ascend_callback' => 'Ascend-Callback',
'tunnel_client_auth_id' => 'Tunnel-Client-Auth-Id',
'acct_unique_session_id' => 'Acct-Unique-Session-Id',
'usr_port_tap_format' => 'USR-Port-Tap-Format',
'ascend_ckt_type' => 'Ascend-Ckt-Type',
'ascend_ppp_async_map' => 'Ascend-PPP-Async-Map',
+ 'usr_rmmie_rcv_pwrlvl_375' => 'USR-RMMIE-Rcv-PwrLvl-3750Hz',
'usr_acct_reason_code' => 'USR-Acct-Reason-Code',
'ascend_filter' => 'Ascend-Filter',
'h323_redirect_number' => 'h323-redirect-number',
'port_limit' => 'Port-Limit',
- 'x_ascend_shared_profile_' => 'X-Ascend-Shared-Profile-Enable',
- 'tunnel_police_rate' => 'Tunnel_Police_Rate',
- 'ascend_calling_id_screen' => 'Ascend-Calling-Id-Screening',
+ 'rewrite_rule' => 'Rewrite-Rule',
+ 'tunnel_police_rate' => 'Tunnel-Police-Rate',
'usr_multicast_proxy' => 'USR-Multicast-Proxy',
+ 'ascend_max_shared_users' => 'Ascend-Max-Shared-Users',
'usr_bridging' => 'USR-Bridging',
- 'usr_originate_answer_mod' => 'USR-Originate-Answer-Mode',
+ 'cvx_presession_time' => 'CVX-PreSession-Time',
+ 'cvpn5000_vpn_groupinfo' => 'CVPN5000-VPN-GroupInfo',
+ 'autz_type' => 'Autz-Type',
'x_ascend_fr_dlci' => 'X-Ascend-FR-DLCI',
'usr_request_type' => 'USR-Request-Type',
- 'acc_dialout_auth_usernam' => 'Acc-Dialout-Auth-Username',
+ 'acc_igmp_admin_state' => 'Acc-Igmp-Admin-State',
'ascend_host_info' => 'Ascend-Host-Info',
+ 'ascend_dhcp_maximum_leas' => 'Ascend-DHCP-Maximum-Leases',
'usr_rmmie_num_of_updates' => 'USR-RMMIE-Num-Of-Updates',
'x_ascend_fr_profile_name' => 'X-Ascend-FR-Profile-Name',
'ascend_fr_direct_profile' => 'Ascend-FR-Direct-Profile',
'x_ascend_bridge' => 'X-Ascend-Bridge',
- 'tunnel_deadtime' => 'Tunnel_Deadtime',
+ 'tunnel_deadtime' => 'Tunnel-Deadtime',
'ms_chap_error' => 'MS-CHAP-Error',
'framed_route' => 'Framed-Route',
+ 'sip_from' => 'Sip-From',
'expiration' => 'Expiration',
'ascend_backup' => 'Ascend-Backup',
'ascend_pre_output_octets' => 'Ascend-Pre-Output-Octets',
+ 'ascend_calling_id_number' => 'Ascend-Calling-Id-Number-Plan',
'framed_appletalk_zone' => 'Framed-AppleTalk-Zone',
'annex_audit_level' => 'Annex-Audit-Level',
- 'bind_auth_context' => 'Bind_Auth_Context',
- 'cisco_asing_ip_pool' => 'Cisco-Asing-IP-Pool',
+ 'digest_algorithm' => 'Digest-Algorithm',
+ 'bind_auth_context' => 'Bind-Auth-Context',
'ascend_user_acct_base' => 'Ascend-User-Acct-Base',
- 'mcast_receive' => 'Mcast_Receive',
+ 'st_secondary_dns_server' => 'ST-Secondary-DNS-Server',
+ 'mcast_receive' => 'Mcast-Receive',
'usr_ds0' => 'USR-DS0',
+ 'aat_atm_traffic_profile' => 'AAT-ATM-Traffic-Profile',
'ms_ras_vendor' => 'MS-RAS-Vendor',
- 'tunnel_domain' => 'Tunnel_Domain',
- 'usr_secondary_nbns_serve' => 'USR-Secondary_NBNS_Server',
- 'tunnel_max_sessions' => 'Tunnel_Max_Sessions',
+ 'tunnel_domain' => 'Tunnel-Domain',
+ 'tunnel_max_sessions' => 'Tunnel-Max-Sessions',
'ascend_ip_direct' => 'Ascend-IP-Direct',
+ 'xedia_address_pool' => 'Xedia-Address-Pool',
'idle_timeout' => 'Idle-Timeout',
+ 'tunnel_rate_limit_ratf' => 'Tunnel_Rate_Limit_Rate',
+ 'annex_rate_reneg_req_sen' => 'Annex-Rate-Reneg-Req-Sent',
+ 'usr_initial_tx_link_data' => 'USR-Initial-Tx-Link-Data-Rate',
'tunnel_server_auth_id' => 'Tunnel-Server-Auth-Id',
+ 'cvpn3000_ipsec_banner1' => 'CVPN3000-IPSec-Banner1',
'usr_start_time' => 'USR-Start-Time',
'usr_ip' => 'USR-IP',
+ 'cvpn3000_reqrd_client_fw' => 'CVPN3000-Reqrd-Client-Fw-Vendor-Code',
+ 'altiga_ipsec_secondary_d' => 'Altiga-IPSec-Secondary-Domains-G',
'usr_gateway_ip_address' => 'USR-Gateway-IP-Address',
- 'usr_number_of_characters' => 'USR-Number-Of-Characters-Lost',
'ascend_dba_monitor' => 'Ascend-DBA-Monitor',
+ 'ms_link_utilization_thre' => 'MS-Link-Utilization-Threshold',
+ 'st_primary_dns_server' => 'ST-Primary-DNS-Server',
+ 'acc_ace_token_ttl' => 'Acc-Ace-Token-Ttl',
'ms_chap_domain' => 'MS-CHAP-Domain',
'cisco_pre_input_octets' => 'Cisco-Pre-Input-Octets',
+ 'ascend_primary_home_agen' => 'Ascend-Primary-Home-Agent',
'acct_session_time' => 'Acct-Session-Time',
'framed_ip_address' => 'Framed-IP-Address',
- 'x_ascend_ip_pool_definit' => 'X-Ascend-IP-Pool-Definition',
- 'erx_alternate_cli_access' => 'ERX-Alternate-Cli-Access-Level',
- 'medium_type' => 'Medium_Type',
- 'acct_output_octets_64' => 'Acct_Output_Octets_64',
+ 'ns_admin_privilege' => 'NS-Admin-Privilege',
+ 'medium_type' => 'Medium-Type',
+ 'acct_output_octets_64' => 'Acct-Output-Octets-64',
'ascend_cir_timer' => 'Ascend-CIR-Timer',
- 'police_rate' => 'Police_Rate',
+ 'police_rate' => 'Police-Rate',
+ 'tunnel_functioo' => 'Tunnel_Function',
+ 'quintum_h323_time_and_da' => 'Quintum-h323-time-and-day',
+ 'ip_tos_fiele' => 'IP_TOS_Field',
+ 'erx_framed_ip_route_tag' => 'ERX-Framed-Ip-Route-Tag',
'ms_mppe_send_key' => 'MS-MPPE-Send-Key',
- 'ascend_multicast_gleave_' => 'Ascend-Multicast-GLeave-Delay',
+ 'ascend_maximum_call_dura' => 'Ascend-Maximum-Call-Duration',
+ 'pppoe_motn' => 'PPPOE_MOTM',
+ 'lac_poru' => 'LAC_Port',
+ 'bind_dot1q_slou' => 'Bind_Dot1q_Slot',
+ 'ascend_secondary_home_ag' => 'Ascend-Secondary-Home-Agent',
+ 'usr_ip_call_output_filte' => 'USR-IP-Call-Output-Filter',
'x_ascend_host_info' => 'X-Ascend-Host-Info',
'erx_egress_policy_name' => 'ERX-Egress-Policy-Name',
+ 'erx_ppp_password' => 'ERX-PPP-Password',
'user_name' => 'User-Name',
- 'bind_bypass_bypass' => 'Bind_Bypass_Bypass',
+ 'usr_number_of_characters' => 'USR-Number-Of-Characters-Lost',
+ 'bind_bypass_bypass' => 'Bind-Bypass-Bypass',
+ 'usr_rad_multicast_routip' => 'USR-Rad-Multicast-Routing-Proto',
'annex_acct_servers' => 'Annex-Acct-Servers',
+ 'cvpn5000_tunnel_throughp' => 'CVPN5000-Tunnel-Throughput',
'usr_chassis_call_channel' => 'USR-Chassis-Call-Channel',
'annex_input_filter' => 'Annex-Input-Filter',
- 'ascend_home_agent_passwo' => 'Ascend-Home-Agent-Password',
+ 'wispr_billing_class_of_s' => 'WISPr-Billing-Class-Of-Service',
'nas_port_type' => 'NAS-Port-Type',
+ 'cvx_client_assign_dns' => 'CVX-Client-Assign-DNS',
+ 'nomadix_maxbytesdown' => 'Nomadix-MaxBytesDown',
'ascend_endpoint_disc' => 'Ascend-Endpoint-Disc',
- 'tunnel_police_burst' => 'Tunnel_Police_Burst',
- 'bind_auth_max_sessions' => 'Bind_Auth_Max_Sessions',
+ 'tunnel_police_burst' => 'Tunnel-Police-Burst',
+ 'bind_auth_max_sessions' => 'Bind-Auth-Max-Sessions',
+ 'cvx_identification' => 'CVX-Identification',
+ 'cvpn3000_ipsec_allow_pas' => 'CVPN3000-IPSec-Allow-Passwd-Store',
+ 'ascend_calling_id_type_o' => 'Ascend-Calling-Id-Type-Of-Num',
'x_ascend_fr_dce_n392' => 'X-Ascend-FR-DCE-N392',
'usr_connect_term_reason' => 'USR-Connect-Term-Reason',
- 'usr_mbi_ct_pri_card_span' => 'USR-Mbi_Ct_PRI_Card_Span_Line',
'erx_egress_statistics' => 'ERX-Egress-Statistics',
'ascend_fr_dte_n392' => 'Ascend-FR-DTE-N392',
'usr_esn' => 'USR-ESN',
'x_ascend_fr_dte_n392' => 'X-Ascend-FR-DTE-N392',
+ 'itk_modem_init_string' => 'ITK-Modem-Init-String',
'x_ascend_fr_nailed_grp' => 'X-Ascend-FR-Nailed-Grp',
'ascend_bridge_non_pppoe' => 'Ascend-Bridge-Non-PPPoE',
+ 'cvpn3000_ipsec_reqrd_cli' => 'CVPN3000-IPSec-Reqrd-Client-Fw-Cap',
'ascend_ipx_alias' => 'Ascend-IPX-Alias',
'acc_tunnel_port' => 'Acc-Tunnel-Port',
+ 'quintum_h323_return_code' => 'Quintum-h323-return-code',
+ 'cvpn3000_l2tp_encryption' => 'CVPN3000-L2TP-Encryption',
'acct_input_gigawords' => 'Acct-Input-Gigawords',
+ 'bind_dot1q_poru' => 'Bind_Dot1q_Port',
+ 'altiga_primary_wins_g' => 'Altiga-Primary-WINS-G',
'ascend_maximum_channels' => 'Ascend-Maximum-Channels',
+ 'x_ascend_home_agent_pass' => 'X-Ascend-Home-Agent-Password',
'x_ascend_ppp_async_map' => 'X-Ascend-PPP-Async-Map',
+ 'usr_rmmie_manufacturer_i' => 'USR-RMMIE-Manufacturer-ID',
'usr_retrains_requested' => 'USR-Retrains-Requested',
'x_ascend_metric' => 'X-Ascend-Metric',
'acc_apsm_oversubscribed' => 'Acc-Apsm-Oversubscribed',
+ 'usr_originate_answer_mod' => 'USR-Originate-Answer-Mode',
'erx_atm_pcr' => 'ERX-Atm-PCR',
+ 'itk_nas_name' => 'ITK-NAS-Name',
'usr_ipx_routing' => 'USR-IPX-Routing',
'usr_tunneled_mlpp' => 'USR-Tunneled-MLPP',
'usr_send_script5' => 'USR-Send-Script5',
'ascend_traffic_shaper' => 'Ascend-Traffic-Shaper',
+ 'ascend_client_secondarya' => 'Ascend-Client-Secondary-DNS',
'ascend_bacp_enable' => 'Ascend-BACP-Enable',
+ 'usr_call_terminate_in_gm' => 'USR-Call-Terminate-in-GMT',
'login_time' => 'Login-Time',
+ 'bg_path_cosu' => 'BG_Path_Cost',
+ 'aat_require_auth' => 'AAT-Require-Auth',
+ 'cvpn3000_reqrd_client_fy' => 'CVPN3000-Reqrd-Client-Fw-Description',
'ascend_call_type' => 'Ascend-Call-Type',
'erx_address_pool_name' => 'ERX-Address-Pool-Name',
+ 'cvpn3000_ipsec_backup_sf' => 'CVPN3000-IPSec-Backup-Server-List',
'h323_incoming_conf_id' => 'h323-incoming-conf-id',
+ 'user_profile' => 'User-Profile',
+ 'ip_host_adds' => 'Ip_Host_Addr',
+ 'ns_primary_wins' => 'NS-Primary-WINS',
'packet_type' => 'Packet-Type',
+ 'bind_auth_max_sessiont' => 'Bind_Auth_Max_Sessions',
+ 'altiga_allow_alpha_only_' => 'Altiga-Allow-Alpha-Only-Passwords-G',
'usr_security_resp_limit' => 'USR-Security-Resp-Limit',
- 'ip_address_pool_name' => 'Ip_Address_Pool_Name',
- 'ascend_cbcp_trunk_group' => 'Ascend-CBCP-Trunk-Group',
+ 'ip_address_pool_name' => 'Ip-Address-Pool-Name',
'ascend_ipx_node_addr' => 'Ascend-IPX-Node-Addr',
+ 'ascend_cbcp_trunk_group' => 'Ascend-CBCP-Trunk-Group',
'ascend_menu_selector' => 'Ascend-Menu-Selector',
+ 'ascend_assign_ip_global_' => 'Ascend-Assign-IP-Global-Pool',
'usr_ds0s' => 'USR-DS0s',
'usr_actual_voltage' => 'USR-Actual-Voltage',
+ 'quintum_h323_call_type' => 'Quintum-h323-call-type',
'annex_sw_version' => 'Annex-SW-Version',
- 'ascend_history_weigh_typ' => 'Ascend-History-Weigh-Type',
'ascend_receive_secret' => 'Ascend-Receive-Secret',
+ 'bintec_qospolicytable' => 'BinTec-qosPolicyTable',
'usr_ip_rip_policies' => 'USR-IP-RIP-Policies',
+ 'redcreek_tunneled_ip_add' => 'RedCreek-Tunneled-IP-Addr',
'ascend_pw_warntime' => 'Ascend-PW-Warntime',
- 'x_ascend_assign_ip_serve' => 'X-Ascend-Assign-IP-Server',
- 'tunnel_session_auth_serv' => 'Tunnel_Session_Auth_Service_Grp',
+ 'x_ascend_inc_channel_cou' => 'X-Ascend-Inc-Channel-Count',
'usr_blocks_resent' => 'USR-Blocks-Resent',
'usr_fallback_enabled' => 'USR-Fallback-Enabled',
'arap_challenge_response' => 'ARAP-Challenge-Response',
- 'tunnel_session_auth' => 'Tunnel_Session_Auth',
+ 'tunnel_session_auth' => 'Tunnel-Session-Auth',
'usr_sync_async_mode' => 'USR-Sync-Async-Mode',
+ 'itk_dialout_type' => 'ITK-Dialout-Type',
+ 'extreme_netlogin_url' => 'Extreme-Netlogin-Url',
'client_port_dnis' => 'Client-Port-DNIS',
+ 'digest_realm' => 'Digest-Realm',
'ascend_ppp_vj_1172' => 'Ascend-PPP-VJ-1172',
- 'ascend_remote_addr' => 'Ascend-Remote-Addr',
'ascend_fr_n391' => 'Ascend-FR-N391',
+ 'ascend_remote_addr' => 'Ascend-Remote-Addr',
'client_port_id' => 'Client-Port-Id',
- 'usr_num_fax_pages_proces' => 'USR-Num-Fax-Pages-Processed',
+ 'digest_body_digest' => 'Digest-Body-Digest',
'le_ipsec_active_profile' => 'LE-IPSec-Active-Profile',
+ 'digest_cnonce' => 'Digest-CNonce',
'usr_port_tap_facility' => 'USR-Port-Tap-Facility',
'usr_callback_type' => 'USR-Callback-Type',
+ 'client_dns_prj' => 'Client_DNS_Pri',
+ 'digest_response' => 'Digest-Response',
'login_lat_group' => 'Login-LAT-Group',
'x_ascend_call_type' => 'X-Ascend-Call-Type',
'ascend_route_ip' => 'Ascend-Route-IP',
+ 'usr_rad_multicast_routio' => 'USR-Rad-Multicast-Routing-RtLim',
'usr_pw_vpn_id' => 'USR-PW_VPN_ID',
- 'le_nat_sess_dir_fail_act' => 'LE-NAT-Sess-Dir-Fail-Action',
+ 'cvx_modem_end_modulation' => 'CVX-Modem-End-Modulation',
+ 'cvpn3000_pptp_mppc_compr' => 'CVPN3000-PPTP-MPPC-Compression',
'cisco_pre_output_octets' => 'Cisco-Pre-Output-Octets',
'h323_billing_model' => 'h323-billing-model',
'usr_equalization_type' => 'USR-Equalization-Type',
'acc_clearing_cause' => 'Acc-Clearing-Cause',
+ 'altiga_access_hours_g_u' => 'Altiga-Access-Hours-G/U',
+ 'cvpn3000_ipsec_user_grou' => 'CVPN3000-IPSec-User-Group-Lock',
'x_ascend_menu_selector' => 'X-Ascend-Menu-Selector',
'x_ascend_netware_timeout' => 'X-Ascend-Netware-timeout',
'ascend_fr_linkup' => 'Ascend-FR-LinkUp',
- 'police_burst' => 'Police_Burst',
+ 'annex_num_in_multilink' => 'Annex-Num-In-Multilink',
+ 'police_burst' => 'Police-Burst',
+ 'altiga_l2tp_min_authenti' => 'Altiga-L2TP-Min-Authentication-G/U',
'ascend_filter_required' => 'Ascend-Filter-Required',
- 'usr_compression_algorith' => 'USR-Compression-Algorithm',
- 'le_ipsec_outsource_profi' => 'LE-IPSec-Outsource-Profile',
'x_ascend_idle_limit' => 'X-Ascend-Idle-Limit',
- 'usr_call_terminate_in_gm' => 'USR-Call-Terminate-in-GMT',
- 'usr_ipx_call_output_filt' => 'USR-IPX-Call-Output-Filter',
- 'ip_tos_field' => 'IP_TOS_Field',
+ 'nomadix_logoff_url' => 'Nomadix-Logoff-URL',
+ 'cvpn3000_ms_client_icpt_' => 'CVPN3000-MS-Client-Icpt-DHCP-Conf-Msg',
+ 'ip_tos_field' => 'IP-TOS-Field',
'ascend_ip_tos_apply_to' => 'Ascend-IP-TOS-Apply-To',
- 'tunnel_l2f_second_passwo' => 'Tunnel_L2F_Second_Password',
'usr_call_event_code' => 'USR-Call-Event-Code',
+ 'usr_et_bridge_output_fil' => 'USR-ET-Bridge-Output-Filter',
+ 'le_nat_sess_dir_fail_act' => 'LE-NAT-Sess-Dir-Fail-Action',
'usr_rmmie_product_code' => 'USR-RMMIE-Product-Code',
'usr_host_type' => 'USR-Host-Type',
+ 'erx_tunnel_interface_id' => 'ERX-Tunnel-Interface-Id',
'ascend_send_auth' => 'Ascend-Send-Auth',
'shiva_compression_type' => 'Shiva-Compression-Type',
- 'filter_id' => 'Filter-Id',
+ 'itk_banner' => 'ITK-Banner',
'ascend_ft1_caller' => 'Ascend-FT1-Caller',
- 'erx_cli_initial_access_l' => 'ERX-Cli-Initial-Access-Level',
+ 'filter_id' => 'Filter-Id',
+ 'annex_pre_output_octets' => 'Annex-Pre-Output-Octets',
+ 'acct_mcast_in_octett' => 'Acct_Mcast_In_Octets',
'usr_log_filter_packets' => 'USR-Log-Filter-Packets',
'ascend_fr_nailed_grp' => 'Ascend-FR-Nailed-Grp',
- 'usr_initial_tx_link_data' => 'USR-Initial-Tx-Link-Data-Rate',
+ 'ascend_atm_loopback_cell' => 'Ascend-ATM-Loopback-Cell-Loss',
+ 'usr_at_rtmp_output_filte' => 'USR-AT-RTMP-Output-Filter',
'acc_input_errors' => 'Acc-Input-Errors',
'x_ascend_user_acct_port' => 'X-Ascend-User-Acct-Port',
'erx_secondary_wins' => 'ERX-Secondary-Wins',
'usr_rmmie_serial_number' => 'USR-RMMIE-Serial-Number',
- 'ascend_client_primary_dn' => 'Ascend-Client-Primary-DNS',
+ 'usr_et_bridge_input_filt' => 'USR-ET-Bridge-Input-Filter',
+ 'ns_primary_dns' => 'NS-Primary-DNS',
'usr_slot_connected_to' => 'USR-Slot-Connected-To',
'shiva_disconnect_reason' => 'Shiva-Disconnect-Reason',
+ 'cvpn5000_client_assignee' => 'CVPN5000-Client-Assigned-IPX',
+ 'cvx_radius_redirect' => 'CVX-Radius-Redirect',
'usr_receive_acc_map' => 'USR-Receive-Acc-Map',
- 'usr_compression_reset_mo' => 'USR-Compression-Reset-Mode',
- 'usr_rmmie_planned_discon' => 'USR-RMMIE-Planned-Disconnect',
- 'ascend_client_assign_dns' => 'Ascend-Client-Assign-DNS',
+ 'x_ascend_tunneling_proto' => 'X-Ascend-Tunneling-Protocol',
+ 'itk_acct_serv_ip' => 'ITK-Acct-Serv-IP',
'ascend_fr_type' => 'Ascend-FR-Type',
+ 'ascend_client_assign_dns' => 'Ascend-Client-Assign-DNS',
+ 'annex_retrain_requests_r' => 'Annex-Retrain-Requests-Rcvd',
+ 'x_ascend_assign_ip_globa' => 'X-Ascend-Assign-IP-Global-Pool',
'tunnel_client_endpoint' => 'Tunnel-Client-Endpoint',
+ 'alteon_service_type' => 'Alteon-Service-Type',
'x_ascend_send_secret' => 'X-Ascend-Send-Secret',
'x_ascend_call_filter' => 'X-Ascend-Call-Filter',
'usr_ipx_rip_input_filter' => 'USR-IPX-RIP-Input-Filter',
'x_ascend_maximum_time' => 'X-Ascend-Maximum-Time',
- 'ascend_x25_pad_x3_profil' => 'Ascend-X25-Pad-X3-Profile',
- 'pvc_profile_name' => 'PVC_Profile_Name',
+ 'pvc_profile_name' => 'PVC-Profile-Name',
+ 'usr_framed_ip_address_po' => 'USR-Framed_IP_Address_Pool_Name',
+ 'cvpn3000_ipsec_split_dns' => 'CVPN3000-IPSec-Split-DNS-Names',
'ascend_global_call_id' => 'Ascend-Global-Call-Id',
- 'tunnel_local_name' => 'Tunnel_Local_Name',
+ 'usr_initial_rx_link_data' => 'USR-Initial-Rx-Link-Data-Rate',
+ 'st_primary_nbns_server' => 'ST-Primary-NBNS-Server',
+ 'usr_number_of_rings_limi' => 'USR-Number-of-Rings-Limit',
+ 'tunnel_local_name' => 'Tunnel-Local-Name',
'ascend_fr_t392' => 'Ascend-FR-T392',
- 'usr_dnis_reauthenticatio' => 'USR-DNIS-ReAuthentication',
- 'ascend_pre_output_packet' => 'Ascend-Pre-Output-Packets',
+ 'annex_pool_id' => 'Annex-Pool-Id',
'ascend_token_immediate' => 'Ascend-Token-Immediate',
+ 'usr_rmmie_firmware_build' => 'USR-RMMIE-Firmware-Build-Date',
+ 'wispr_bandwidth_min_down' => 'WISPr-Bandwidth-Min-Down',
'usr_chassis_call_slot' => 'USR-Chassis-Call-Slot',
- 'rate_limit_burst' => 'Rate_Limit_Burst',
+ 'rate_limit_burst' => 'Rate-Limit-Burst',
'cisco_route_ip' => 'Cisco-Route-IP',
- 'dhcp_max_leases' => 'DHCP_Max_Leases',
+ 'xedia_netbios_server' => 'Xedia-NetBios-Server',
+ 'session_error_msg' => 'Session-Error-Msg',
+ 'dhcp_max_leases' => 'DHCP-Max-Leases',
+ 'acc_vpsm_reject_cause' => 'Acc-Vpsm-Reject-Cause',
'user_category' => 'User-Category',
- 'x_ascend_maximum_call_du' => 'X-Ascend-Maximum-Call-Duration',
- 'bind_type' => 'Bind_Type',
+ 'x_ascend_multicast_rate_' => 'X-Ascend-Multicast-Rate-Limit',
+ 'cvpn3000_ipsec_auth_on_r' => 'CVPN3000-IPSec-Auth-On-Rekey',
+ 'altiga_min_password_leng' => 'Altiga-Min-Password-Length-G',
+ 'bind_type' => 'Bind-Type',
+ 'ascend_tunneling_protoco' => 'Ascend-Tunneling-Protocol',
+ 'cvx_modem_retx_packets' => 'CVX-Modem-ReTx-Packets',
'usr_framed_ipx_route' => 'USR-Framed-IPX-Route',
- 'rate_limit_rate' => 'Rate_Limit_Rate',
+ 'rate_limit_rate' => 'Rate-Limit-Rate',
'ascend_atm_connect_vpi' => 'Ascend-ATM-Connect-Vpi',
- 'x_ascend_inc_channel_cou' => 'X-Ascend-Inc-Channel-Count',
'connect_info' => 'Connect-Info',
- 'x_ascend_pre_input_packe' => 'X-Ascend-Pre-Input-Packets',
'usr_port_tap_address' => 'USR-Port-Tap-Address',
- 'ascend_home_agent_udp_po' => 'Ascend-Home-Agent-UDP-Port',
- 'usr_final_rx_link_data_r' => 'USR-Final-Rx-Link-Data-Rate',
+ 'usr_simplified_mnp_level' => 'USR-Simplified-MNP-Levels',
+ 'mcast_receivf' => 'Mcast_Receive',
+ 'annex_begin_modulation' => 'Annex-Begin-Modulation',
'usr_pw_usr_ifilter_ip' => 'USR-PW_USR_IFilter_IP',
'ascend_route_appletalk' => 'Ascend-Route-Appletalk',
'ms_chap_lm_enc_pw' => 'MS-CHAP-LM-Enc-PW',
+ 'altiga_ipsec_over_nat_po' => 'Altiga-IPSec-Over-NAT-Port-Num-G',
+ 'itk_isdn_prot' => 'ITK-ISDN-Prot',
'ascend_callback_delay' => 'Ascend-Callback-Delay',
+ 'session_error_code' => 'Session-Error-Code',
+ 'nomadix_endofsession' => 'Nomadix-EndofSession',
'x_ascend_bacp_enable' => 'X-Ascend-BACP-Enable',
- 'bg_trans_bpdu' => 'BG_Trans_BPDU',
+ 'bg_trans_bpdu' => 'BG-Trans-BPDU',
+ 'bind_int_interface_namf' => 'Bind_Int_Interface_Name',
+ 'foundry_privilege_level' => 'Foundry-Privilege-Level',
'huntgroup_name' => 'Huntgroup-Name',
'x_ascend_ipx_alias' => 'X-Ascend-IPX-Alias',
- 'x_ascend_secondary_home_' => 'X-Ascend-Secondary-Home-Agent',
+ 'tunnel_l2f_second_passwp' => 'Tunnel_L2F_Second_Password',
+ 'xedia_dns_server' => 'Xedia-DNS-Server',
'usr_ipx_wan' => 'USR-IPX-WAN',
+ 'annex_addr_resolution_se' => 'Annex-Addr-Resolution-Servers',
+ 'acct_output_octets_65' => 'Acct_Output_Octets_64',
'menu' => 'Menu',
+ 'erx_tunnel_nas_port_meth' => 'ERX-Tunnel-Nas-Port-Method',
+ 'aat_output_octets_diff' => 'AAT-Output-Octets-Diff',
'x_ascend_fr_direct_dlci' => 'X-Ascend-FR-Direct-DLCI',
'acct_status_type' => 'Acct-Status-Type',
'ascend_port_redir_server' => 'Ascend-Port-Redir-Server',
+ 'telebit_port_name' => 'Telebit-Port-Name',
'acc_dns_server_sec' => 'Acc-Dns-Server-Sec',
+ 'cvx_modem_remote_retrain' => 'CVX-Modem-Remote-Retrains',
'ascend_minimum_channels' => 'Ascend-Minimum-Channels',
- 'ascend_telnet_profile' => 'Ascend-Telnet-Profile',
'ascend_ipx_route' => 'Ascend-IPX-Route',
+ 'ascend_telnet_profile' => 'Ascend-Telnet-Profile',
'usr_call_connect_in_gmt' => 'USR-Call-Connect-in-GMT',
+ 'usr_cusr_hat_script_rule' => 'USR-CUSR-hat-Script-Rules',
'x_ascend_dba_monitor' => 'X-Ascend-DBA-Monitor',
+ 'response_packet_type' => 'Response-Packet-Type',
'usr_event_id' => 'USR-Event-Id',
+ 'cvpn3000_ipsec_over_udp_' => 'CVPN3000-IPSec-Over-UDP-Port',
'ascend_inc_channel_count' => 'Ascend-Inc-Channel-Count',
'usr_send_script3' => 'USR-Send-Script3',
+ 'annex_pre_input_packets' => 'Annex-Pre-Input-Packets',
'framed_callback_id' => 'Framed-Callback-Id',
+ 'xedia_client_access_netw' => 'Xedia-Client-Access-Network',
'arap_zone_access' => 'ARAP-Zone-Access',
+ 'ascend_port_redir_portnu' => 'Ascend-Port-Redir-Portnum',
'service_type' => 'Service-Type',
'usr_nfas_id' => 'USR-NFAS-ID',
'shiva_calling_number' => 'Shiva-Calling-Number',
'ascend_user_acct_host' => 'Ascend-User-Acct-Host',
+ 'tunnel_session_auth_serv' => 'Tunnel-Session-Auth-Service-Grp',
+ 'juniper_deny_commands' => 'Juniper-Deny-Commands',
'ascend_fr_link_mgt' => 'Ascend-FR-Link-Mgt',
+ 'nokia_imsi' => 'Nokia-IMSI',
+ 'quintum_h323_prompt_id' => 'Quintum-h323-prompt-id',
+ 'cvpn3000_require_individ' => 'CVPN3000-Require-Individual-User-Auth',
+ 'tunnel_retransmiu' => 'Tunnel_Retransmit',
+ 'source_validatioo' => 'Source_Validation',
+ 'sip_to' => 'Sip-To',
'ms_primary_nbns_server' => 'MS-Primary-NBNS-Server',
'quintum_avpair' => 'Quintum-AVPair',
- 'x_ascend_home_agent_pass' => 'X-Ascend-Home-Agent-Password',
'ascend_transit_number' => 'Ascend-Transit-Number',
'ascend_cache_refresh' => 'Ascend-Cache-Refresh',
- 'versanet_termination_cau' => 'Versanet-Termination-Cause',
'ascend_user_acct_type' => 'Ascend-User-Acct-Type',
+ 'usr_num_fax_pages_proces' => 'USR-Num-Fax-Pages-Processed',
'usr_mic' => 'USR-MIC',
- 'ascend_base_channel_coun' => 'Ascend-Base-Channel-Count',
- 'x_ascend_dhcp_pool_numbe' => 'X-Ascend-DHCP-Pool-Number',
+ 'usr_failure_to_connect_r' => 'USR-Failure-to-Connect-Reason',
+ 'cisco_fax_auth_status' => 'Cisco-Fax-Auth-Status',
+ 'bind_dot1q_vlan_tag_ie' => 'Bind_Dot1q_Vlan_Tag_Id',
'ms_chap2_success' => 'MS-CHAP2-Success',
+ 'erx_tunnel_virtual_route' => 'ERX-Tunnel-Virtual-Router',
'cisco_idle_limit' => 'Cisco-Idle-Limit',
'ascend_pw_lifetime' => 'Ascend-PW-Lifetime',
+ 'cvpn3000_access_hours' => 'CVPN3000-Access-Hours',
+ 'bintec_sapcirctable' => 'BinTec-sapCircTable',
'usr_packet_bus_session' => 'USR-Packet-Bus-Session',
- 'ascend_atm_loopback_cell' => 'Ascend-ATM-Loopback-Cell-Loss',
- 'acct_input_packets_64' => 'Acct_Input_Packets_64',
+ 'acct_input_packets_64' => 'Acct-Input-Packets-64',
+ 'ascend_x25_pad_x3_parame' => 'Ascend-X25-Pad-X3-Parameters',
+ 'usr_secondary_nbns_serve' => 'USR-Secondary_NBNS_Server',
'ascend_modem_slotno' => 'Ascend-Modem-SlotNo',
+ 'digest_qop' => 'Digest-QOP',
'usr_characters_received' => 'USR-Characters-Received',
+ 'rate_limit_ratf' => 'Rate_Limit_Rate',
'ms_bap_usage' => 'MS-BAP-Usage',
'cisco_data_filter' => 'Cisco-Data-Filter',
- 'ascend_seconds_of_histor' => 'Ascend-Seconds-Of-History',
+ 'usr_simplified_v42bis_us' => 'USR-Simplified-V42bis-Usage',
'h323_setup_time' => 'h323-setup-time',
- 'acc_dialout_auth_passwor' => 'Acc-Dialout-Auth-Password',
+ 'annex_wan_number' => 'Annex-Wan-Number',
+ 'cvx_vpop_id' => 'CVX-VPOP-ID',
+ 'usr_pw_tunnel_authentica' => 'USR-PW_Tunnel_Authentication',
'le_nat_outsource_inmap' => 'LE-NAT-Outsource-Inmap',
+ 'cvx_modem_begin_recv_lin' => 'CVX-Modem-Begin-Recv-Line-Lvl',
+ 'telebit_login_command' => 'Telebit-Login-Command',
+ 'cisco_command_code' => 'Cisco-Command-Code',
+ 'itk_ppp_auth_type' => 'ITK-PPP-Auth-Type',
+ 'bintec_qosiftable' => 'BinTec-qosIfTable',
+ 'x_ascend_mpp_idle_percen' => 'X-Ascend-MPP-Idle-Percent',
'usr_sap_filter_in' => 'USR-SAP-Filter-In',
'framed_appletalk_link' => 'Framed-AppleTalk-Link',
- 'usr_initial_rx_link_data' => 'USR-Initial-Rx-Link-Data-Rate',
- 'usr_ospf_addressless_ind' => 'USR-OSPF-Addressless-Index',
+ 'tunnel_domaio' => 'Tunnel_Domain',
'usr_ipx' => 'USR-IPX',
+ 'nas_real_poru' => 'NAS_Real_Port',
'shiva_connect_reason' => 'Shiva-Connect-Reason',
+ 'x_ascend_pre_output_octe' => 'X-Ascend-Pre-Output-Octets',
'cisco_ppp_vj_slot_comp' => 'Cisco-PPP-VJ-Slot-Comp',
+ 'freeradius_proxied_to' => 'Freeradius-Proxied-To',
'ascend_atm_vpi' => 'Ascend-ATM-Vpi',
'acc_ml_mlx_admin_state' => 'Acc-ML-MLX-Admin-State',
+ 'cvx_modem_snr' => 'CVX-Modem-SNR',
'usr_igmp_robustness' => 'USR-IGMP-Robustness',
+ 'annex_rate_reneg_req_rcv' => 'Annex-Rate-Reneg-Req-Rcvd',
'add_prefix' => 'Add-Prefix',
'x_ascend_call_by_call' => 'X-Ascend-Call-By-Call',
- 'x_ascend_connect_progres' => 'X-Ascend-Connect-Progress',
+ 'usr_last_callers_number_' => 'USR-Last-Callers-Number-ANI',
+ 'postauth_type' => 'PostAuth-Type',
+ 'pvc_circuit_paddinh' => 'PVC_Circuit_Padding',
'usr_at_rtmp_input_filter' => 'USR-AT-RTMP-Input-Filter',
'erx_igmp_enable' => 'ERX-Igmp-Enable',
- 'usr_rmmie_rcv_pwrlvl_375' => 'USR-RMMIE-Rcv-PwrLvl-3750Hz',
+ 'bind_bypass_contexu' => 'Bind_Bypass_Context',
+ 'x_ascend_num_in_multilin' => 'X-Ascend-Num-In-Multilink',
'usr_pw_packet' => 'USR-PW_Packet',
'dialback_no' => 'Dialback-No',
'ascend_ip_tos_precedence' => 'Ascend-IP-TOS-Precedence',
+ 'cvpn5000_vpn_password' => 'CVPN5000-VPN-Password',
'annex_cli_filter' => 'Annex-CLI-Filter',
'x_ascend_dial_number' => 'X-Ascend-Dial-Number',
'usr_iwf_call_identifier' => 'USR-IWF-Call-Identifier',
'ms_secondary_dns_server' => 'MS-Secondary-DNS-Server',
- 'ascend_client_secondary_' => 'Ascend-Client-Secondary-WINS',
'shiva_type_of_service' => 'Shiva-Type-Of-Service',
- 'usr_framed_ip_address_po' => 'USR-Framed_IP_Address_Pool_Name',
- 'bind_ses_context' => 'Bind_Ses_Context',
+ 'bind_ses_context' => 'Bind-Ses-Context',
'acc_reason_code' => 'Acc-Reason-Code',
'ms_chap_cpw_1' => 'MS-CHAP-CPW-1',
+ 'wispr_bandwidth_max_down' => 'WISPr-Bandwidth-Max-Down',
'h323_call_type' => 'h323-call-type',
+ 'bind_bypass_bypast' => 'Bind_Bypass_Bypass',
+ 'usr_number_of_link_timeo' => 'USR-Number-of-Link-Timeouts',
'ascend_fr_08_mode' => 'Ascend-FR-08-Mode',
'usr_calling_party_number' => 'USR-Calling-Party-Number',
- 'usr_rad_multicast_routin' => 'USR-Rad-Multicast-Routing-RtLim',
'usr_reply_script2' => 'USR-Reply-Script2',
'usr_security_login_limit' => 'USR-Security-Login-Limit',
'cisco_link_compression' => 'Cisco-Link-Compression',
- 'usr_et_bridge_output_fil' => 'USR-ET-Bridge-Output-Filter',
'ascend_vrouter_name' => 'Ascend-VRouter-Name',
+ 'erx_ppp_auth_protocol' => 'ERX-PPP-Auth-Protocol',
+ 'x_ascend_call_block_dura' => 'X-Ascend-Call-Block-Duration',
'usr_modem_setup_time' => 'USR-Modem-Setup-Time',
+ 'pppoe_urm' => 'PPPOE_URL',
'cisco_ip_direct' => 'Cisco-IP-Direct',
'x_ascend_temporary_rtes' => 'X-Ascend-Temporary-Rtes',
'ascend_x25_pad_alias_3' => 'Ascend-X25-Pad-Alias-3',
- 'usr_rmmie_pwrlvl_xmit_lv' => 'USR-RMMIE-PwrLvl-Xmit-Lvl',
+ 'annex_multilink_id' => 'Annex-Multilink-Id',
+ 'mcast_maxgroupt' => 'Mcast_MaxGroups',
'configuration_token' => 'Configuration-Token',
- 'usr_at_rtmp_output_filte' => 'USR-AT-RTMP-Output-Filter',
- 'usr_ip_default_route_opt' => 'USR-IP-Default-Route-Option',
- 'ascend_calling_subaddres' => 'Ascend-Calling-Subaddress',
+ 'ascend_h323_conference_i' => 'Ascend-H323-Conference-Id',
+ 'ascend_ipx_header_compre' => 'Ascend-IPX-Header-Compression',
'stripped_user_name' => 'Stripped-User-Name',
+ 'usr_ipx_rip_output_filte' => 'USR-IPX-RIP-Output-Filter',
'cisco_call_filter' => 'Cisco-Call-Filter',
+ 'nas_ipv6_address' => 'NAS-IPv6-Address',
'termination_menu' => 'Termination-Menu',
+ 'ascend_shared_profile_en' => 'Ascend-Shared-Profile-Enable',
'port_message' => 'Port-Message',
- 'usr_igmp_maximum_respons' => 'USR-IGMP-Maximum-Response-Time',
'erx_ingress_policy_name' => 'ERX-Ingress-Policy-Name',
- 'ascend_call_attempt_limi' => 'Ascend-Call-Attempt-Limit',
'acc_service_profile' => 'Acc-Service-Profile',
'ascend_bir_proxy' => 'Ascend-BIR-Proxy',
+ 'aat_ppp_address' => 'AAT-PPP-Address',
+ 'usr_mbi_ct_pri_card_span' => 'USR-Mbi_Ct_PRI_Card_Span_Line',
'ascend_x25_nui_prompt' => 'Ascend-X25-Nui-Prompt',
- 'usr_rmmie_pwrlvl_noise_l' => 'USR-RMMIE-PwrLvl-Noise-Lvl',
- 'usr_rmmie_pwrlvl_nearech' => 'USR-RMMIE-PwrLvl-NearEcho-Canc',
- 'x_ascend_multicast_clien' => 'X-Ascend-Multicast-Client',
+ 'itk_modem_pool_id' => 'ITK-Modem-Pool-Id',
+ 'usr_compression_reset_mo' => 'USR-Compression-Reset-Mode',
'usr_unauthenticated_time' => 'USR-Unauthenticated-Time',
+ 'ascend_multicast_gleave_' => 'Ascend-Multicast-GLeave-Delay',
'acc_callback_cbcp_type' => 'Acc-Callback-CBCP-Type',
+ 'medium_typf' => 'Medium_Type',
'login_service' => 'Login-Service',
- 'usr_rad_multicast_routin' => 'USR-Rad-Multicast-Routing-Bound',
+ 'itk_username_prompt' => 'ITK-Username-Prompt',
'ascend_dial_number' => 'Ascend-Dial-Number',
+ 'framed_ipv6_route' => 'Framed-IPv6-Route',
'x_ascend_remote_addr' => 'X-Ascend-Remote-Addr',
- 'usr_rmmie_rcv_pwrlvl_330' => 'USR-RMMIE-Rcv-PwrLvl-3300Hz',
'usr_call_end_date_time' => 'USR-Call-End-Date-Time',
- 'bind_dot1q_slot' => 'Bind_Dot1q_Slot',
+ 'bind_dot1q_slot' => 'Bind-Dot1q-Slot',
'le_connect_detail' => 'LE-Connect-Detail',
'annex_user_level' => 'Annex-User-Level',
- 'tunnel_dnis' => 'Tunnel_DNIS',
- 'assigned_ip_address' => 'Assigned_IP_Address',
+ 'tunnel_dnis' => 'Tunnel-DNIS',
+ 'assigned_ip_address' => 'Assigned-IP-Address',
'acc_bridging_support' => 'Acc-Bridging-Support',
'usr_channel' => 'USR-Channel',
'arap_security_data' => 'ARAP-Security-Data',
- 'bind_auth_service_grp' => 'Bind_Auth_Service_Grp',
- 'x_ascend_pre_output_pack' => 'X-Ascend-Pre-Output-Packets',
- 'x_ascend_seconds_of_hist' => 'X-Ascend-Seconds-Of-History',
+ 'bind_auth_service_grp' => 'Bind-Auth-Service-Grp',
+ 'cisco_abort_cause' => 'Cisco-Abort-Cause',
+ 'bg_span_dit' => 'BG_Span_Dis',
'h323_voice_quality' => 'h323-voice-quality',
- 'usr_rmmie_last_update_ti' => 'USR-RMMIE-Last-Update-Time',
- 'usr_disconnect_cause_ind' => 'USR-Disconnect-Cause-Indicator',
+ 'lac_real_port_typf' => 'LAC_Real_Port_Type',
'usr_channel_connected_to' => 'USR-Channel-Connected-To',
- 'ascend_calling_id_number' => 'Ascend-Calling-Id-Number-Plan',
+ 'ascend_client_assign_win' => 'Ascend-Client-Assign-WINS',
+ 'redcreek_tunneled_gatewa' => 'RedCreek-Tunneled-Gateway',
'usr_number_of_fallbacks' => 'USR-Number-of-Fallbacks',
- 'usr_ip_call_output_filte' => 'USR-IP-Call-Output-Filter',
+ 'nokia_prepaid_ind' => 'Nokia-Prepaid-Ind',
+ 'nomadix_maxbytesup' => 'Nomadix-MaxBytesUp',
+ 'login_hosu' => 'Login-Host',
'ascend_bir_enable' => 'Ascend-BIR-Enable',
'usr_connect_time_limit' => 'USR-Connect-Time-Limit',
'ascend_presession_time' => 'Ascend-PreSession-Time',
- 'ascend_private_route_req' => 'Ascend-Private-Route-Required',
- 'ascend_dsl_cir_xmit_limi' => 'Ascend-Dsl-CIR-Xmit-Limit',
+ 'altiga_simultaneous_logi' => 'Altiga-Simultaneous-Logins-G/U',
+ 'cvpn3000_ipsec_default_d' => 'CVPN3000-IPSec-Default-Domain',
+ 'aat_atm_vci' => 'AAT-ATM-VCI',
+ 'extreme_netlogin_url_des' => 'Extreme-Netlogin-Url-Desc',
+ 'itk_auth_serv_ip' => 'ITK-Auth-Serv-IP',
+ 'erx_alternate_cli_vroute' => 'ERX-Alternate-Cli-Vrouter-Name',
'framed_compression' => 'Framed-Compression',
'ascend_svc_enabled' => 'Ascend-SVC-Enabled',
'proxy_state' => 'Proxy-State',
- 'ascend_tunnel_vrouter_na' => 'Ascend-Tunnel-VRouter-Name',
- 'usr_ipx_call_input_filte' => 'USR-IPX-Call-Input-Filter',
- 'x_ascend_assign_ip_globa' => 'X-Ascend-Assign-IP-Global-Pool',
- 'erx_alternate_cli_vroute' => 'ERX-Alternate-Cli-Vrouter-Name',
- 'ascend_dhcp_maximum_leas' => 'Ascend-DHCP-Maximum-Leases',
+ 'aat_vrouter_name' => 'AAT-Vrouter-Name',
+ 'usr_rmmie_pwrlvl_farecho' => 'USR-RMMIE-PwrLvl-FarEcho-Canc',
+ 'nas_poru' => 'NAS-Port',
+ 'wispr_location_name' => 'WISPr-Location-Name',
+ 'digest_user_name' => 'Digest-User-Name',
'ascend_modem_shelfno' => 'Ascend-Modem-ShelfNo',
- 'bind_auth_protocol' => 'Bind_Auth_Protocol',
'shasta_user_privilege' => 'Shasta-User-Privilege',
+ 'bind_auth_protocol' => 'Bind-Auth-Protocol',
+ 'ascend_home_agent_passwo' => 'Ascend-Home-Agent-Password',
'acct_interim_interval' => 'Acct-Interim-Interval',
+ 'ascend_history_weigh_typ' => 'Ascend-History-Weigh-Type',
+ 'ms_link_drop_time_limit' => 'MS-Link-Drop-Time-Limit',
'hint' => 'Hint',
'x_ascend_target_util' => 'X-Ascend-Target-Util',
- 'ms_link_drop_time_limit' => 'MS-Link-Drop-Time-Limit',
'acc_access_partition' => 'Acc-Access-Partition',
- 'x_ascend_multilink_id' => 'X-Ascend-Multilink-ID',
'usr_power_supply_number' => 'USR-Power-Supply-Number',
- 'acc_ipx_compression' => 'Acc-Ipx-Compression',
+ 'x_ascend_multilink_id' => 'X-Ascend-Multilink-ID',
+ 'redcreek_tunneled_domain' => 'RedCreek-Tunneled-DomainName',
'nomadix_bw_down' => 'Nomadix-Bw-Down',
- 'usr_call_reference_numbe' => 'USR-Call-Reference-Number',
+ 'acc_ipx_compression' => 'Acc-Ipx-Compression',
+ 'quintum_h323_setup_time' => 'Quintum-h323-setup-time',
'cisco_target_util' => 'Cisco-Target-Util',
- 'usr_back_channel_data_ra' => 'USR-Back-Channel-Data-Rate',
'acc_ip_gateway_sec' => 'Acc-Ip-Gateway-Sec',
- 'usr_dte_ring_no_answer_l' => 'USR-DTE-Ring-No-Answer-Limit',
- 'usr_connect_time' => 'USR-Connect-Time',
+ 'ascend_dsl_cir_xmit_limi' => 'Ascend-Dsl-CIR-Xmit-Limit',
'ascend_ip_pool_definitio' => 'Ascend-IP-Pool-Definition',
+ 'bind_sub_user_at_contexu' => 'Bind_Sub_User_At_Context',
+ 'itk_dest_no' => 'ITK-Dest-No',
+ 'usr_connect_time' => 'USR-Connect-Time',
'usr_call_start_date_time' => 'USR-Call-Start-Date-Time',
+ 'altiga_l2tp_encryption_g' => 'Altiga-L2TP-Encryption-G',
+ 'ascend_auth_delay' => 'Ascend-Auth-Delay',
+ 'ascend_x25_pad_x3_profil' => 'Ascend-X25-Pad-X3-Profile',
+ 'ascend_access_intercepta' => 'Ascend-Access-Intercept-Log',
+ 'ascend_home_agent_udp_po' => 'Ascend-Home-Agent-UDP-Port',
+ 'bind_tun_context' => 'Bind-Tun-Context',
'dialback_name' => 'Dialback-Name',
- 'bind_tun_context' => 'Bind_Tun_Context',
'h323_redirect_ip_address' => 'h323-redirect-ip-address',
'annex_keypress_timeout' => 'Annex-Keypress-Timeout',
+ 'x_ascend_home_network_na' => 'X-Ascend-Home-Network-Name',
'ascend_x25_pad_alias_1' => 'Ascend-X25-Pad-Alias-1',
+ 'ascend_call_attempt_limi' => 'Ascend-Call-Attempt-Limit',
+ 'quintum_h323_currency_ty' => 'Quintum-h323-currency-type',
'ms_chap_response' => 'MS-CHAP-Response',
+ 'st_secondary_nbns_server' => 'ST-Secondary-NBNS-Server',
+ 'x_ascend_history_weigh_t' => 'X-Ascend-History-Weigh-Type',
'usr_max_channels' => 'USR-Max-Channels',
'ascend_fr_dte_n393' => 'Ascend-FR-DTE-N393',
'ascend_pre_input_octets' => 'Ascend-Pre-Input-Octets',
'erx_atm_mbs' => 'ERX-Atm-MBS',
+ 'cvpn3000_simultaneous_lo' => 'CVPN3000-Simultaneous-Logins',
+ 'juniper_allow_commands' => 'Juniper-Allow-Commands',
'usr_line_reversals' => 'USR-Line-Reversals',
+ 'itk_users_default_pw' => 'ITK-Users-Default-Pw',
'x_ascend_third_prompt' => 'X-Ascend-Third-Prompt',
+ 'cisco_fax_msg_id' => 'Cisco-Fax-Msg-Id',
'x_ascend_pw_warntime' => 'X-Ascend-PW-Warntime',
'ascend_data_filter' => 'Ascend-Data-Filter',
'framed_address' => 'Framed-Address',
'context_name' => 'Context-Name',
'usr_send_script2' => 'USR-Send-Script2',
'ms_arap_pw_change_reason' => 'MS-ARAP-PW-Change-Reason',
+ 'tunnel_session_auth_cty' => 'Tunnel_Session_Auth_Ctx',
'acct_session_id' => 'Acct-Session-Id',
+ 'annex_port' => 'Annex-Port',
+ 'quintum_h323_call_origin' => 'Quintum-h323-call-origin',
+ 'erx_cli_initial_access_l' => 'ERX-Cli-Initial-Access-Level',
+ 'x_ascend_shared_profile_' => 'X-Ascend-Shared-Profile-Enable',
+ 'tunnel_cmd_timeouu' => 'Tunnel_Cmd_Timeout',
'initial_modulation_type' => 'Initial-Modulation-Type',
'ascend_h323_gatekeeper' => 'Ascend-H323-Gatekeeper',
'x_ascend_fcp_parameter' => 'X-Ascend-FCP-Parameter',
- 'tunnel_type' => 'Tunnel-Type',
'multi_link_flag' => 'Multi-Link-Flag',
+ 'tunnel_type' => 'Tunnel-Type',
+ 'erx_output_gigapkts' => 'ERX-Output-Gigapkts',
'ascend_idle_limit' => 'Ascend-Idle-Limit',
+ 'ns_user_group' => 'NS-User-Group',
'password_retry' => 'Password-Retry',
'h323_remote_address' => 'h323-remote-address',
'erx_atm_service_category' => 'ERX-Atm-Service-Category',
'acct_input_packets' => 'Acct-Input-Packets',
'h323_disconnect_time' => 'h323-disconnect-time',
- 'ascend_billing_number' => 'Ascend-Billing-Number',
'usr_syslog_tap' => 'USR-Syslog-Tap',
+ 'telebit_accounting_info' => 'Telebit-Accounting-Info',
+ 'ascend_billing_number' => 'Ascend-Billing-Number',
+ 'ascend_tunnel_vrouter_na' => 'Ascend-Tunnel-VRouter-Name',
'ms_mppe_encryption_type' => 'MS-MPPE-Encryption-Type',
+ 'quintum_h323_credit_amou' => 'Quintum-h323-credit-amount',
+ 'acc_ace_token' => 'Acc-Ace-Token',
'ascend_assign_ip_pool' => 'Ascend-Assign-IP-Pool',
+ 'annex_end_modulation' => 'Annex-End-Modulation',
'usr_routing_protocol' => 'USR-Routing-Protocol',
+ 'cvx_assign_ip_pool' => 'CVX-Assign-IP-Pool',
'usr_rad_location_type' => 'USR-Rad-Location-Type',
+ 'usr_rmmie_pwrlvl_noise_l' => 'USR-RMMIE-PwrLvl-Noise-Lvl',
'usr_characters_sent' => 'USR-Characters-Sent',
'usr_mp_edo_hiper' => 'USR-MP-EDO-HIPER',
+ 'ascend_x25_nui_password_' => 'Ascend-X25-Nui-Password-Prompt',
'annex_host_restrict' => 'Annex-Host-Restrict',
'user_service_type' => 'User-Service-Type',
'acct_multi_session_id' => 'Acct-Multi-Session-Id',
'ms_chap_cpw_2' => 'MS-CHAP-CPW-2',
- 'x_ascend_primary_home_ag' => 'X-Ascend-Primary-Home-Agent',
+ 'x_ascend_secondary_home_' => 'X-Ascend-Secondary-Home-Agent',
'x_ascend_dialout_allowed' => 'X-Ascend-Dialout-Allowed',
'ascend_connect_progress' => 'Ascend-Connect-Progress',
'x_ascend_ara_pw' => 'X-Ascend-Ara-PW',
+ 'cisco_fax_modem_time' => 'Cisco-Fax-Modem-Time',
+ 'sql_group' => 'Sql-Group',
+ 'annex_multicast_rate_lim' => 'Annex-Multicast-Rate-Limit',
+ 'cvpn3000_user_auth_servg' => 'CVPN3000-User-Auth-Server-Secret',
'ns_mta_md5_password' => 'NS-MTA-MD5-Password',
+ 'annex_addr_resolution_pr' => 'Annex-Addr-Resolution-Protocol',
'callback_number' => 'Callback-Number',
- 'acct_output_packets_64' => 'Acct_Output_Packets_64',
+ 'cvx_multilink_match_info' => 'CVX-Multilink-Match-Info',
+ 'tunnel_max_tunnelt' => 'Tunnel_Max_Tunnels',
+ 'tunnel_local_namf' => 'Tunnel_Local_Name',
+ 'quintum_h323_conf_id' => 'Quintum-h323-conf-id',
+ 'acct_output_packets_64' => 'Acct-Output-Packets-64',
+ 'annex_signal_to_noise_ra' => 'Annex-Signal-to-Noise-Ratio',
+ 'acct_output_packets_65' => 'Acct_Output_Packets_64',
'x_ascend_user_acct_key' => 'X-Ascend-User-Acct-Key',
+ 'erx_dial_out_number' => 'ERX-Dial-Out-Number',
'ascend_modem_portno' => 'Ascend-Modem-PortNo',
'ascend_assign_ip_server' => 'Ascend-Assign-IP-Server',
'ascend_fcp_parameter' => 'Ascend-FCP-Parameter',
- 'ascend_inter_arrival_jit' => 'Ascend-Inter-Arrival-Jitter',
+ 'usr_chassis_temp_thresho' => 'USR-Chassis-Temp-Threshold',
+ 'usr_mpip_tunnel_originat' => 'USR-MPIP-Tunnel-Originator',
+ 'tunnel_rate_limit_bursu' => 'Tunnel_Rate_Limit_Burst',
'client_ip_address' => 'Client-IP-Address',
- 'usr_number_of_link_timeo' => 'USR-Number-of-Link-Timeouts',
- 'ascend_dsl_cir_recv_limi' => 'Ascend-Dsl-CIR-Recv-Limit',
+ 'le_nat_tcp_session_timeo' => 'LE-NAT-TCP-Session-Timeout',
+ 'quintum_h323_redirect_ip' => 'Quintum-h323-redirect-ip-address',
'ms_acct_eap_type' => 'MS-Acct-EAP-Type',
- 'x_ascend_user_acct_type' => 'X-Ascend-User-Acct-Type',
'usr_rmmie_x2_status' => 'USR-RMMIE-x2-Status',
- 'ascend_dsl_downstream_li' => 'Ascend-Dsl-Downstream-Limit',
+ 'x_ascend_user_acct_type' => 'X-Ascend-User-Acct-Type',
'shiva_customer_id' => 'Shiva-Customer-Id',
- 'lac_real_port' => 'LAC_Real_Port',
+ 'pvc_encapsulation_typf' => 'PVC_Encapsulation_Type',
+ 'st_acct_vc_connection_id' => 'ST-Acct-VC-Connection-Id',
+ 'lac_real_port' => 'LAC-Real-Port',
'h323_connect_time' => 'h323-connect-time',
- 'old_password' => 'Old-Password',
'usr_vpn_gw_location_id' => 'USR-VPN-GW-Location-Id',
+ 'old_password' => 'Old-Password',
'x_ascend_if_netmask' => 'X-Ascend-IF-Netmask',
'add_suffix' => 'Add-Suffix',
- 'x_ascend_client_assign_d' => 'X-Ascend-Client-Assign-DNS',
- 'usr_q931_call_reference_' => 'USR-Q931-Call-Reference-Value',
+ 'lac_port_typf' => 'LAC_Port_Type',
+ 'acc_ip_pool_name' => 'Acc-Ip-Pool-Name',
'usr_terminal_type' => 'USR-Terminal-Type',
'usr_spoofing' => 'USR-Spoofing',
'erx_tunnel_password' => 'ERX-Tunnel-Password',
- 'ascend_assign_ip_client' => 'Ascend-Assign-IP-Client',
+ 'ascend_inter_arrival_jit' => 'Ascend-Inter-Arrival-Jitter',
+ 'ascend_call_block_durati' => 'Ascend-Call-Block-Duration',
+ 'itk_channel_binding' => 'ITK-Channel-Binding',
'usr_server_time' => 'USR-Server-Time',
+ 'ascend_assign_ip_client' => 'Ascend-Assign-IP-Client',
+ 'erx_pppoe_max_sessions' => 'ERX-Pppoe-Max-Sessions',
+ 'cvx_multilink_group_numb' => 'CVX-Multilink-Group-Number',
+ 'x_ascend_client_assign_d' => 'X-Ascend-Client-Assign-DNS',
+ 'erx_pppoe_url' => 'ERX-Pppoe-Url',
+ 'police_ratf' => 'Police_Rate',
'ascend_data_svc' => 'Ascend-Data-Svc',
'annex_authen_servers' => 'Annex-Authen-Servers',
'nomadix_bw_up' => 'Nomadix-Bw-Up',
+ 'cvx_modem_data_compressi' => 'CVX-Modem-Data-Compression',
'shiva_link_speed' => 'Shiva-Link-Speed',
'usr_reply_script6' => 'USR-Reply-Script6',
'usr_expansion_algorithm' => 'USR-Expansion-Algorithm',
- 'x_ascend_mpp_idle_percen' => 'X-Ascend-MPP-Idle-Percent',
+ 'cabletron_protocol_calla' => 'Cabletron-Protocol-Callable',
'cisco_data_rate' => 'Cisco-Data-Rate',
'usr_primary_dns_server' => 'USR-Primary_DNS_Server',
- 'erx_local_loopback_inter' => 'ERX-Local-Loopback-Interface',
+ 'juniper_deny_configurati' => 'Juniper-Deny-Configuration',
'ascend_target_util' => 'Ascend-Target-Util',
- 'usr_default_dte_data_rat' => 'USR-Default-DTE-Data-Rate',
+ 'digest_method' => 'Digest-Method',
+ 'altiga_ipsec_split_tunne' => 'Altiga-IPSec-Split-Tunnel-List-G',
+ 'erx_alternate_cli_access' => 'ERX-Alternate-Cli-Access-Level',
'x_ascend_event_type' => 'X-Ascend-Event-Type',
+ 'usr_q931_call_reference_' => 'USR-Q931-Call-Reference-Value',
'usr_mp_mrru' => 'USR-MP-MRRU',
- 'bind_bypass_context' => 'Bind_Bypass_Context',
+ 'cvx_ipsvc_mask' => 'CVX-IPSVC-Mask',
+ 'bind_bypass_context' => 'Bind-Bypass-Context',
+ 'usr_rmmie_last_update_ev' => 'USR-RMMIE-Last-Update-Event',
'no_such_attribute' => 'No-Such-Attribute',
- 'acct_mcast_out_packets' => 'Acct_Mcast_Out_Packets',
+ 'acct_mcast_out_packets' => 'Acct-Mcast-Out-Packets',
'tunnel_medium_type' => 'Tunnel-Medium-Type',
+ 'quintum_h323_remote_addr' => 'Quintum-h323-remote-address',
'acc_callback_delay' => 'Acc-Callback-Delay',
- 'x_ascend_home_agent_udp_' => 'X-Ascend-Home-Agent-UDP-Port',
- 'acct_input_octets_64' => 'Acct_Input_Octets_64',
+ 'acct_input_octets_64' => 'Acct-Input-Octets-64',
+ 'ascend_base_channel_coun' => 'Ascend-Base-Channel-Count',
'ascend_atm_connect_vci' => 'Ascend-ATM-Connect-Vci',
'erx_primary_dns' => 'ERX-Primary-Dns',
+ 'altiga_ipsec_over_nat_g' => 'Altiga-IPSec-Over-NAT-G',
+ 'cvx_multicast_rate_limit' => 'CVX-Multicast-Rate-Limit',
'ascend_xmit_rate' => 'Ascend-Xmit-Rate',
'ms_new_arap_password' => 'MS-New-ARAP-Password',
'usr_call_error_code' => 'USR-Call-Error-Code',
'acct_output_octets' => 'Acct-Output-Octets',
- 'usr_failure_to_connect_r' => 'USR-Failure-to-Connect-Reason',
+ 'ascend_client_primary_wi' => 'Ascend-Client-Primary-WINS',
+ 'cvpn3000_primary_wins' => 'CVPN3000-Primary-WINS',
+ 'bintec_ipextrttable' => 'BinTec-ipExtRtTable',
+ 'cisco_fax_mdn_flag' => 'Cisco-Fax-Mdn-Flag',
+ 'ascend_destination_nas_p' => 'Ascend-Destination-Nas-Port',
'ascend_num_in_multilink' => 'Ascend-Num-In-Multilink',
+ 'digest_attributes' => 'Digest-Attributes',
+ 'cvpn3000_ipsec_tunnel_ty' => 'CVPN3000-IPSec-Tunnel-Type',
'x_ascend_number_sessions' => 'X-Ascend-Number-Sessions',
'usr_ip_rip_output_filter' => 'USR-IP-RIP-Output-Filter',
- 'usr_chassis_temp_thresho' => 'USR-Chassis-Temp-Threshold',
+ 'tunnel_police_bursu' => 'Tunnel_Police_Burst',
+ 'redcreek_tunneled_wins_s' => 'RedCreek-Tunneled-WINS-Server1',
'usr_blocks_sent' => 'USR-Blocks-Sent',
+ 'erx_cli_allow_all_vr_acc' => 'ERX-Cli-Allow-All-VR-Access',
+ 'tunnel_police_ratf' => 'Tunnel_Police_Rate',
'usr_ids0_call_type' => 'USR-IDS0-Call-Type',
'acc_ccp_option' => 'Acc-Ccp-Option',
'ascend_client_gateway' => 'Ascend-Client-Gateway',
- 'x_ascend_multicast_rate_' => 'X-Ascend-Multicast-Rate-Limit',
+ 'cvx_maximum_channels' => 'CVX-Maximum-Channels',
+ 'bg_aging_timf' => 'BG_Aging_Time',
+ 'annex_secondary_dns_serv' => 'Annex-Secondary-DNS-Server',
'le_ipsec_passive_profile' => 'LE-IPSec-Passive-Profile',
'usr_chassis_call_span' => 'USR-Chassis-Call-Span',
- 'usr_mobileip_home_agent_' => 'USR-MobileIP-Home-Agent-Address',
+ 'aat_client_primary_wins_' => 'AAT-Client-Primary-WINS-NBNS',
+ 'h323_currency' => 'h323-currency',
'password' => 'Password',
'le_nat_log_options' => 'LE-NAT-Log-Options',
- 'x_ascend_ppp_address' => 'X-Ascend-PPP-Address',
'usr_fallback_limit' => 'USR-Fallback-Limit',
+ 'x_ascend_ppp_address' => 'X-Ascend-PPP-Address',
'suffix' => 'Suffix',
'usr_multicast_receive' => 'USR-Multicast-Receive',
- 'client_dns_sec' => 'Client_DNS_Sec',
+ 'client_dns_sec' => 'Client-DNS-Sec',
'annex_product_name' => 'Annex-Product-Name',
'cisco_pw_lifetime' => 'Cisco-PW-Lifetime',
'x_ascend_fr_dce_n393' => 'X-Ascend-FR-DCE-N393',
'x_ascend_ts_idle_limit' => 'X-Ascend-TS-Idle-Limit',
- 'usr_last_number_dialed_o' => 'USR-Last-Number-Dialed-Out',
- 'mcast_send' => 'Mcast_Send',
- 'pppoe_motm' => 'PPPOE_MOTM',
+ 'mcast_send' => 'Mcast-Send',
+ 'x_ascend_primary_home_ag' => 'X-Ascend-Primary-Home-Agent',
+ 'tunnel_max_sessiont' => 'Tunnel_Max_Sessions',
+ 'pppoe_motm' => 'PPPOE-MOTM',
'usr_pw_usr_ifilter_ipx' => 'USR-PW_USR_IFilter_IPX',
- 'usr_pw_tunnel_authentica' => 'USR-PW_Tunnel_Authentication',
- 'ascend_source_ip_check' => 'Ascend-Source-IP-Check',
- 'ascend_assign_ip_global_' => 'Ascend-Assign-IP-Global-Pool',
'ms_ras_version' => 'MS-RAS-Version',
- 'usr_rad_multicast_routin' => 'USR-Rad-Multicast-Routing-Ttl',
- 'x_ascend_modem_slotno' => 'X-Ascend-Modem-SlotNo',
+ 'ascend_source_ip_check' => 'Ascend-Source-IP-Check',
+ 'bintec_ospfiftable' => 'BinTec-ospfIfTable',
'acc_ml_call_threshold' => 'Acc-ML-Call-Threshold',
+ 'x_ascend_modem_slotno' => 'X-Ascend-Modem-SlotNo',
'ascend_menu_item' => 'Ascend-Menu-Item',
- 'usr_cdma_call_reference_' => 'USR-CDMA-Call-Reference-Number',
'callback_id' => 'Callback-Id',
'framed_ipx_network' => 'Framed-IPX-Network',
- 'x_ascend_disconnect_caus' => 'X-Ascend-Disconnect-Cause',
+ 'altiga_pptp_encryption_g' => 'Altiga-PPTP-Encryption-G',
+ 'ascend_x25_reverse_charg' => 'Ascend-X25-Reverse-Charging',
'ascend_user_acct_key' => 'Ascend-User-Acct-Key',
'x_ascend_pw_lifetime' => 'X-Ascend-PW-Lifetime',
'user_name_is_star' => 'User-Name-Is-Star',
- 'x_ascend_authen_alias' => 'X-Ascend-Authen-Alias',
+ 'nomadix_url_redirection' => 'Nomadix-URL-Redirection',
'framed_pool' => 'Framed-Pool',
+ 'x_ascend_authen_alias' => 'X-Ascend-Authen-Alias',
+ 'cisco_fax_dsn_address' => 'Cisco-Fax-Dsn-Address',
'ms_primary_dns_server' => 'MS-Primary-DNS-Server',
+ 'acc_dialout_auth_usernam' => 'Acc-Dialout-Auth-Username',
'realm' => 'Realm',
'arap_features' => 'ARAP-Features',
+ 'bind_auth_protocom' => 'Bind_Auth_Protocol',
'acc_connect_tx_speed' => 'Acc-Connect-Tx-Speed',
- 'usr_last_number_dialed_i' => 'USR-Last-Number-Dialed-In-DNIS',
'usr_chassis_temperature' => 'USR-Chassis-Temperature',
+ 'altiga_ipsec_mode_config' => 'Altiga-IPSec-Mode-Config-G',
+ 'ascend_home_agent_ip_add' => 'Ascend-Home-Agent-IP-Addr',
'x_ascend_xmit_rate' => 'X-Ascend-Xmit-Rate',
+ 'cvpn3000_secondary_dns' => 'CVPN3000-Secondary-DNS',
'x_ascend_send_passwd' => 'X-Ascend-Send-Passwd',
+ 'bind_int_contexu' => 'Bind_Int_Context',
+ 'cisco_fax_account_id_ori' => 'Cisco-Fax-Account-Id-Origin',
'le_modem_info' => 'LE-Modem-Info',
'ascend_ipx_peer_mode' => 'Ascend-IPX-Peer-Mode',
- 'le_nat_other_session_tim' => 'LE-NAT-Other-Session-Timeout',
- 'tunnel_rate_limit_rate' => 'Tunnel_Rate_Limit_Rate',
- 'ascend_maximum_call_dura' => 'Ascend-Maximum-Call-Duration',
+ 'juniper_local_user_name' => 'Juniper-Local-User-Name',
+ 'tunnel_rate_limit_rate' => 'Tunnel-Rate-Limit-Rate',
+ 'quintum_h323_credit_time' => 'Quintum-h323-credit-time',
+ 'acc_modem_modulation_typ' => 'Acc-Modem-Modulation-Type',
+ 'x_ascend_seconds_of_hist' => 'X-Ascend-Seconds-Of-History',
'ascend_dhcp_pool_number' => 'Ascend-DHCP-Pool-Number',
+ 'redcreek_tunneled_ip_net' => 'RedCreek-Tunneled-IP-Netmask',
'x_ascend_callback' => 'X-Ascend-Callback',
- 'ascend_access_intercept_' => 'Ascend-Access-Intercept-Log',
'usr_iwf_ip_address' => 'USR-IWF-IP-Address',
+ 'aat_input_octets_diff' => 'AAT-Input-Octets-Diff',
'nas_port_id' => 'NAS-Port-Id',
'le_advice_of_charge' => 'LE-Advice-of-Charge',
+ 'x_ascend_dhcp_pool_numbe' => 'X-Ascend-DHCP-Pool-Number',
'ascend_add_seconds' => 'Ascend-Add-Seconds',
'annex_transmit_speed' => 'Annex-Transmit-Speed',
'usr_port_tap' => 'USR-Port-Tap',
'usr_at_call_input_filter' => 'USR-AT-Call-Input-Filter',
+ 'framed_ipv6_pool' => 'Framed-IPv6-Pool',
'ascend_qos_downstream' => 'Ascend-QOS-Downstream',
- 'ascend_x25_reverse_charg' => 'Ascend-X25-Reverse-Charging',
- 'lac_port' => 'LAC_Port',
+ 'lac_port' => 'LAC-Port',
'tunnel_assignment_id' => 'Tunnel-Assignment-Id',
+ 'acct_mcast_out_octett' => 'Acct_Mcast_Out_Octets',
+ 'ascend_bi_directional_au' => 'Ascend-Bi-Directional-Auth',
'fall_through' => 'Fall-Through',
+ 'cvpn3000_ipsec_ip_compre' => 'CVPN3000-IPSec-IP-Compression',
'cisco_disconnect_cause' => 'Cisco-Disconnect-Cause',
- 'module_message' => 'Module-Message',
+ 'usr_rad_multicast_routiq' => 'USR-Rad-Multicast-Routing-Bound',
+ 'altiga_tunneling_protoco' => 'Altiga-Tunneling-Protocols-G/U',
+ 'itk_tunnel_prot' => 'ITK-Tunnel-Prot',
+ 'client_dns_sed' => 'Client_DNS_Sec',
'framed_ip_netmask' => 'Framed-IP-Netmask',
+ 'usr_call_reference_numbe' => 'USR-Call-Reference-Number',
'ascend_egress_enabled' => 'Ascend-Egress-Enabled',
'ascend_dsl_rate_mode' => 'Ascend-Dsl-Rate-Mode',
- 'x_ascend_client_primary_' => 'X-Ascend-Client-Primary-DNS',
'usr_pw_usr_ofilter_sap' => 'USR-PW_USR_OFilter_SAP',
+ 'bintec_iproutetable' => 'BinTec-ipRouteTable',
'acct_terminate_cause' => 'Acct-Terminate-Cause',
'x_ascend_fr_dte_n393' => 'X-Ascend-FR-DTE-N393',
- 'x_ascend_call_block_dura' => 'X-Ascend-Call-Block-Duration',
'ascend_ppp_address' => 'Ascend-PPP-Address',
+ 'erx_maximum_bps' => 'ERX-Maximum-BPS',
'caller_id' => 'Caller-ID',
- 'bind_int_interface_name' => 'Bind_Int_Interface_Name',
- 'x_ascend_ppp_vj_slot_com' => 'X-Ascend-PPP-VJ-Slot-Comp',
+ 'bintec_ipfiltertable' => 'BinTec-ipFilterTable',
+ 'x_ascend_base_channel_co' => 'X-Ascend-Base-Channel-Count',
+ 'bind_int_interface_name' => 'Bind-Int-Interface-Name',
'usr_modem_group' => 'USR-Modem-Group',
'cisco_maximum_channels' => 'Cisco-Maximum-Channels',
+ 'erx_ppp_username' => 'ERX-PPP-Username',
'ascend_link_compression' => 'Ascend-Link-Compression',
+ 'annex_retransmitted_pack' => 'Annex-Retransmitted-Packets',
'usr_retrains_granted' => 'USR-Retrains-Granted',
'ascend_dropped_packets' => 'Ascend-Dropped-Packets',
+ 'erx_bearer_type' => 'ERX-Bearer-Type',
'usr_pw_usr_ofilter_ip' => 'USR-PW_USR_OFilter_IP',
'quintum_nas_port' => 'Quintum-NAS-Port',
+ 'x_ascend_pre_output_pack' => 'X-Ascend-Pre-Output-Packets',
+ 'usr_cdma_call_reference_' => 'USR-CDMA-Call-Reference-Number',
+ 'tunnel_function' => 'Tunnel-Function',
'annex_tunnel_authen_mode' => 'Annex-Tunnel-Authen-Mode',
- 'tunnel_function' => 'Tunnel_Function',
'usr_mp_edo' => 'USR-MP-EDO',
'le_nat_outmap' => 'LE-NAT-Outmap',
+ 'cvpn3000_primary_dns' => 'CVPN3000-Primary-DNS',
'usr_modulation_type' => 'USR-Modulation-Type',
+ 'ascend_calling_id_screen' => 'Ascend-Calling-Id-Screening',
'ascend_maximum_time' => 'Ascend-Maximum-Time',
+ 'user_password' => 'User-Password',
'annex_callback_portlist' => 'Annex-Callback-Portlist',
- 'x_ascend_remove_seconds' => 'X-Ascend-Remove-Seconds',
+ 'cvpn3000_ipsec_split_tun' => 'CVPN3000-IPSec-Split-Tunnel-List',
+ 'annex_pre_output_packets' => 'Annex-Pre-Output-Packets',
+ 'usr_at_call_output_filte' => 'USR-AT-Call-Output-Filter',
+ 'x_ascend_client_primary_' => 'X-Ascend-Client-Primary-DNS',
'tunnel_server_endpoint' => 'Tunnel-Server-Endpoint',
+ 'x_ascend_remove_seconds' => 'X-Ascend-Remove-Seconds',
+ 'cvpn3000_user_auth_serve' => 'CVPN3000-User-Auth-Server-Name',
'arap_password' => 'ARAP-Password',
+ 'x_ascend_assign_ip_serve' => 'X-Ascend-Assign-IP-Server',
+ 'cisco_fax_pages' => 'Cisco-Fax-Pages',
'ms_chap_mppe_keys' => 'MS-CHAP-MPPE-Keys',
'ascend_source_auth' => 'Ascend-Source-Auth',
'group' => 'Group',
'usr_send_script6' => 'USR-Send-Script6',
'le_nat_inmap' => 'LE-NAT-Inmap',
'chap_password' => 'CHAP-Password',
- 'annex_primary_nbns_serve' => 'Annex-Primary-NBNS-Server',
'annex_receive_speed' => 'Annex-Receive-Speed',
- 'usr_rmmie_manufacturer_i' => 'USR-RMMIE-Manufacturer-ID',
- 'bind_l2tp_flow_control' => 'Bind_L2TP_Flow_Control',
+ 'usr_mobileip_home_agent_' => 'USR-MobileIP-Home-Agent-Address',
+ 'bind_l2tp_flow_control' => 'Bind-L2TP-Flow-Control',
'smb_account_ctrl' => 'SMB-Account-CTRL',
- 'ascend_calling_id_presen' => 'Ascend-Calling-Id-Presentatn',
'ascend_ip_pool_chaining' => 'Ascend-IP-Pool-Chaining',
'le_admin_group' => 'LE-Admin-Group',
- 'nas_identifier' => 'NAS-Identifier',
- 'x_ascend_history_weigh_t' => 'X-Ascend-History-Weigh-Type',
'tunnel_connection_id' => 'Tunnel-Connection-Id',
- 'nas_real_port' => 'NAS_Real_Port',
+ 'tunnel_windox' => 'Tunnel_Window',
+ 'nas_identifier' => 'NAS-Identifier',
+ 'dhcp_max_leaset' => 'DHCP_Max_Leases',
+ 'digest_nonce_count' => 'Digest-Nonce-Count',
+ 'nas_real_port' => 'NAS-Real-Port',
'ms_old_arap_password' => 'MS-Old-ARAP-Password',
- 'usr_ip_rip_simple_auth_p' => 'USR-IP-RIP-Simple-Auth-Password',
- 'erx_primary_wins' => 'ERX-Primary-Wins',
'usr_pw_index' => 'USR-PW_Index',
- 'erx_cli_allow_all_vr_acc' => 'ERX-Cli-Allow-All-VR-Access',
+ 'erx_primary_wins' => 'ERX-Primary-Wins',
+ 'ascend_appletalk_peer_mo' => 'Ascend-Appletalk-Peer-Mode',
'le_ipsec_log_options' => 'LE-IPSec-Log-Options',
- 'ascend_home_agent_ip_add' => 'Ascend-Home-Agent-IP-Addr',
+ 'x_ascend_maximum_channel' => 'X-Ascend-Maximum-Channels',
+ 'cvx_ipsvc_aznlvl' => 'CVX-IPSVC-AZNLVL',
+ 'x_ascend_client_secondar' => 'X-Ascend-Client-Secondary-DNS',
'annex_re_chap_timeout' => 'Annex-Re-CHAP-Timeout',
- 'usr_final_tx_link_data_r' => 'USR-Final-Tx-Link-Data-Rate',
- 'client_dns_pri' => 'Client_DNS_Pri',
+ 'aat_ip_pool_definition' => 'AAT-IP-Pool-Definition',
+ 'client_dns_pri' => 'Client-DNS-Pri',
+ 'cisco_service_info' => 'Cisco-Service-Info',
'usr_primary_nbns_server' => 'USR-Primary_NBNS_Server',
- 'usr_cusr_hat_script_rule' => 'USR-CUSR-hat-Script-Rules',
- 'ascend_multicast_rate_li' => 'Ascend-Multicast-Rate-Limit',
- 'usr_rmmie_pwrlvl_farecho' => 'USR-RMMIE-PwrLvl-FarEcho-Canc',
+ 'aat_atm_direct' => 'AAT-ATM-Direct',
+ 'bind_ses_contexu' => 'Bind_Ses_Context',
+ 'sip_translated_request_u' => 'Sip-Translated-Request-URI',
'acc_acct_on_off_reason' => 'Acc-Acct-On-Off-Reason',
'le_multicast_client' => 'LE-Multicast-Client',
+ 'bind_sub_passwore' => 'Bind_Sub_Password',
+ 'cvpn3000_cisco_ip_phone_' => 'CVPN3000-Cisco-IP-Phone-Bypass',
'ascend_send_passwd' => 'Ascend-Send-Passwd',
- 'annex_unauthenticated_ti' => 'Annex-Unauthenticated-Time',
- 'tunnel_context' => 'Tunnel_Context',
- 'acc_nbns_server_sec' => 'Acc-Nbns-Server-Sec',
+ 'tunnel_remote_namf' => 'Tunnel_Remote_Name',
+ 'cvx_disconnect_cause' => 'CVX-Disconnect-Cause',
+ 'itk_auth_serv_prot' => 'ITK-Auth-Serv-Prot',
+ 'tunnel_context' => 'Tunnel-Context',
+ 'digest_uri' => 'Digest-URI',
'usr_channel_decrement' => 'USR-Channel-Decrement',
- 'usr_rmmie_firmware_versi' => 'USR-RMMIE-Firmware-Version',
+ 'acc_nbns_server_sec' => 'Acc-Nbns-Server-Sec',
'ms_chap_challenge' => 'MS-CHAP-Challenge',
- 'x_ascend_client_secondar' => 'X-Ascend-Client-Secondary-DNS',
+ 'cisco_assign_ip_pool' => 'Cisco-Assign-IP-Pool',
'ascend_cbcp_mode' => 'Ascend-CBCP-Mode',
'ascend_x25_rpoa' => 'Ascend-X25-Rpoa',
'usr_dtr_false_timeout' => 'USR-DTR-False-Timeout',
- 'usr_rad_multicast_routin' => 'USR-Rad-Multicast-Routing-Proto',
- 'ascend_x25_pad_x3_parame' => 'Ascend-X25-Pad-X3-Parameters',
+ 'acct_dyn_ac_enu' => 'Acct_Dyn_Ac_Ent',
'usr_physical_state' => 'USR-Physical-State',
+ 'x_ascend_ppp_vj_slot_com' => 'X-Ascend-PPP-VJ-Slot-Comp',
+ 'x_ascend_link_compressio' => 'X-Ascend-Link-Compression',
'ascend_fr_t391' => 'Ascend-FR-T391',
- 'bind_dot1q_port' => 'Bind_Dot1q_Port',
- 'lac_port_type' => 'LAC_Port_Type',
- 'bg_aging_time' => 'BG_Aging_Time',
+ 'bind_dot1q_port' => 'Bind-Dot1q-Port',
+ 'ns_secondary_dns' => 'NS-Secondary-DNS',
+ 'altiga_ipsec_tunnel_type' => 'Altiga-IPSec-Tunnel-Type-G',
+ 'lac_port_type' => 'LAC-Port-Type',
+ 'bg_aging_time' => 'BG-Aging-Time',
'erx_atm_scr' => 'ERX-Atm-SCR',
+ 'x_ascend_pre_input_octet' => 'X-Ascend-Pre-Input-Octets',
+ 'cisco_fax_connect_speed' => 'Cisco-Fax-Connect-Speed',
'x_ascend_menu_item' => 'X-Ascend-Menu-Item',
+ 'quintum_h323_voice_quali' => 'Quintum-h323-voice-quality',
'ascend_x25_pad_banner' => 'Ascend-X25-Pad-Banner',
+ 'module_failure_message' => 'Module-Failure-Message',
'h323_gw_id' => 'h323-gw-id',
'h323_preferred_lang' => 'h323-preferred-lang',
'usr_min_compression_size' => 'USR-Min-Compression-Size',
'usr_compression_type' => 'USR-Compression-Type',
- 'x_ascend_call_attempt_li' => 'X-Ascend-Call-Attempt-Limit',
+ 'bintec_ipxstaticroutetab' => 'BinTec-ipxStaticRouteTable',
'ascend_dialout_allowed' => 'Ascend-Dialout-Allowed',
'annex_local_username' => 'Annex-Local-Username',
'cisco_pre_input_packets' => 'Cisco-Pre-Input-Packets',
- 'ascend_send_secret' => 'Ascend-Send-Secret',
'shiva_function' => 'Shiva-Function',
- 'usr_dte_data_idle_timout' => 'USR-DTE-Data-Idle-Timout',
+ 'ascend_send_secret' => 'Ascend-Send-Secret',
'usr_number_of_blers' => 'USR-Number-of-Blers',
+ 'usr_dte_data_idle_timout' => 'USR-DTE-Data-Idle-Timout',
'usr_card_type' => 'USR-Card-Type',
- 'ascend_token_idle' => 'Ascend-Token-Idle',
+ 'x_ascend_connect_progres' => 'X-Ascend-Connect-Progress',
'x_ascend_group' => 'X-Ascend-Group',
+ 'ascend_token_idle' => 'Ascend-Token-Idle',
+ 'erx_qos_profile_interfac' => 'ERX-Qos-Profile-Interface-Type',
+ 'ascend_private_route_tab' => 'Ascend-Private-Route-Table-ID',
'nt_password' => 'NT-Password',
- 'acct_mcast_in_packets' => 'Acct_Mcast_In_Packets',
+ 'acct_mcast_in_packets' => 'Acct-Mcast-In-Packets',
+ 'x_ascend_multicast_clien' => 'X-Ascend-Multicast-Client',
'usr_supports_tags' => 'USR-Supports-Tags',
+ 'cvpn3000_authd_user_idle' => 'CVPN3000-Authd-User-Idle-Timeout',
'ascend_number_sessions' => 'Ascend-Number-Sessions',
'x_ascend_add_seconds' => 'X-Ascend-Add-Seconds',
'usr_number_of_upshifts' => 'USR-Number-of-Upshifts',
'proxy_to_realm' => 'Proxy-To-Realm',
+ 'aat_client_secondary_win' => 'AAT-Client-Secondary-WINS-NBNS',
+ 'aat_ip_tos_precedence' => 'AAT-IP-TOS-Precedence',
'acc_callback_num_valid' => 'Acc-Callback-Num-Valid',
- 'x_ascend_maximum_channel' => 'X-Ascend-Maximum-Channels',
+ 'nokia_ggsn_ip_address' => 'Nokia-GGSN-IP-Address',
'acc_access_community' => 'Acc-Access-Community',
- 'x_ascend_fr_direct_profi' => 'X-Ascend-FR-Direct-Profile',
+ 'ascend_multicast_rate_li' => 'Ascend-Multicast-Rate-Limit',
+ 'usr_default_dte_data_rat' => 'USR-Default-DTE-Data-Rate',
+ 'usr_rmmie_pwrlvl_nearech' => 'USR-RMMIE-PwrLvl-NearEcho-Canc',
'usr_send_name' => 'USR-Send-Name',
'usr_chassis_slot' => 'USR-Chassis-Slot',
'login_ip_host' => 'Login-IP-Host',
'ascend_netware_timeout' => 'Ascend-Netware-timeout',
+ 'bind_sub_user_at_context' => 'Bind-Sub-User-At-Context',
'vendor_specific' => 'Vendor-Specific',
- 'bind_sub_user_at_context' => 'Bind_Sub_User_At_Context',
'ascend_fr_direct_dlci' => 'Ascend-FR-Direct-DLCI',
- 'ascend_atm_fault_managem' => 'Ascend-ATM-Fault-Management',
'ascend_qos_upstream' => 'Ascend-QOS-Upstream',
- 'source_validation' => 'Source_Validation',
+ 'aat_user_mac_address' => 'AAT-User-MAC-Address',
+ 'source_validation' => 'Source-Validation',
'x_ascend_token_expiry' => 'X-Ascend-Token-Expiry',
+ 'altiga_ipsec_user_group_' => 'Altiga-IPSec-User-Group-Lock-G',
'ascend_dec_channel_count' => 'Ascend-Dec-Channel-Count',
+ 'assigned_ip_addrest' => 'Assigned_IP_Address',
'usr_local_framed_ip_addr' => 'USR-Local-Framed-IP-Addr',
'usr_service_option' => 'USR-Service-Option',
'usr_transmit_acc_map' => 'USR-Transmit-Acc-Map',
'ascend_fr_direct' => 'Ascend-FR-Direct',
+ 'usr_final_rx_link_data_r' => 'USR-Final-Rx-Link-Data-Rate',
'x_ascend_expect_callback' => 'X-Ascend-Expect-Callback',
+ 'x_ascend_disconnect_caus' => 'X-Ascend-Disconnect-Cause',
'acc_ml_damping_factor' => 'Acc-ML-Damping-Factor',
'framed_netmask' => 'Framed-Netmask',
'usr_connect_speed' => 'USR-Connect-Speed',
- 'ascend_client_primary_wi' => 'Ascend-Client-Primary-WINS',
+ 'x_ascend_home_agent_ip_a' => 'X-Ascend-Home-Agent-IP-Addr',
+ 'usr_disconnect_cause_ind' => 'USR-Disconnect-Cause-Indicator',
+ 'bg_span_dis' => 'BG-Span-Dis',
'cisco_multilink_id' => 'Cisco-Multilink-ID',
- 'bg_span_dis' => 'BG_Span_Dis',
+ 'tunnel_max_tunnels' => 'Tunnel-Max-Tunnels',
+ 'ascend_dsl_downstream_li' => 'Ascend-Dsl-Downstream-Limit',
'ascend_multilink_id' => 'Ascend-Multilink-ID',
- 'tunnel_max_tunnels' => 'Tunnel_Max_Tunnels',
+ 'altiga_ipsec_default_dom' => 'Altiga-IPSec-Default-Domain-G',
'ascend_dhcp_reply' => 'Ascend-DHCP-Reply',
+ 'login_ipv6_host' => 'Login-IPv6-Host',
'ascend_x25_cug' => 'Ascend-X25-Cug',
'shiva_network_protocols' => 'Shiva-Network-Protocols',
+ 'cvpn3000_ipsec_mode_conf' => 'CVPN3000-IPSec-Mode-Config',
+ 'extreme_netlogin_vlan' => 'Extreme-Netlogin-Vlan',
'ascend_ara_pw' => 'Ascend-Ara-PW',
- 'ip_host_addr' => 'Ip_Host_Addr',
+ 'tunnel_l2f_second_passwo' => 'Tunnel-L2F-Second-Password',
+ 'altiga_sep_card_assignme' => 'Altiga-SEP-Card-Assignment-G/U',
+ 'ip_host_addr' => 'Ip-Host-Addr',
'le_ip_gateway' => 'LE-IP-Gateway',
'usr_mobile_numbytes_txed' => 'USR-Mobile-NumBytes-Txed',
+ 'altiga_ipsec_allow_passw' => 'Altiga-IPSec-Allow-Passwd-Store-G/U',
+ 'itk_users_default_entry' => 'ITK-Users-Default-Entry',
+ 'quintum_h323_redirect_nu' => 'Quintum-h323-redirect-number',
'x_ascend_fr_t392' => 'X-Ascend-FR-T392',
+ 'acc_igmp_version' => 'Acc-Igmp-Version',
'cisco_pre_output_packets' => 'Cisco-Pre-Output-Packets',
- 'tunnel_group' => 'Tunnel_Group',
- 'bind_sub_password' => 'Bind_Sub_Password',
+ 'tunnel_group' => 'Tunnel-Group',
+ 'x_ascend_home_agent_udp_' => 'X-Ascend-Home-Agent-UDP-Port',
+ 'cvpn3000_tunneling_proto' => 'CVPN3000-Tunneling-Protocols',
+ 'usr_igmp_maximum_respons' => 'USR-IGMP-Maximum-Response-Time',
+ 'bind_sub_password' => 'Bind-Sub-Password',
'eap_message' => 'EAP-Message',
'exec_program' => 'Exec-Program',
- 'bg_path_cost' => 'BG_Path_Cost',
- 'auth_type' => 'Auth-Type',
+ 'cvpn3000_reqrd_client_fx' => 'CVPN3000-Reqrd-Client-Fw-Product-Code',
+ 'bg_path_cost' => 'BG-Path-Cost',
'usr_modem_training_time' => 'USR-Modem-Training-Time',
- 'ascend_cbcp_enable' => 'Ascend-CBCP-Enable',
+ 'auth_type' => 'Auth-Type',
+ 'itk_acct_serv_prot' => 'ITK-Acct-Serv-Prot',
'x_ascend_ipx_route' => 'X-Ascend-IPX-Route',
+ 'altiga_primary_dns_g' => 'Altiga-Primary-DNS-G',
+ 'ascend_cbcp_enable' => 'Ascend-CBCP-Enable',
+ 'ms_mppe_encryption_polic' => 'MS-MPPE-Encryption-Policy',
+ 'annex_unauthenticated_ti' => 'Annex-Unauthenticated-Time',
+ 'annex_begin_receive_line' => 'Annex-Begin-Receive-Line-Level',
+ 'ascend_atm_direct_profil' => 'Ascend-ATM-Direct-Profile',
+ 'redcreek_tunneled_dns_se' => 'RedCreek-Tunneled-DNS-Server',
'ascend_redirect_number' => 'Ascend-Redirect-Number',
'h323_credit_time' => 'h323-credit-time',
+ 'cvx_idle_limit' => 'CVX-Idle-Limit',
'ascend_appletalk_route' => 'Ascend-Appletalk-Route',
+ 'aat_ip_tos' => 'AAT-IP-TOS',
+ 'cvx_ppp_address' => 'CVX-PPP-Address',
+ 'aat_data_filter' => 'AAT-Data-Filter',
+ 'cvx_primary_dns' => 'CVX-Primary-DNS',
'shiva_link_protocol' => 'Shiva-Link-Protocol',
'x_ascend_fr_circuit_name' => 'X-Ascend-FR-Circuit-Name',
- 'client_id' => 'Client-Id',
'usr_appletalk' => 'USR-Appletalk',
- 'usr_mpip_tunnel_originat' => 'USR-MPIP-Tunnel-Originator',
+ 'client_id' => 'Client-Id',
+ 'tunnel_algorithn' => 'Tunnel_Algorithm',
+ 'aat_assign_ip_pool' => 'AAT-Assign-IP-Pool',
+ 'quintum_h323_incoming_co' => 'Quintum-h323-incoming-conf-id',
+ 'aat_atm_vpi' => 'AAT-ATM-VPI',
'annex_output_filter' => 'Annex-Output-Filter',
- 'pvc_circuit_padding' => 'PVC_Circuit_Padding',
- 'x_ascend_minimum_channel' => 'X-Ascend-Minimum-Channels',
+ 'pvc_circuit_padding' => 'PVC-Circuit-Padding',
+ 'usr_ipx_call_output_filt' => 'USR-IPX-Call-Output-Filter',
+ 'usr_rmmie_planned_discon' => 'USR-RMMIE-Planned-Disconnect',
+ 'session_error_msh' => 'Session_Error_Msg',
+ 'usr_rad_multicast_routin' => 'USR-Rad-Multicast-Routing-Ttl',
'h323_time_and_day' => 'h323-time-and-day',
- 'ascend_ipx_header_compre' => 'Ascend-IPX-Header-Compression',
+ 'cvpn3000_ipsec_backup_se' => 'CVPN3000-IPSec-Backup-Servers',
'termination_action' => 'Termination-Action',
- 'x_ascend_modem_portno' => 'X-Ascend-Modem-PortNo',
+ 'cvpn3000_ipsec_client_fx' => 'CVPN3000-IPSec-Client-Fw-Filter-Opt',
+ 'aat_client_primary_dnt' => 'AAT-Client-Primary-DNS',
'acct_tunnel_packets_lost' => 'Acct-Tunnel-Packets-Lost',
+ 'x_ascend_modem_portno' => 'X-Ascend-Modem-PortNo',
'framed_filter_id' => 'Framed-Filter-Id',
'usr_ccp_algorithm' => 'USR-CCP-Algorithm',
+ 'quintum_h323_preferred_l' => 'Quintum-h323-preferred-lang',
+ 'ascend_fr_link_status_dl' => 'Ascend-FR-Link-Status-DLCI',
'ascend_token_expiry' => 'Ascend-Token-Expiry',
- 'annex_secondary_nbns_ser' => 'Annex-Secondary-NBNS-Server',
- 'usr_et_bridge_call_outpu' => 'USR-ET-Bridge-Call-Output-Filte',
+ 'itk_auth_req_type' => 'ITK-Auth-Req-Type',
'acc_modem_error_protocol' => 'Acc-Modem-Error-Protocol',
'acc_request_type' => 'Acc-Request-Type',
+ 'usr_last_number_dialed_i' => 'USR-Last-Number-Dialed-In-DNIS',
'x_ascend_ipx_peer_mode' => 'X-Ascend-IPX-Peer-Mode',
'ascend_ppp_vj_slot_comp' => 'Ascend-PPP-VJ-Slot-Comp',
'cisco_presession_time' => 'Cisco-PreSession-Time',
'usr_chat_script_name' => 'USR-Chat-Script-Name',
+ 'tunnel_session_auti' => 'Tunnel_Session_Auth',
'ascend_fr_circuit_name' => 'Ascend-FR-Circuit-Name',
'ascend_expect_callback' => 'Ascend-Expect-Callback',
'framed_mtu' => 'Framed-MTU',
- 'ascend_port_redir_protoc' => 'Ascend-Port-Redir-Protocol',
'usr_pw_vpn_name' => 'USR-PW_VPN_Name',
+ 'nomadix_ip_upsell' => 'Nomadix-IP-Upsell',
'ascend_nas_port_format' => 'Ascend-NAS-Port-Format',
- 'shasta_vpn_name' => 'Shasta-VPN-Name',
'usr_dtr_true_timeout' => 'USR-DTR-True-Timeout',
- 'ascend_third_prompt' => 'Ascend-Third-Prompt',
+ 'shasta_vpn_name' => 'Shasta-VPN-Name',
'connect_rate' => 'Connect-Rate',
- 'usr_block_error_count_li' => 'USR-Block-Error-Count-Limit',
+ 'ascend_third_prompt' => 'Ascend-Third-Prompt',
+ 'cabletron_protocol_enabl' => 'Cabletron-Protocol-Enable',
+ 'annex_pre_input_octets' => 'Annex-Pre-Input-Octets',
+ 'cvx_modem_error_correcti' => 'CVX-Modem-Error-Correction',
+ 'cvx_ss7_session_id_type' => 'CVX-SS7-Session-ID-Type',
'called_station_id' => 'Called-Station-Id',
+ 'itk_ddi' => 'ITK-DDI',
'usr_pw_cutoff' => 'USR-PW_Cutoff',
'ascend_data_rate' => 'Ascend-Data-Rate',
+ 'acct_input_packets_65' => 'Acct_Input_Packets_64',
'x_ascend_ts_idle_mode' => 'X-Ascend-TS-Idle-Mode',
'ascend_x25_pad_prompt' => 'Ascend-X25-Pad-Prompt',
'x_ascend_dhcp_reply' => 'X-Ascend-DHCP-Reply',
'acc_nbns_server_pri' => 'Acc-Nbns-Server-Pri',
+ 'post_auth_type' => 'Post-Auth-Type',
'ascend_call_filter' => 'Ascend-Call-Filter',
'acc_tunnel_secret' => 'Acc-Tunnel-Secret',
- 'usr_simplified_v42bis_us' => 'USR-Simplified-V42bis-Usage',
- 'bind_int_context' => 'Bind_Int_Context',
+ 'colubris_avpair' => 'Colubris-AVPair',
+ 'bind_int_context' => 'Bind-Int-Context',
+ 'annex_logical_channel_nu' => 'Annex-Logical-Channel-Number',
'erx_virtual_router_name' => 'ERX-Virtual-Router-Name',
+ 'wispr_redirection_url' => 'WISPr-Redirection-URL',
+ 'bintec_ipextiftable' => 'BinTec-ipExtIfTable',
'crypt_password' => 'Crypt-Password',
'challenge_state' => 'Challenge-State',
- 'ascend_client_secondary_' => 'Ascend-Client-Secondary-DNS',
- 'strip_user_name' => 'Strip-User-Name',
+ 'x_ascend_pre_input_packe' => 'X-Ascend-Pre-Input-Packets',
+ 'altiga_ipsec_l2l_keepali' => 'Altiga-IPSec-L2L-Keepalives-G',
+ 'x_ascend_dhcp_maximum_le' => 'X-Ascend-DHCP-Maximum-Leases',
+ 'acc_dialout_auth_passwor' => 'Acc-Dialout-Auth-Password',
+ 'itk_ip_pool' => 'ITK-IP-Pool',
+ 'pvc_profile_namf' => 'PVC_Profile_Name',
'x_ascend_user_acct_host' => 'X-Ascend-User-Acct-Host',
- 'x_ascend_route_ip' => 'X-Ascend-Route-IP',
- 'x_ascend_assign_ip_clien' => 'X-Ascend-Assign-IP-Client',
+ 'strip_user_name' => 'Strip-User-Name',
+ 'itk_ppp_client_server_mo' => 'ITK-PPP-Client-Server-Mode',
'usr_mbi_ct_bchannel_used' => 'USR-Mbi_Ct_BChannel_Used',
+ 'x_ascend_route_ip' => 'X-Ascend-Route-IP',
+ 'ascend_seconds_of_histor' => 'Ascend-Seconds-Of-History',
+ 'cvx_data_rate' => 'CVX-Data-Rate',
'ascend_x25_profile_name' => 'Ascend-X25-Profile-Name',
+ 'itk_ftp_auth_ip' => 'ITK-Ftp-Auth-IP',
+ 'cisco_control_info' => 'Cisco-Control-Info',
+ 'cvpn3000_secondary_wins' => 'CVPN3000-Secondary-WINS',
'usr_call_type' => 'USR-Call-Type',
'x_ascend_user_acct_base' => 'X-Ascend-User-Acct-Base',
+ 'acct_mcast_in_packett' => 'Acct_Mcast_In_Packets',
+ 'ns_vsys_name' => 'NS-VSYS-Name',
'acct_output_gigawords' => 'Acct-Output-Gigawords',
- 'usr_rmmie_firmware_build' => 'USR-RMMIE-Firmware-Build-Date',
- 'ascend_fr_link_status_dl' => 'Ascend-FR-Link-Status-DLCI',
+ 'bind_typf' => 'Bind_Type',
+ 'bintec_ipqostable' => 'BinTec-ipQoSTable',
+ 'bintec_ipxstaticservtabl' => 'BinTec-ipxStaticServTable',
+ 'cvpn3000_l2tp_mppc_compr' => 'CVPN3000-L2TP-MPPC-Compression',
'login_lat_port' => 'Login-LAT-Port',
'usr_call_arrival_in_gmt' => 'USR-Call-Arrival-in-GMT',
- 'acct_mcast_in_octets' => 'Acct_Mcast_In_Octets',
+ 'acct_mcast_in_octets' => 'Acct-Mcast-In-Octets',
'erx_sa_validate' => 'ERX-Sa-Validate',
'ascend_service_type' => 'Ascend-Service-Type',
- 'ascend_x25_nui_password_' => 'Ascend-X25-Nui-Password-Prompt',
'usr_pw_vpn_gateway' => 'USR-PW_VPN_Gateway',
- 'ascend_fr_dce_n392' => 'Ascend-FR-DCE-N392',
'acc_ip_compression' => 'Acc-Ip-Compression',
- 'lac_real_port_type' => 'LAC_Real_Port_Type',
- 'ascend_if_netmask' => 'Ascend-IF-Netmask',
+ 'ascend_fr_dce_n392' => 'Ascend-FR-DCE-N392',
+ 'bintec_ipxcirctable' => 'BinTec-ipxCircTable',
+ 'lac_real_port_type' => 'LAC-Real-Port-Type',
+ 'ascend_client_primary_dn' => 'Ascend-Client-Primary-DNS',
'acct_session_start_time' => 'Acct-Session-Start-Time',
+ 'ascend_if_netmask' => 'Ascend-IF-Netmask',
'ms_chap_nt_enc_pw' => 'MS-CHAP-NT-Enc-PW',
- 'ascend_port_redir_portnu' => 'Ascend-Port-Redir-Portnum',
- 'mcast_maxgroups' => 'Mcast_MaxGroups',
- 'x_ascend_home_agent_ip_a' => 'X-Ascend-Home-Agent-IP-Addr',
+ 'ms_mppe_encryption_types' => 'MS-MPPE-Encryption-Types',
+ 'cisco_fax_process_abort_' => 'Cisco-Fax-Process-Abort-Flag',
+ 'mcast_maxgroups' => 'Mcast-MaxGroups',
+ 'annex_end_receive_line_l' => 'Annex-End-Receive-Line-Level',
+ 'usr_ipx_call_input_filte' => 'USR-IPX-Call-Input-Filter',
+ 'usr_back_channel_data_ra' => 'USR-Back-Channel-Data-Rate',
'ascend_cache_time' => 'Ascend-Cache-Time',
'x_ascend_data_svc' => 'X-Ascend-Data-Svc',
- 'erx_tunnel_virtual_route' => 'ERX-Tunnel-Virtual-Router',
'usr_re_chap_timeout' => 'USR-Re-Chap-Timeout',
+ 'bintec_bibodialtable' => 'BinTec-biboDialTable',
+ 'annex_connect_progress' => 'Annex-Connect-Progress',
'x_ascend_ppp_vj_1172' => 'X-Ascend-PPP-VJ-1172',
'usr_igmp_routing' => 'USR-IGMP-Routing',
+ 'x_ascend_ip_pool_definit' => 'X-Ascend-IP-Pool-Definition',
'h323_prompt_id' => 'h323-prompt-id',
+ 'foundry_command_string' => 'Foundry-Command-String',
'le_terminate_detail' => 'LE-Terminate-Detail',
+ 'cvpn3000_pptp_encryption' => 'CVPN3000-PPTP-Encryption',
+ 'quintum_h323_disconnect_' => 'Quintum-h323-disconnect-time',
'acc_ml_clear_threshold' => 'Acc-ML-Clear-Threshold',
'x_ascend_ip_direct' => 'X-Ascend-IP-Direct',
- 'nas_port' => 'NAS-Port',
- 'x_ascend_data_rate' => 'X-Ascend-Data-Rate',
'usr_ip_call_input_filter' => 'USR-IP-Call-Input-Filter',
+ 'x_ascend_data_rate' => 'X-Ascend-Data-Rate',
+ 'nas_port' => 'NAS-Port',
+ 'ascend_client_secondary_' => 'Ascend-Client-Secondary-WINS',
'ascend_auth_type' => 'Ascend-Auth-Type',
'x_ascend_preempt_limit' => 'X-Ascend-Preempt-Limit',
+ 'cvx_xmit_rate' => 'CVX-Xmit-Rate',
+ 'annex_transmitted_packet' => 'Annex-Transmitted-Packets',
'h323_credit_amount' => 'h323-credit-amount',
'usr_reply_script1' => 'USR-Reply-Script1',
- 'usr_et_bridge_input_filt' => 'USR-ET-Bridge-Input-Filter',
'current_time' => 'Current-Time',
'cisco_xmit_rate' => 'Cisco-Xmit-Rate',
- 'ascend_authen_alias' => 'Ascend-Authen-Alias',
'x_ascend_session_svr_key' => 'X-Ascend-Session-Svr-Key',
+ 'ascend_authen_alias' => 'Ascend-Authen-Alias',
+ 'erx_redirect_vr_name' => 'ERX-Redirect-VR-Name',
+ 'module_success_message' => 'Module-Success-Message',
'acc_dialout_auth_mode' => 'Acc-Dialout-Auth-Mode',
+ 'bind_auth_contexu' => 'Bind_Auth_Context',
+ 'x_ascend_minimum_channel' => 'X-Ascend-Minimum-Channels',
'usr_event_date_time' => 'USR-Event-Date-Time',
'x_ascend_ipx_node_addr' => 'X-Ascend-IPX-Node-Addr',
- 'ascend_primary_home_agen' => 'Ascend-Primary-Home-Agent',
+ 'cvpn3000_ipsec_over_udp' => 'CVPN3000-IPSec-Over-UDP',
'x_ascend_user_acct_time' => 'X-Ascend-User-Acct-Time',
- 'usr_at_call_output_filte' => 'USR-AT-Call-Output-Filter',
+ 'cisco_email_server_ack_f' => 'Cisco-Email-Server-Ack-Flag',
+ 'telebit_activate_command' => 'Telebit-Activate-Command',
'acc_output_errors' => 'Acc-Output-Errors',
- 'usr_ipx_rip_output_filte' => 'USR-IPX-RIP-Output-Filter',
+ 'juniper_allow_configurat' => 'Juniper-Allow-Configuration',
+ 'bind_l2tp_tunnel_name' => 'Bind-L2TP-Tunnel-Name',
'x_ascend_pri_number_type' => 'X-Ascend-PRI-Number-Type',
- 'bind_l2tp_tunnel_name' => 'Bind_L2TP_Tunnel_Name',
- 'replicate_to_realm' => 'Replicate-To-Realm',
+ 'bintec_biboppptable' => 'BinTec-biboPPPTable',
+ 'le_ipsec_outsource_profi' => 'LE-IPSec-Outsource-Profile',
'usr_at_zip_input_filter' => 'USR-AT-Zip-Input-Filter',
+ 'replicate_to_realm' => 'Replicate-To-Realm',
'annex_mrru' => 'Annex-MRRU',
'event_timestamp' => 'Event-Timestamp',
+ 'nokia_sgsn_ip_address' => 'Nokia-SGSN-IP-Address',
'ascend_pre_input_packets' => 'Ascend-Pre-Input-Packets',
+ 'cvpn5000_client_assigned' => 'CVPN5000-Client-Assigned-IP',
+ 'tunnel_dnit' => 'Tunnel_DNIS',
'h323_call_origin' => 'h323-call-origin',
'x_ascend_fr_type' => 'X-Ascend-FR-Type',
+ 'itk_provider_id' => 'ITK-Provider-Id',
+ 'cvx_ppp_log_mask' => 'CVX-PPP-Log-Mask',
'x_ascend_token_idle' => 'X-Ascend-Token-Idle',
+ 'usr_rmmie_pwrlvl_xmit_lv' => 'USR-RMMIE-PwrLvl-Xmit-Lvl',
'usr_igmp_query_interval' => 'USR-IGMP-Query-Interval',
+ 'quintum_h323_billing_mod' => 'Quintum-h323-billing-model',
'ascend_atm_vci' => 'Ascend-ATM-Vci',
'usr_port_tap_output' => 'USR-Port-Tap-Output',
'session' => 'Session',
+ 'itk_welcome_message' => 'ITK-Welcome-Message',
+ 'cvpn3000_ike_keep_alives' => 'CVPN3000-IKE-Keep-Alives',
'ascend_uu_info' => 'Ascend-UU-Info',
- 'ms_mppe_recv_key' => 'MS-MPPE-Recv-Key',
+ 'usr_et_bridge_call_outpu' => 'USR-ET-Bridge-Call-Output-Filte',
'usr_secondary_dns_server' => 'USR-Secondary_DNS_Server',
- 'x_ascend_tunneling_proto' => 'X-Ascend-Tunneling-Protocol',
+ 'ms_mppe_recv_key' => 'MS-MPPE-Recv-Key',
+ 'bintec_ripcirctable' => 'BinTec-ripCircTable',
'acc_dial_port_index' => 'Acc-Dial-Port-Index',
'cisco_nas_port' => 'Cisco-NAS-Port',
+ 'itk_username' => 'ITK-Username',
'usr_send_script1' => 'USR-Send-Script1',
+ 'cvpn3000_ipsec_ike_peer_' => 'CVPN3000-IPSec-IKE-Peer-ID-Check',
+ 'ascend_dsl_upstream_limi' => 'Ascend-Dsl-Upstream-Limit',
+ 'x_ascend_dec_channel_cou' => 'X-Ascend-Dec-Channel-Count',
'usr_tunnel_security' => 'USR-Tunnel-Security',
'arap_security' => 'ARAP-Security',
'tunnel_preference' => 'Tunnel-Preference',
+ 'cisco_port_used' => 'Cisco-Port-Used',
'usr_reply_script4' => 'USR-Reply-Script4',
- 'h323_currency_type' => 'h323-currency-type',
+ 'cvpn5000_client_real_ip' => 'CVPN5000-Client-Real-IP',
'usr_rmmie_status' => 'USR-RMMIE-Status',
- 'ascend_shared_profile_en' => 'Ascend-Shared-Profile-Enable',
- 'annex_syslog_tap' => 'Annex-Syslog-Tap',
'usr_send_script4' => 'USR-Send-Script4',
+ 'quintum_h323_connect_tim' => 'Quintum-h323-connect-time',
+ 'annex_syslog_tap' => 'Annex-Syslog-Tap',
+ 'redcreek_tunneled_hostna' => 'RedCreek-Tunneled-HostName',
'acc_clearing_location' => 'Acc-Clearing-Location',
+ 'ascend_access_intercept_' => 'Ascend-Access-Intercept-LEA',
'annex_disconnect_reason' => 'Annex-Disconnect-Reason',
- 'x_ascend_dhcp_maximum_le' => 'X-Ascend-DHCP-Maximum-Leases',
'usr_at_input_filter' => 'USR-AT-Input-Filter',
'usr_auth_mode' => 'USR-Auth-Mode',
- 'shiva_session_id' => 'Shiva-Session-Id',
'usr_expected_voltage' => 'USR-Expected-Voltage',
+ 'shiva_session_id' => 'Shiva-Session-Id',
+ 'annex_maximum_call_durat' => 'Annex-Maximum-Call-Duration',
+ 'usr_block_error_count_li' => 'USR-Block-Error-Count-Limit',
'ascend_owner_ip_addr' => 'Ascend-Owner-IP-Addr',
- 'ascend_atm_direct_profil' => 'Ascend-ATM-Direct-Profile',
+ 'bind_tun_contexu' => 'Bind_Tun_Context',
'usr_pw_usr_ofilter_ipx' => 'USR-PW_USR_OFilter_IPX',
'framed_routing' => 'Framed-Routing',
- 'pam_auth' => 'Pam-Auth',
+ 'annex_primary_nbns_serve' => 'Annex-Primary-NBNS-Server',
'usr_interface_index' => 'USR-Interface-Index',
- 'x_ascend_transit_number' => 'X-Ascend-Transit-Number',
+ 'pam_auth' => 'Pam-Auth',
'usr_end_time' => 'USR-End-Time',
+ 'rate_limit_bursu' => 'Rate_Limit_Burst',
+ 'nomadix_expiration' => 'Nomadix-Expiration',
+ 'x_ascend_transit_number' => 'X-Ascend-Transit-Number',
+ 'itk_usergroup' => 'ITK-Usergroup',
'x_ascend_assign_ip_pool' => 'X-Ascend-Assign-IP-Pool',
+ 'annex_secondary_nbns_ser' => 'Annex-Secondary-NBNS-Server',
+ 'bind_dot1q_vlan_tag_id' => 'Bind-Dot1q-Vlan-Tag-Id',
'ms_secondary_nbns_server' => 'MS-Secondary-NBNS-Server',
- 'bind_dot1q_vlan_tag_id' => 'Bind_Dot1q_Vlan_Tag_Id',
+ 'tunnel_retransmit' => 'Tunnel-Retransmit',
'acct_tunnel_connection' => 'Acct-Tunnel-Connection',
- 'tunnel_retransmit' => 'Tunnel_Retransmit',
'x_ascend_backup' => 'X-Ascend-Backup',
+ 'xedia_ppp_echo_interval' => 'Xedia-PPP-Echo-Interval',
'usr_bearer_capabilities' => 'USR-Bearer-Capabilities',
- 'ascend_calling_id_type_o' => 'Ascend-Calling-Id-Type-Of-Num',
'shiva_acct_serv_switch' => 'Shiva-Acct-Serv-Switch',
- 'ascend_h323_conference_i' => 'Ascend-H323-Conference-Id',
'acct_authentic' => 'Acct-Authentic',
+ 'le_nat_other_session_tim' => 'LE-NAT-Other-Session-Timeout',
+ 'cvpn3000_ipsec_banner2' => 'CVPN3000-IPSec-Banner2',
'x_ascend_force_56' => 'X-Ascend-Force-56',
'framed_appletalk_network' => 'Framed-AppleTalk-Network',
'reply_message' => 'Reply-Message',
- 'annex_addr_resolution_pr' => 'Annex-Addr-Resolution-Protocol',
'class' => 'Class',
'h323_conf_id' => 'h323-conf-id',
+ 'quintum_h323_disconnecta' => 'Quintum-h323-disconnect-cause',
+ 'itk_filter_rule' => 'ITK-Filter-Rule',
+ 'wispr_bandwidth_max_up' => 'WISPr-Bandwidth-Max-Up',
+ 'usr_appletalk_network_ra' => 'USR-Appletalk-Network-Range',
'ascend_cbcp_delay' => 'Ascend-CBCP-Delay',
+ 'usr_dte_ring_no_answer_l' => 'USR-DTE-Ring-No-Answer-Limit',
+ 'pre_acct_type' => 'Pre-Acct-Type',
+ 'usr_local_ip_address' => 'USR-Local-IP-Address',
'ascend_dropped_octets' => 'Ascend-Dropped-Octets',
'ascend_h323_dialed_time' => 'Ascend-H323-Dialed-Time',
- 'usr_local_ip_address' => 'USR-Local-IP-Address',
+ 'cisco_email_server_addre' => 'Cisco-Email-Server-Address',
'ascend_x25_x121_address' => 'Ascend-X25-X121-Address',
- 'ascend_destination_nas_p' => 'Ascend-Destination-Nas-Port',
- 'annex_local_ip_address' => 'Annex-Local-IP-Address',
+ 'cvx_multicast_client' => 'CVX-Multicast-Client',
+ 'wispr_bandwidth_min_up' => 'WISPr-Bandwidth-Min-Up',
'usr_at_output_filter' => 'USR-AT-Output-Filter',
+ 'annex_local_ip_address' => 'Annex-Local-IP-Address',
'cisco_ip_pool_definition' => 'Cisco-IP-Pool-Definition',
+ 'cisco_gateway_id' => 'Cisco-Gateway-Id',
+ 'itk_password_prompt' => 'ITK-Password-Prompt',
'annex_domain_name' => 'Annex-Domain-Name',
+ 'foundry_command_exceptio' => 'Foundry-Command-Exception-Flag',
'ascend_preempt_limit' => 'Ascend-Preempt-Limit',
+ 'erx_minimum_bps' => 'ERX-Minimum-BPS',
+ 'aat_mcast_client' => 'AAT-MCast-Client',
+ 'ascend_atm_fault_managem' => 'Ascend-ATM-Fault-Management',
'ascend_event_type' => 'Ascend-Event-Type',
- 'x_ascend_pre_input_octet' => 'X-Ascend-Pre-Input-Octets',
'exec_program_wait' => 'Exec-Program-Wait',
-
- #NOMENT
- 'nomadix_ip_upsell' => 'Nomadix-IP-Upsell',
+ 'framed_interface_id' => 'Framed-Interface-Id',
#NETC.NET.AU (RADIATOR?)
'authentication_type' => 'Authentication-Type',
if ($self->svcpart) { # Case 1
$svcpart = $self->svcpart;
- } elsif (my $cust_svc = $self->cust_svc) { # Case 2
- $svcpart = $cust_svc->svcpart;
+ } elsif ( $self->svcnum
+ && qsearchs('cust_svc',{'svcnum'=>$self->svcnum} )
+ ) { #Case 2
+ $svcpart = $self->cust_svc->svcpart;
} else { # Case 3
$svcpart = '';
}
$self->SUPER::check;
}
-=item insert [ JOBNUM_ARRAYREF ]
+=item insert [ JOBNUM_ARRAYREF [ OBJECTS_ARRAYREF ] ]
Adds this record to the database. If there is an error, returns the error,
otherwise returns false.
If an arrayref is passed as parameter, the B<jobnum>s of any export jobs will
be added to the array.
+If an arrayref of FS::tablename objects (for example, FS::acct_snarf objects)
+is passed as the optional second parameter, they will have their svcnum fields
+set and will be inserted after this record, but before any exports are run.
+
=cut
sub insert {
my $self = shift;
local $FS::queue::jobnums = shift if @_;
+ my $objects = scalar(@_) ? shift : [];
my $error;
local $SIG{HUP} = 'IGNORE';
return $error if $error;
my $svcnum = $self->svcnum;
- my $cust_svc;
- unless ( $svcnum ) {
+ my $cust_svc = $svcnum ? qsearchs('cust_svc',{'svcnum'=>$self->svcnum}) : '';
+ #unless ( $svcnum ) {
+ if ( !$svcnum or !$cust_svc ) {
$cust_svc = new FS::cust_svc ( {
#hua?# 'svcnum' => $svcnum,
+ 'svcnum' => $self->svcnum,
'pkgnum' => $self->pkgnum,
'svcpart' => $self->svcpart,
} );
}
$svcnum = $self->svcnum($cust_svc->svcnum);
} else {
- $cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
+ #$cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
unless ( $cust_svc ) {
$dbh->rollback if $oldAutoCommit;
return "no cust_svc record found for svcnum ". $self->svcnum;
return $error;
}
+ foreach my $object ( @$objects ) {
+ $object->svcnum($self->svcnum);
+ $error = $object->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
#new-style exports!
unless ( $noexport_hack ) {
foreach my $part_export ( $self->cust_svc->part_svc->part_export ) {
#get part_svc
my $svcpart;
- if ( $self->svcnum ) {
+ if ( $self->svcnum && qsearchs('cust_svc', {'svcnum'=>$self->svcnum}) ) {
my $cust_svc = $self->cust_svc;
return "Unknown svcnum" unless $cust_svc;
$svcpart = $cust_svc->svcpart;
sub cancel { ''; }
-=back
+=item clone_suspended
+
+Constructor used by FS::part_export::_export_suspend fallback. Stub returning
+same object for svc_ classes which don't implement a suspension fallback
+(everything except svc_acct at the moment). Document better.
-=head1 VERSION
+=cut
+
+sub clone_suspended {
+ shift;
+}
+
+=item clone_kludge_unsuspend
+
+Constructor used by FS::part_export::_export_unsuspend fallback. Stub returning
+same object for svc_ classes which don't implement a suspension fallback
+(everything except svc_acct at the moment). Document better.
-$Id: svc_Common.pm,v 1.13 2003-08-05 00:20:47 khoff Exp $
+=cut
+
+sub clone_kludge_unsuspend {
+ shift;
+}
+
+=back
=head1 BUGS
use Fcntl qw(:flock);
use FS::UID qw( datasrc );
use FS::Conf;
-use FS::Record qw( qsearch qsearchs fields dbh );
+use FS::Record qw( qsearch qsearchs fields dbh dbdef );
use FS::svc_Common;
use FS::cust_svc;
use FS::part_svc;
contain an arrayref of group names. See L<FS::radius_usergroup>. (used in
sqlradius export only)
+The additional field I<child_objects> can optionally be defined; if so it
+should contain an arrayref of FS::tablename objects. They will have their
+svcnum fields set and will be inserted after this record, but before any
+exports are run.
+
(TODOC: L<FS::queue> and L<freeside-queued>)
(TODOC: new exports!)
+
=cut
sub insert {
# 'domsvc' => $self->domsvc,
# } );
- if ( $self->svcnum ) {
+ if ( $self->svcnum && qsearchs('cust_svc',{'svcnum'=>$self->svcnum}) ) {
my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$self->svcnum});
unless ( $cust_svc ) {
$dbh->rollback if $oldAutoCommit;
#see? i told you it was more complicated
my @jobnums;
- $error = $self->SUPER::insert(\@jobnums);
+ $error = $self->SUPER::insert(\@jobnums, $self->child_objects || [] );
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
sub delete {
my $self = shift;
+ return "can't delete system account" if $self->_check_system;
+
return "Can't delete an account which is a (svc_forward) source!"
if qsearch( 'svc_forward', { 'srcsvc' => $self->svcnum } );
my $error;
warn "$me replacing $old with $new\n" if $DEBUG;
+ return "can't modify system account" if $old->_check_system;
+
return "Username in use"
if $old->username ne $new->username &&
qsearchs( 'svc_acct', { 'username' => $new->username,
=item suspend
-Suspends this account by prefixing *SUSPENDED* to the password. If there is an
-error, returns the error, otherwise returns false.
+Suspends this account by calling export-specific suspend hooks. 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 {
my $self = shift;
- my %hash = $self->hash;
- unless ( $hash{_password} =~ /^\*SUSPENDED\* /
- || $hash{_password} eq '*'
- ) {
- $hash{_password} = '*SUSPENDED* '.$hash{_password};
- my $new = new FS::svc_acct ( \%hash );
- my $error = $new->replace($self);
- return $error if $error;
- }
-
+ return "can't suspend system account" if $self->_check_system;
$self->SUPER::suspend;
}
=item unsuspend
-Unsuspends this account by removing *SUSPENDED* from the password. If there is
-an error, returns the error, otherwise returns false.
+Unsuspends this account by by calling export-specific suspend hooks. 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 {
or return "Illegal finger: ". $self->getfield('finger');
$self->setfield('finger', $1);
- $recref->{quota} =~ /^(\d*)$/ or return "Illegal quota";
+ $recref->{quota} =~ /^(\w*)$/ or return "Illegal quota";
$recref->{quota} = $1;
unless ( $part_svc->part_svc_column('slipip')->columnflag eq 'F' ) {
#$recref->{password} = $1.
# crypt($3,$saltset[int(rand(64))].$saltset[int(rand(64))]
#;
- } elsif ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([\w\.\/\$\;\+]{13,34})$/ ) {
+ } elsif ( $recref->{_password} =~ /^((\*SUSPENDED\* )?)([\w\.\/\$\;\+]{13,60})$/ ) {
$recref->{_password} = $1.$3;
} elsif ( $recref->{_password} eq '*' ) {
$recref->{_password} = '*';
+ } elsif ( $recref->{_password} eq '!' ) {
+ $recref->{_password} = '!';
} elsif ( $recref->{_password} eq '!!' ) {
$recref->{_password} = '!!';
} else {
$self->SUPER::check;
}
+=item _check_system
+
+=cut
+
+sub _check_system {
+ my $self = shift;
+ scalar( grep { $self->username eq $_ || $self->email eq $_ }
+ $conf->config('system_usernames')
+ );
+}
+
=item radius
Depriciated, use radius_reply instead.
$self->username. '@'. $self->domain;
}
+=item acct_snarf
+
+Returns an array of FS::acct_snarf records associated with the account.
+If the acct_snarf table does not exist or there are no associated records,
+an empty list is returned
+
+=cut
+
+sub acct_snarf {
+ my $self = shift;
+ return () unless dbdef->table('acct_snarf');
+ eval "use FS::acct_snarf;";
+ die $@ if $@;
+ qsearch('acct_snarf', { 'svcnum' => $self->svcnum } );
+}
+
=item seconds_since TIMESTAMP
Returns the number of seconds this account has been online since TIMESTAMP,
$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
+given time range. (document this better)
+
+=cut
+
+sub get_session_history_sqlradacct {
+ my $self = shift;
+ $self->cust_svc->get_session_history_sqlradacct(@_);
+}
+
=item radius_groups
Returns all RADIUS groups for this account (see L<FS::radius_usergroup>).
}
}
+=item clone_suspended
+
+Constructor used by FS::part_export::_export_suspend fallback. Document
+better.
+
+=cut
+
+sub clone_suspended {
+ my $self = shift;
+ my %hash = $self->hash;
+ $hash{_password} = join('',map($pw_set[ int(rand $#pw_set) ], (0..7) ) );
+ new FS::svc_acct \%hash;
+}
+
+=item clone_kludge_unsuspend
+
+Constructor used by FS::part_export::_export_unsuspend fallback. Document
+better.
+
+=cut
+
+sub clone_kludge_unsuspend {
+ my $self = shift;
+ my %hash = $self->hash;
+ $hash{_password} = '';
+ new FS::svc_acct \%hash;
+}
+
=back
=head1 SUBROUTINES
return "Unknown catchall" unless $svc_acct;
}
- my $error = $self->ut_textn('purpose')
- or $self->SUPER::check;
- return $error if $error;
+ $self->ut_textn('purpose')
+ or $self->SUPER::check;
}
--- /dev/null
+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;
+
=item srcsvc - svcnum of the source of the forward (see L<FS::svc_acct>)
+=item src - literal source (username or full email address)
+
=item dstsvc - svcnum of the destination of the forward (see L<FS::svc_acct>)
-=item dst - foreign destination (email address) - forward not local to freeside
+=item dst - literal destination (username or full email address)
=back
#my $part_svc = $x;
my $error = $self->ut_numbern('svcnum')
- || $self->ut_number('srcsvc')
+ || $self->ut_numbern('srcsvc')
|| $self->ut_numbern('dstsvc')
;
return $error if $error;
- return "Unknown srcsvc" unless $self->srcsvc_acct;
+ return "Both srcsvc and src were defined; only one can be specified"
+ if $self->srcsvc && $self->src;
+
+ return "one of srcsvc or src is required"
+ unless $self->srcsvc || $self->src;
+
+ return "Unknown srcsvc: ". $self->srcsvc
+ unless ! $self->srcsvc || $self->srcsvc_acct;
return "Both dstsvc and dst were defined; only one can be specified"
if $self->dstsvc && $self->dst;
return "one of dstsvc or dst is required"
unless $self->dstsvc || $self->dst;
- #return "Unknown dstsvc: $dstsvc" unless $self->dstsvc_acct || ! $self->dstsvc;
- return "Unknown dstsvc"
- unless qsearchs('svc_acct', { 'svcnum' => $self->dstsvc } )
- || ! $self->dstsvc;
+ return "Unknown dstsvc: ". $self->dstsvc
+ unless ! $self->dstsvc || $self->dstsvc_acct;
+ #return "Unknown dstsvc"
+ # unless qsearchs('svc_acct', { 'svcnum' => $self->dstsvc } )
+ # || ! $self->dstsvc;
+ if ( $self->src ) {
+ $self->src =~ /^([\w\.\-\&]*)(\@([\w\-]+\.)+\w+)?$/
+ or return "Illegal src: ". $self->dst;
+ $self->src("$1$2");
+ } else {
+ $self->src('');
+ }
if ( $self->dst ) {
- $self->dst =~ /^([\w\.\-]+)\@(([\w\-]+\.)+\w+)$/
+ $self->dst =~ /^([\w\.\-\&]*)(\@([\w\-]+\.)+\w+)?$/
or return "Illegal dst: ". $self->dst;
- $self->dst("$1\@$2");
+ $self->dst("$1$2");
} else {
$self->dst('');
}
=item srcsvc_acct
-Returns the FS::svc_acct object referenced by the srcsvc column.
+Returns the FS::svc_acct object referenced by the srcsvc column, or false for
+literally specified forwards.
=cut
=item dstsvc_acct
Returns the FS::svc_acct object referenced by the srcsvc column, or false for
-forwards not local to freeside.
+literally specified forwards.
=cut
bin/freeside-expiration-alerter
bin/freeside-queued
bin/freeside-radgroup
-bin/freeside-receivables-report
bin/freeside-reexport
bin/freeside-selfservice-server
bin/freeside-setinvoice
FS/UI/agent.pm
FS/UID.pm
FS/Msgcat.pm
+FS/acct_snarf.pm
FS/agent.pm
FS/agent_type.pm
FS/cust_bill.pm
FS/part_export/bind.pm
FS/part_export/bind_slave.pm
FS/part_export/bsdshell.pm
+FS/part_export/communigate_pro.pm
+FS/part_export/communigate_pro_singledomain.pm
FS/part_export/cp.pm
FS/part_export/cyrus.pm
FS/part_export/domain_shellcommands.pm
FS/svc_acct_pop.pm
FS/svc_broadband.pm
FS/svc_domain.pm
+FS/svc_external.pm
FS/router.pm
FS/type_pkgs.pm
FS/nas.pm
t/session.t
t/svc_acct.t
t/svc_acct_pop.t
+t/svc_broadband.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
$username $password 2>/dev/null
[ -e /usr/local/etc/freeside/dbdef.DBI:Pg:host=localhost\;dbname=$domain ] \
- || ( freeside-setup $username 2>/dev/null; \
- /home/ivan/freeside/bin/populate-msgcat $username )
+ || ( freeside-setup -s $username 2>/dev/null; \
+ /home/ivan/freeside/bin/populate-msgcat $username 2>/dev/null )
use FS::cust_main;
&untaint_argv; #what it sounds like (eww)
-use vars qw($opt_d $opt_v $opt_p);
-getopts("p:d:v");
+use vars qw($opt_d $opt_v $opt_p $opt_s $opt_y);
+getopts("p:d:vsy:");
my $user = shift or die &usage;
adminsuidsetup $user;
#we're at now now (and later).
my($time)= $opt_d ? str2time($opt_d) : $^T;
+$time += $opt_y * 86400 if $opt_y;
my($cust_main,%saw);
foreach $cust_main ( @cust_main ) {
if $error;
}
- my $error = $cust_main->bill( 'time' => $time );
+ my $error = $cust_main->bill( 'time' => $time,
+ 'resetup' => $opt_s, );
warn "Error billing, custnum ". $cust_main->custnum. ": $error" if $error;
$cust_main->apply_payments;
=head1 SYNOPSIS
- freeside-daily [ -d 'date' ] [ -p 'payby' ] [ -v ] user [ custnum custnum ... ]
+ freeside-daily [ -d 'date' ] [ -y days ] [ -p 'payby' ] [ -s ] [ -v ] user [ custnum custnum ... ]
=head1 DESCRIPTION
-d: Pretend it's 'date'. Date is in any format Date::Parse is happy with,
but be careful.
+ -y: In addition to -d, which specifies an absolute date, the -y switch
+ specifies an offset, in days. For example, "-y 15" would increment the
+ "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>)
+ -s: re-charge setup fees
+
-v: enable debugging
user: From the mapsecrets file - see config.html from the base documentation
+++ /dev/null
-#!/usr/bin/perl -Tw
-
-use strict;
-use Date::Parse;
-use Time::Local;
-use Getopt::Std;
-use Text::Template;
-use Net::SMTP;
-use Mail::Header;
-use Mail::Internet;
-use FS::Conf;
-use FS::UID qw(adminsuidsetup);
-use FS::Record qw(qsearch);
-use FS::cust_main;
-
-
-&untaint_argv; #what it sounds like (eww)
-use vars qw($opt_v $opt_p $opt_m $opt_e $opt_t $report_lines $report_template @buf $header);
-getopts("vpmet:"); #switches
-
-#we're at now now (and later).
-my($_date)= $^T;
-
-# Get the current month
-my ($sec,$min,$hour,$mday,$mon,$year) =
- (localtime($_date) )[0,1,2,3,4,5];
-$mon++;
-$year += 1900;
-
-# Login to the database
-my $user = shift or die &usage;
-adminsuidsetup $user;
-
-# Get the needed configuration files
-my $conf = new FS::Conf;
-my $lpr = $conf->config('lpr');
-my $email = $conf->config('email');
-my $smtpmachine = $conf->config('smtpmachine');
-my $mail_sender = $conf->exists('invoice_from') ? $conf->config('invoice_from') :
- 'postmaster';
-my @report_template = $conf->config('report_template')
- or die "cannot load config file report_template";
-$report_lines = 0;
- foreach ( grep /report_lines\(\d+\)/, @report_template ) { #kludgy :/
- /report_lines\((\d+)\)/;
- $report_lines += $1;
-}
-die "no report_lines() functions in template?" unless $report_lines;
-$report_template = new Text::Template (
- TYPE => 'ARRAY',
- SOURCE => [ map "$_\n", @report_template ],
-) or die "can't create new Text::Template object: $Text::Template::ERROR";
-
-
-my(@customers)=qsearch('cust_main',{});
-if (scalar(@customers) == 0)
-{
- exit 1;
-}
-
-# Open print and email pipes
-# $lpr and opt_p for printing
-# $email and opt_m for email
-
-if ($lpr && $opt_p)
-{
- open(LPR, "|$lpr");
-}
-
-if ($email && $opt_m)
-{
- $ENV{MAILADDRESS} = $mail_sender;
- $header = new Mail::Header ( [
- "From: Account Processor",
- "To: $email",
- "Sender: $mail_sender",
- "Reply-To: $mail_sender",
- "Subject: Receivables",
- ] );
-}
-
-my $total = 0;
-
-
-# Now I can start looping
-foreach my $customer (@customers)
-{
- my $custnum = $customer->getfield('custnum');
- my $first = $customer->getfield('first');
- my $last = $customer->getfield('last');
- my $company = $customer->getfield('company');
- my $daytime = $customer->getfield('daytime');
- my $balance = $customer->balance;
-
-
- if ($balance != 0) {
- $total += $balance;
- push @buf, sprintf(qq{%8d %-32.32s %12s %9.2f},
- $custnum,
- $first . " " . $last . " " . $company,
- $daytime,
- $balance);
-
- }
-
-}
-
-push @buf, ('', sprintf(qq{%61s}, "========="), sprintf(qq{%61.2f}, $total));
-
-sub FS::receivables_report::_template::report_lines {
- my $lines = shift;
- map {
- scalar(@buf) ? shift @buf : '' ;
- }
- ( 1 .. $lines );
-}
-
-$FS::receivables_report::_template::title = " R E C E I V A B L E S ";
-$FS::receivables_report::_template::title = $opt_t if $opt_t;
-$FS::receivables_report::_template::page = 1;
-$FS::receivables_report::_template::date = $_date;
-$FS::receivables_report::_template::date = $_date;
-$FS::receivables_report::_template::total_pages =
- int( scalar(@buf) / $report_lines);
-$FS::receivables_report::_template::total_pages++ if scalar(@buf) % $report_lines;
-
-my @report;
-while (@buf) {
- push @report, split("\n",
- $report_template->fill_in( PACKAGE => 'FS::receivables_report::_template' )
- );
- $FS::receivables_report::_template::page++;
-}
-
-if ($opt_v) {
- print map "$_\n", @report;
-}
-if($lpr && $opt_p)
-{
- print LPR map "$_\n", @report;
- print LPR "\f" if $opt_e;
- close LPR || die "Could not close printer: $lpr\n";
-}
-if($email && $opt_m)
-{
- my $message = new Mail::Internet (
- 'Header' => $header,
- 'Body' => [ (@report) ],
- );
- $!=0;
- $message->smtpsend( Host => "$smtpmachine" )
- or die "can't send report to $email via $smtpmachine: $!";
-}
-
-
-# subroutines
-
-sub untaint_argv {
- foreach $_ ( $[ .. $#ARGV ) { #untaint @ARGV
- $ARGV[$_] =~ /^([\w\-\/ \.]*)$/ || die "Illegal argument \"$ARGV[$_]\"";
- $ARGV[$_]=$1;
- }
-}
-
-sub usage {
- die "Usage:\n\n freeside-receivables-report [-v] [-p] [-e] user\n";
-}
-
-=head1 NAME
-
-freeside-receivables-report - Prints or emails outstanding receivables.
-
-=head1 SYNOPSIS
-
- freeside-receivables-report [-v] [-p] [-m] [-e] [-t "title"] user
-
-=head1 DESCRIPTION
-
-Prints or emails outstanding receivables
-
-B<-v>: Verbose - Prints records to STDOUT.
-
-B<-p>: Print to printer lpr as found in the conf directory.
-
-B<-m>: Mail output to user found in the Conf email file.
-
-B<-e>: Print a final form feed to the printer.
-
-B<-t>: supply a title for the top of each page.
-
-user: From the mapsecrets file - see config.html from the base documentation
-
-=head1 VERSION
-
-$Id: freeside-receivables-report,v 1.6 2002-09-09 22:57:34 ivan Exp $
-
-=head1 BUGS
-
-Yes..... Use at your own risk. No guarantees or warrantees of any
-kind apply to this program. Parts of this program are hacked from
-other GNU licensed software created mainly by Ivan Kohler.
-
-This is released under the GNU Public License. See www.gnu.org
-for more information regarding this license.
-
-=head1 SEE ALSO
-
-L<FS::cust_main>, config.html from the base documentation
-
-=head1 AUTHOR
-
-Jeff Finucane <jeff@cmh.net>
-
-based on print-batch by Joel Griffiths <griff@aver-computer.com>
-
-=cut
-
use strict;
use vars qw( $Debug %kids $kids $max_kids $shutdown $log_file $ssh_pid );
+use subs qw( lock_write unlock_write );
use Fcntl qw(:flock);
use POSIX qw(:sys_wait_h setsid);
use IO::Handle;
my $user = shift or die &usage;
my $machine = shift or die &usage;
-my $pid_file = "/var/run/freeside-selfservice-server.$user.pid";
-#my $pid_file = "/var/run/freeside-selfservice-server.$user.pid"; $FS::UID::datasrc not posible, but should include machine name at least, hmm
+my $tag = scalar(@ARGV) ? shift : '';
+
+# $FS::UID::datasrc not posible
+my $pid_file = "/var/run/freeside-selfservice-server.$user.$machine.pid";
+
+my $lock_file = "/usr/local/etc/freeside/selfservice.$machine.writelock";
+open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
&init($user);
my $conf = new FS::Conf;
-if ($conf->exists('selfservice_server-quiet')) {
- $FS::cust_bill::quiet = 1;
- $FS::cust_pkg::quiet = 1;
-}
-
my $clientd = "/usr/local/sbin/freeside-selfservice-clientd"; #better name?
my $warnkids=0;
my($writer,$reader,$error) = (new IO::Handle, new IO::Handle, new IO::Handle);
warn "connecting to $machine\n" if $Debug;
- $ssh_pid = sshopen2($machine,$reader,$writer,$clientd);
+ $ssh_pid = sshopen2($machine,$reader,$writer,$clientd,$tag);
# nstore_fd(\*writer, {'hi'=>'there'});
warn "receiving packet from client\n" if $Debug;
- my $packet = fd_retrieve($reader);
+ my $packet = eval { fd_retrieve($reader); };
+ if ( $@ ) {
+ warn "Storable error receiving packet from client".
+ " (assuming lost connection): $@\n"
+ if $Debug;
+ if ( $ssh_pid ) {
+ warn "sending TERM signal to ssh process $ssh_pid\n" if $Debug;
+ kill 'TERM', $ssh_pid;
+ $ssh_pid = 0;
+ }
+ last;
+ }
warn "packet received\n".
join('', map { " $_=>$packet->{$_}\n" } keys %$packet )
if $Debug > 1;
$rv->{_token} = $packet->{_token}; #identifier
warn "sending response\n" if $Debug;
- flock($writer, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+ lock_write;
nstore_fd($rv, $writer) or die "FATAL: can't send response: $!";
$writer->flush or die "FATAL: can't flush: $!";
- flock($writer, LOCK_UN) or die "WARNING: can't release write lock: $!";
+ unlock_write;
warn "child exiting\n" if $Debug;
exit; #end-of-kid
}
+ warn "connection lost, reconnecting\n" if $Debug;
+ sleep 3;
+
}
###
close $log;
}
+sub lock_write {
+ #broken on freebsd?
+ #flock($writer, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+
+ flock(LOCKFILE, LOCK_EX) or die "FATAL: can't lock $lock_file: $!";
+
+}
+
+sub unlock_write {
+ #broken on freebsd?
+ #flock($writer, LOCK_UN) or die "WARNING: can't release write lock: $!";
+
+ flock(LOCKFILE, LOCK_UN) or die "FATAL: can't unlock $lock_file: $!";
+
+}
+
sub usage {
- die "Usage:\n\n fs_signup_server user machine\n";
+ die "Usage:\n\n freeside-selfservice-server user machine\n";
}
'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'] ],
+ 'index' => [ ['typenum'], ['disabled'] ],
},
'agent_type' => {
],
'primary_key' => 'eventpart',
'unique' => [],
- 'index' => [ ['payby'] ],
+ 'index' => [ ['payby'], ['disabled'], ],
},
'cust_bill_pkg' => {
'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,
'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' => [],
],
'primary_key' => 'paynum',
'unique' => [],
- 'index' => [ [ 'custnum' ], [ 'paybatch' ] ],
+ 'index' => [ [ 'custnum' ], [ 'paybatch' ], [ 'payby' ], [ '_date' ] ],
},
'cust_bill_pay' => {
'pkg', 'varchar', '', $char_d,
'comment', 'varchar', '', $char_d,
'setup', @perl_type,
- 'freq', 'int', '', '', #billing frequency (months)
+ 'freq', 'varchar', '', $char_d, #billing frequency
'recur', @perl_type,
'setuptax', 'char', 'NULL', 1,
'recurtax', 'char', 'NULL', 1,
'pkgpart', 'int', '', '',
'svcpart', 'int', '', '',
'quantity', 'int', '', '',
+ 'primary_svc','char', 'NULL', 1,
],
'primary_key' => '',
'unique' => [ ['pkgpart', 'svcpart'] ],
'columns' => [
'refnum', 'serial', '', '',
'referral', 'varchar', '', $char_d,
+ 'disabled', 'char', 'NULL', 1,
],
'primary_key' => 'refnum',
'unique' => [],
- 'index' => [],
+ 'index' => [ ['disabled'] ],
},
'part_svc' => {
'columns' => [
'svcnum', 'int', '', '',
'username', 'varchar', '', $username_len, #unique (& remove dup code)
- '_password', 'varchar', '', 50, #13 for encryped pw's plus ' *SUSPENDED* (mp5 passwords can be 34)
+ '_password', 'varchar', '', 72, #13 for encryped pw's plus ' *SUSPENDED* (md5 passwords can be 34, blowfish 60)
'sec_phrase', 'varchar', 'NULL', $char_d,
'popnum', 'int', 'NULL', '',
'uid', 'int', 'NULL', '',
'svc_forward' => {
'columns' => [
- 'svcnum', 'int', '', '',
- 'srcsvc', 'int', '', '',
- 'dstsvc', 'int', '', '',
- 'dst', 'varchar', 'NULL', $char_d,
+ 'svcnum', 'int', '', '',
+ 'srcsvc', 'int', 'NULL', '',
+ 'src', 'varchar', 'NULL', 255,
+ 'dstsvc', 'int', 'NULL', '',
+ 'dst', 'varchar', 'NULL', 255,
],
'primary_key' => 'svcnum',
'unique' => [],
'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' => [],
+ },
+
);
%tables;
--- /dev/null
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::acct_snarf;
+$loaded=1;
+print "ok 1\n";
--- /dev/null
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_broadband;
+$loaded=1;
+print "ok 1\n";
--- /dev/null
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::svc_external;
+$loaded=1;
+print "ok 1\n";
DB_USER = freeside
DB_PASSWORD=
-TEMPLATE = asp
-#TEMPLATE = mason
+#TEMPLATE = asp
+TEMPLATE = mason
ASP_GLOBAL = /usr/local/etc/freeside/asp-global
MASON_HANDLER = /usr/local/etc/freeside/handler.pl
MASONDATA = /usr/local/etc/freeside/masondata
-#deb, others?
+#deb
FREESIDE_DOCUMENT_ROOT = /var/www/freeside
+#redhat, mandrake
+#FREESIDE_DOCUMENT_ROOT = /var/www/html/freeside
#freebsd
#FREESIDE_DOCUMENT_ROOT = /usr/local/www/data/freeside
+#openbsd
+#FREESIDE_DOCUMENT_ROOT = /var/www/htdocs/freeside
-#deb, others?
+#deb, redhat, mandrake, others?
INIT_FILE = /etc/init.d/freeside
#freebsd
#INIT_FILE = /usr/local/etc/rc.d/011.freeside.sh
-#deb, others?
+#deb
HTTPD_RESTART = /etc/init.d/apache restart
+#redhat, mandrake
+#HTTPD_RESTART = /etc/init.d/httpd restart
#freebsd
#HTTPD_RESTART = /usr/local/etc/rc.d/apache.sh stop; sleep 1; /usr/local/etc/rc.d/apache.sh start
+#openbsd
+#HTTPD_RESTART = kill -TERM `cat /var/www/logs/httpd.pid`; sleep 1; /usr/sbin/httpd -u -DSSL
FREESIDE_RESTART = ${INIT_FILE} restart
-#deb, others?
+#deb, redhat, mandrake, others?
INSTALLGROUP = root
-#freebsd
+#freebsd, openbsd
#INSTALLGROUP = wheel
#edit the stuff below to have the daemons start
#eventually this shouldn't be needed
FREESIDE_PATH = `pwd`
-PASSWD_USER = nostart
-PASSWD_MACHINE = localhost
-
-SIGNUP_USER = nostart
-SIGNUP_MACHINE = localhost
-SIGNUP_AGENTNUM = 2
-SIGNUP_REFNUM = 2
-
SELFSERVICE_USER = fs_selfservice
-SELFSERVICE_MACHINE = localhost
+SELFSERVICE_MACHINES = localhost
+# SELFSERVICE_MACHINES = web1.example.com web2.example.com
#---
#not changable yet
FREESIDE_CONF = /usr/local/etc/freeside
-VERSION=1.5.0pre3
-TAG=freeside_1_5_0pre3
+VERSION=1.5.0pre4
+TAG=freeside_1_5_0pre4
help:
@echo "supported targets: aspdocs masondocs alldocs docs install-docs"
perl -p -i -e "\
s/%%%QUEUED_USER%%%/${QUEUED_USER}/g;\
s'%%%FREESIDE_PATH%%%'${FREESIDE_PATH}'g;\
- s/%%%PASSWD_USER%%%/${PASSWD_USER}/g;\
- s/%%%PASSWD_MACHINE%%%/${PASSWD_MACHINE}/g;\
- s/%%%SIGNUP_USER%%%/${SIGNUP_USER}/g;\
- s/%%%SIGNUP_MACHINE%%%/${SIGNUP_MACHINE}/g;\
- s/%%%SIGNUP_AGENTNUM%%%/${SIGNUP_AGENTNUM}/g;\
- s/%%%SIGNUP_REFNUM%%%/${SIGNUP_REFNUM}/g;\
s/%%%SELFSERVICE_USER%%%/${SELFSERVICE_USER}/g;\
- s/%%%SELFSERVICE_MACHINE%%%/${SELFSERVICE_MACHINE}/g;\
+ s/%%%SELFSERVICE_MACHINES%%%/${SELFSERVICE_MACHINES}/g;\
" ${INIT_FILE}
install: install-perl-modules install-docs install-init
create-config: install-perl-modules
[ -e ${FREESIDE_CONF} ] && mv ${FREESIDE_CONF} ${FREESIDE_CONF}.`date +%Y%m%d%H%M%S` || true
- mkdir ${FREESIDE_CONF}
- chown freeside ${FREESIDE_CONF}
+ install -d -o freeside ${FREESIDE_CONF}
touch ${FREESIDE_CONF}/secrets
chown freeside ${FREESIDE_CONF}/secrets
mkdir "${FREESIDE_CONF}/conf.${DATASOURCE}"
rm -rf conf/registries #old dirs just won't go away
- cp conf/[a-z]* "${FREESIDE_CONF}/conf.${DATASOURCE}"
+ #cp conf/[a-z]* "${FREESIDE_CONF}/conf.${DATASOURCE}"
+ cp `ls -d conf/[a-z]* | grep -v CVS` "${FREESIDE_CONF}/conf.${DATASOURCE}"
chown -R freeside "${FREESIDE_CONF}/conf.${DATASOURCE}"
mkdir "${FREESIDE_CONF}/counters.${DATASOURCE}"
#these are probably only useful if you're me...
upload-docs: forcehtmlman
- ssh cleanwhisker.420.am rm -rf /var/www/www.sisd.com/freeside/devdocs
- scp -pr httemplate/docs cleanwhisker.420.am:/var/www/www.sisd.com/freeside/devdocs
+ ssh pouncequick.420.am rm -rf /var/www/www.sisd.com/freeside/devdocs
+ scp -pr httemplate/docs pouncequick.420.am:/var/www/www.sisd.com/freeside/devdocs
release: upload-docs
cd /home/ivan/freeside
cvs export -r ${TAG} -d freeside-${VERSION} freeside
tar czvf freeside-${VERSION}.tar.gz freeside-${VERSION}
- scp freeside-${VERSION}.tar.gz ivan@cleanwhisker.420.am:/var/www/sisd.420.am/freeside/
+ scp freeside-${VERSION}.tar.gz ivan@pouncequick.420.am:/var/www/sisd.420.am/freeside/
mv freeside-${VERSION} freeside-${VERSION}.tar.gz ..
update-webdemo:
#!/usr/bin/perl -w
#
-# $Id: bind.import,v 1.3 2002-07-15 01:44:23 ivan Exp $
+# $Id: bind.import,v 1.4 2004-02-12 10:44:11 ivan Exp $
#need to manually put header in /usr/local/etc/freeside/export.<datasrc./bind/<machine>/named.conf.HEADER
##
sub usage {
- die "Usage:\n\n svc_domain.import user\n";
+ die "Usage:\n\n bind.import user\n";
}
########
--- /dev/null
+#!/usr/bin/perl -w
+# this quick hack helps you generate/maintain .fetchmailrc files from
+# FS::acct_snarf data. it is run from a shellcommands export as:
+# create-fetchmailrc $username $dir $snarf_machine1 $snarf_username1 $snarf__password1 $snarf_machine2 $snarf_username2 $snarf__password2 ...
+
+use strict;
+use POSIX qw( setuid setgid );
+
+my $header = <<END;
+# Configuration created by create-fetchmailrc
+set postmaster "postmaster"
+set bouncemail
+set no spambounce
+set properties ""
+set daemon 240
+END
+
+my $username = shift @ARGV or die "no username specified\n";
+my $homedir = shift @ARGV or die "no homedir specified\n";
+my $filename = "$homedir/.fetchmailrc";
+
+my $gid = scalar(getgrnam($username)) or die "can't find $username's gid\n";
+my $uid = scalar(getpwnam($username)) or die "can't find $username's uid\n";
+
+open(FETCHMAILRC, ">$filename") or die "can't open $filename: $!\n";
+chown $uid, $gid, $filename or die "can't chown $uid.$gid $filename: $!\n";
+chmod 0600, $filename or die "can't chmod 600 $filename: $!\n";
+print FETCHMAILRC $header;
+
+while ($ARGV[0]) {
+ my( $s_machine, $s_username, $s_password ) = splice( @ARGV, 0, 3 );
+ print FETCHMAILRC <<END;
+poll $s_machine
+ user '$s_username' there with password '$s_password' is '$username' here
+END
+}
+
+close FETCHMAILRC;
+
+setgid($gid) or die "can't setgid $gid\n";
+setuid($uid) or die "can't setuid $uid\n";
+$ENV{HOME} = $homedir;
+
+system(qq(fetchmail -a -K --antispam "550,451" -d 180 -f $filename));
+
#!/usr/bin/perl -Tw
-#
-# $Id: dbdef-create,v 1.6 2002-09-19 13:34:52 ivan Exp $
use strict;
use DBI;
-use DBIx::DBSchema 0.21;
+use DBIx::DBSchema 0.22;
use FS::UID qw(adminsuidsetup datasrc driver_name);
my $user = shift or die &usage;
while (<>) {
next if /^(#|\s*$|\$INCLUDE\s+)/;
next if /^(VALUE|VENDOR|BEGIN\-VENDOR|END\-VENDOR)\s+/;
- /^(ATTRIBUTE|ATTRIB_NMC)\s+([\w\-]+)\s+/ or die $_;
+ /^(ATTRIBUTE|ATTRIB_NMC)\s+([\w\-\/]+)\s+/ or die $_;
$attrib = $2;
$dbname = lc($2);
- $dbname =~ s/\-/_/g;
+ $dbname =~ s/[\-\/]/_/g;
+ $dbname = substr($dbname,0,24);
+ while ( exists $hash{$dbname} ) {
+ #warn $dbname;
+ $dbname =~ s/(.)$//;
+ my $w = $1;
+ $w =~ tr/_a-z0-9/a-z0-9_/;
+ $dbname = "$dbname$w";
+ }
$hash{$dbname} = $attrib;
#print "$2\n";
}
# print "$_\n" if length($_)>24;
# print substr($_,0,24),"\n" if length($_)>24;
# $max = length($_) if length($_)>$max;
-#everything >24 is still unique, at least with freeradius comprehensive dataset
- print " '". substr($_,0,24). "' => '$hash{$_}',\n";
+# have to fudge things since everything >24 is *not* unique
+
+ #print " '". substr($_,0,24). "' => '$hash{$_}',\n";
+ print " '$_' => '$hash{$_}',\n";
}
print <<END;
#!/usr/bin/perl
-foreach $file ( split(/\n/, `find . -depth -print | grep cgi\$`) ) {
+foreach $file ( split(/\n/, `find . -depth -print`) ) {
+ next unless $file =~ /(cgi|html)$/;
open(F,$file) or die "can't open $file for reading: $!";
@file = <F>;
#print "$file ". scalar(@file). "\n";
system("chmod u+w $file");
open(W,">$file") or die "can't open $file for writing: $!";
select W; $| = 1; select STDOUT;
+ $newline = ''; #avoid prepending extraneous newlines
$all = join('',@file);
$mode = 'html';
#die;
next;
} elsif ( $all =~ /^<%(.*)$/s ) {
- print W "\n";
+ print W $newline; $newline = "\n";
$all = $1;
$mode = 'perlc';
next;
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+#use File::Path;
+use File::Rsync;
+use Net::SSH qw(ssh);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch); # qsearchs);
+use FS::part_export;
+#use FS::cust_pkg;
+use FS::cust_svc;
+#use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+my $spooldir = "/usr/local/etc/freeside/export.". datasrc. "/postfix";
+mkdir $spooldir, 0700 unless -d $spooldir;
+
+my @exports = qsearch('part_export', { 'exporttype' => 'postfix' } );
+
+my $rsync = File::Rsync->new({
+ rsh => 'ssh',
+# dry_run => 1,
+});
+
+foreach my $export ( @exports ) {
+
+ my $machine = $export->machine;
+ my $prefix = "$spooldir/$machine";
+ mkdir $prefix, 0700 unless -d $prefix;
+
+ #construct %domain hash
+
+ my $mydomain = $export->option('mydomain');
+ my %domain;
+ foreach my $svc_forward ( $export->svc_x ) {
+
+ my( $username, $domain );
+ my $srcsvc_acct = $svc_forward->srcsvc_acct;
+ if ( $srcsvc_acct ) {
+ ( $username, $domain ) = ( $srcsvc_acct->username, $srcsvc_acct->domain );
+ } elsif ( $svc_forward->src =~ /([^@]*)\@([^@]+)$/ ) {
+ ( $username, $domain ) = ( $1, $2 );
+ } else {
+ die "bad svc_forward record? svcnum ". $svc_forward->svcnum. "\n";
+ }
+
+ my( $dusername, $ddomain );
+ my $dstsvc_acct = $svc_forward->dstsvc_acct;
+ if ( $dstsvc_acct ) {
+ $dusername = $dstsvc_acct->username;
+ $ddomain = $dstsvc_acct->domain;
+ } elsif ( $svc_forward->dst =~ /([^@]+)\@([^@]+)$/ ) {
+ ( $dusername, $ddomain ) = ( $1, $2 );
+ } else {
+ die "bad svc_forward record? svcnum ". $svc_forward->svcnum. "\n";
+ }
+ my $dest;
+ if ( $ddomain eq $mydomain ) {
+ $dest = $dusername;
+ } else {
+ $dest = "$dusername\@$ddomain";
+ }
+
+ push @{$domain{$domain}{$username}}, $dest;
+
+ }
+
+ #write aliases
+
+ my $aliases = delete $domain{$mydomain};
+ open(ALIASES, ">$prefix/aliases") or die "can't open $prefix/aliases: $!";
+ foreach my $alias ( keys %$aliases ) {
+ print ALIASES "$alias: ". join(',', @{ $aliases->{$alias} } ). "\n";
+ }
+ close ALIASES;
+
+ #write virtual
+
+ open(VIRTUAL, ">$prefix/virtual") or die "can't open $prefix/virtual: $!";
+ foreach my $domain ( keys %domain ) {
+ print VIRTUAL "$domain DOMAIN\n";
+ #foreach my $virtual ( sort { $a ne '' <=> $b ne '' } keys %{$domain{$domain}} ) {
+ foreach my $virtual ( sort { ( ($b ne '') <=> ($a ne '') ) || $a cmp $b } keys %{$domain{$domain}} ) {
+ print VIRTUAL "$virtual\@$domain ".
+ join(',', @{ $domain{$domain}{$virtual} } ). "\n";
+ }
+ print VIRTUAL "\n";
+ }
+ close VIRTUAL;
+
+ #rsync
+
+ my $user = $export->option('user');
+ $rsync->exec( {
+ src => "$prefix/aliases",
+ dest => "$user\@$machine:". $export->option('aliases'),
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+# warn $rsync->out;
+
+ ssh("$user\@$machine", "newaliases");
+# ssh("$user\@$machine", "postfix reload");
+
+ $rsync->exec( {
+ src => "$prefix/virtual",
+ dest => "$user\@$machine:". $export->option('virtual'),
+ } ) or die "rsync to $machine failed: ". join(" / ", $rsync->err);
+# warn $rsync->out;
+ ssh("$user\@$machine", "postmap hash:/etc/postfix/virtual");
+ ssh("$user\@$machine", "postfix reload");
+
+}
+
+# -----
+
+sub usage {
+ die "Usage:\n postfix.export user\n";
+}
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use Term::Query qw(query);
+use Net::SCP qw(iscp);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+##use FS::svc_acct_sm;
+#use FS::svc_domain;
+#use FS::domain_record;
+use FS::svc_acct;
+##use FS::part_svc;
+use FS::svc_forward;
+use FS::svc_domain;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+#$FS::svc_Common::noexport_hack = 1;
+#$FS::domain_record::noserial_hack = 1;
+
+use vars qw($defaultdomain);
+$defaultdomain = 'surferz.net';
+
+use vars qw($svcpart $forward_svcpart);
+$svcpart = 2;
+$forward_svcpart = 4;
+
+use vars qw($spooldir);
+$spooldir = "/usr/local/etc/freeside/export.". datasrc. "/sendmail";
+mkdir $spooldir unless -d $spooldir;
+
+print "\n\n", <<END;
+Enter the location and name of your Sendmail aliases file, for example
+"mail.isp.com:/etc/mail/aliases"
+END
+my($aliases)=&getvalue(":");
+
+use vars qw($aliases_machine $aliases_prefix);
+$aliases_machine = (split(/:/, $aliases))[0];
+$aliases_prefix = "$spooldir/$aliases_machine";
+mkdir $aliases_prefix unless -d $aliases_prefix;
+
+#iscp("root\@$aliases","$aliases_prefix/aliases.import");
+iscp("ivan\@$aliases","$aliases_prefix/aliases.import");
+
+print "\n\n", <<END;
+Enter the location and name of your Sendmail virtusertable directory, for example
+"mail.isp.com:/etc/mail/virtusertable"
+END
+my($virtusertable)=&getvalue(":");
+
+use vars qw($virtusertable_machine $virtusertable_prefix);
+$virtusertable_machine = (split(/:/, $virtusertable))[0];
+$virtusertable_prefix = "$spooldir/$virtusertable_machine";
+mkdir $virtusertable_prefix unless -d $virtusertable_prefix;
+mkdir "$virtusertable_prefix/virtusertable.import"
+ unless -d "$virtusertable_prefix/virtusertable.import";
+
+#iscp("root\@$virtusertable/*","$aliases_prefix/virtusertable.import/");
+iscp("ivan\@$virtusertable/*","$aliases_prefix/virtusertable.import/");
+
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+##
+
+foreach my $file (
+ "$aliases_prefix/aliases.import",
+ glob("$aliases_prefix/virtusertable.import/*"),
+) {
+
+ warn "importing $file\n";
+
+ open(FILE,"<$file") or die $!;
+ while (<FILE>) {
+ next if /^\s*#/ || /^\s*$/; #skip comments & blank lines
+
+ unless ( /^([\w\@\.\-]+)[:\s]\s*(.*\S)\s*$/ ) {
+ warn "Unparsable line: $_";
+ next;
+ }
+ my($rawusername, $rawdest) = ($1, $2);
+
+ my($username, $domain);
+ if ( $rawusername =~ /^([\w\-\.\&]*)\@([\w\.\-]+)$/ ) {
+ $username = $1;
+ $domain = $2;
+ } elsif ( $rawusername =~ /\@/ ) {
+ die "Unparsable username: $rawusername\n";
+ } else {
+ $username = $rawusername;
+ $domain = $defaultdomain;
+ }
+
+ #find svc_acct record or set $src
+ my($srcsvc, $src) = &svcnum_or_literal($username, $domain);
+
+ foreach my $dest ( split(/,/, $rawdest) ) {
+
+ my($dusername, $ddomain);
+ if ( $dest =~ /^([\w\-\.\&]+)\@([\w\.\-]+)$/ ) {
+ $dusername = $1;
+ $ddomain = $2;
+ } elsif ( $dest =~ /\@/ ) {
+ die "Unparsable username: $dest\n";
+ } else {
+ $dusername = $dest;
+ $ddomain = $defaultdomain;
+ }
+ my($dstsvc, $dst) = &svcnum_or_literal($dusername, $ddomain);
+
+ my $svc_forward = new FS::svc_forward ({
+ svcpart => $forward_svcpart,
+ srcsvc => $srcsvc,
+ src => $src,
+ dstsvc => $dstsvc,
+ dst => $dst,
+ });
+ my $error = $svc_forward->insert;
+ #my $error = $svc_forward->check;
+ if ( $error ) {
+ die "$rawusername: $rawdest: $error\n";
+ }
+ }
+
+
+ } #next entry
+
+} #next file
+
+##
+
+sub svcnum_or_literal {
+ my($username, $domain) = @_;
+
+ my $svc_domain = qsearchs('svc_domain', { 'domain' => $domain } );
+ my $domsvc = $svc_domain ? $svc_domain->svcnum : '';
+
+ my @svc_acct = grep { $_->cust_svc->svcpart == $svcpart }
+ qsearch('svc_acct', {
+ 'username' => $username,
+ 'domsvc' => $domsvc,
+ });
+
+ if ( scalar(@svc_acct) > 1 ) {
+ die "multiple sources found for $username\@$domain !\n";
+ }
+
+ my( $svcnum, $literal ) = ('', '');
+ if ( @svc_acct ) {
+ my $svc_acct = $svc_acct[0];
+ $svcnum = $svc_acct->svcnum;
+ } else {
+ $literal = "$username\@$domain";
+ }
+
+ return( $svcnum, $literal );
+
+}
+
+sub usage {
+ die "Usage:\n\n sendmail.import user\n";
+}
+
+
+
+
+
--- /dev/null
+#!/usr/bin/perl -Tw
+# $Id: shadow.reimport,v 1.1 2004-02-03 00:19:45 ivan Exp $
+
+use strict;
+use vars qw(%part_svc);
+use Term::Query qw(query);
+use Net::SCP qw(iscp);
+use FS::UID qw(adminsuidsetup datasrc);
+use FS::Record qw(qsearch qsearchs);
+use FS::svc_acct;
+use FS::part_svc;
+
+my $user = shift or die &usage;
+adminsuidsetup $user;
+
+push @FS::svc_acct::shells, qw(/bin/sync /sbin/shutdown /bin/halt /sbin/halt); #others?
+
+my($spooldir)="/usr/local/etc/freeside/export.". datasrc;
+
+#$FS::svc_acct::nossh_hack = 1;
+$FS::svc_Common::noexport_hack = 1;
+
+###
+
+%part_svc=map { $_->svcpart, $_ } qsearch('part_svc',{'svcdb'=>'svc_acct'});
+
+die "No services with svcdb svc_acct!\n" unless %part_svc;
+
+print "\n\n", &menu_svc, "\n", <<END;
+Enter part number to import.
+END
+my($shell_svcpart)=&getpart;
+
+print "\n\n", <<END;
+Enter the location and name of your _user_ shadow file, for example
+"mail.isp.com:/etc/shadow" or "bsd.isp.com:/etc/master.passwd"
+END
+my($loc_shadow)=&getvalue(":");
+iscp("root\@$loc_shadow", "$spooldir/shadow.import");
+
+sub menu_svc {
+ ( join "\n", map "$_: ".$part_svc{$_}->svc, sort keys %part_svc ). "\n";
+}
+sub getpart {
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query "Enter part number:", 'irk', [ keys %part_svc ];
+ $^W=1;
+ $return;
+}
+sub getvalue {
+ my $prompt = shift;
+ $^W=0; # Term::Query isn't -w-safe
+ my $return = query $prompt, '';
+ $^W=1;
+ $return;
+}
+
+print "\n\n";
+
+###
+
+open(SHADOW,"<$spooldir/shadow.import");
+
+my($line, $updated);
+while (<SHADOW>) {
+ $line++;
+ chop;
+ my($username,$password)=split(/:/);
+
+ my @svc_acct = grep { $_->cust_svc->svcpart == $shell_svcpart }
+ qsearch('svc_acct', { 'username' => $username } );
+
+ next unless @svc_acct;
+
+ if ( scalar(@svc_acct) > 1 ) {
+ warn "more than one $username found!\n";
+ next;
+ }
+
+ my $svc_acct = shift @svc_acct;
+
+ next if $svc_acct->_password eq $password;
+
+ my $new_svc_acct = new FS::svc_acct( { $svc_acct->hash } );
+ $new_svc_acct->_password($password);
+ #my $error = $new_svc_acct->replace($svc_acct);
+ #die "$username: $error" if $error;
+
+ $updated++;
+
+}
+
+warn "$updated of $line passwords changed\n";
+
+sub usage {
+ die "Usage:\n\n shadow.reimport user\n";
+}
+
--- /dev/null
+%% file: Standard Multipage.tex\r
+%% Purpose: Multipage bill template for e-Bills\r
+%% \r
+%% Created by Mark Asplen-Taylor\r
+%% Asplen Management Ltd\r
+%% www.asplen.co.uk\r
+%%\r
+%% Modified for Freeside by Ivan Kohler\r
+%%\r
+%% Changes\r
+%% 0.1 4/12/00 Created\r
+%% 0.2 18/10/01 More fields added\r
+%% 1.0 16/11/01 RELEASED\r
+%% 1.2 16/10/02 Invoice number added\r
+%% 1.3 2/12/02 Logo graphic added\r
+%% 1.4 7/2/03 Multipage headers/footers added\r
+%% n/a 10/12/03 forked for Freeside; checked into CVS\r
+%%\r
+\r
+\documentclass[letterpaper]{article}\r
+\r
+\usepackage{fancyhdr,lastpage,ifthen,longtable,afterpage}\r
+\usepackage{graphicx} % required for logo graphic\r
+\r
+\addtolength{\voffset}{-0.0in} % top margin to top of header\r
+\addtolength{\hoffset}{-0.60in} %left margin on page\r
+\addtolength{\topmargin}{-0.6in} % top margin to top of header\r
+\setlength{\headheight}{1in} % height of header\r
+\setlength{\headsep}{0.5in} % between header and text\r
+\addtolength{\textheight}{-0.4in} % height of main text\r
+\r
+\addtolength{\textheight}{-0.5in} % height of main text\r
+\setlength{\footskip}{0.5in} % bottom of footer from bottom of text\r
+\r
+\addtolength{\textwidth}{2.1in} % width of text\r
+\setlength{\oddsidemargin}{0in} % odd page left margin\r
+\setlength{\evensidemargin}{0in} % even page left margin\r
+\r
+\renewcommand{\headrulewidth}{0pt}\r
+\renewcommand{\footrulewidth}{1pt}\r
+\r
+ % New command for address lines i.e. skip them if blank\r
+\r
+\newcommand{\addressline}[1]{\ifthenelse{\equal{#1}{}}{}{#1\newline}}\r
+\newcommand{\dollar}[1][]{\symbol{36}} % Inserts dollar symbol\r
+\r
+\pagestyle{fancy}\r
+\r
+%% Font options are:\r
+%% bch Bitsream Charter\r
+%% put Utopia\r
+%% phv Adobe Helvetica\r
+%% pnc New Century Schoolbook\r
+%% ptm Times\r
+%% pcr Courier\r
+\r
+\renewcommand{\familydefault}{phv} \r
+\r
+\begin{document}\r
+%\r
+%% Headers and footers defined for the first page\r
+\fancyfoot[CO,CE]{\small{\r
+\begin{tabular}{c}\r
+$footer\r
+\end{tabular}}}\r
+%\r
+%% The LH Heading comprising logo\r
+%% UNCOMMENT the following FOUR lines and change the path if necssary to provide a logo\r
+\fancyhead[LO,LE]{\r
+\begin{tabular}{l}\r
+\includegraphics{/usr/local/etc/freeside/logo.eps}\r
+\end{tabular}}\r
+%\r
+%% The Heading comprising isue date, customer ref & INVOICE name\r
+\fancyhead[RO,RE]{\r
+\begin{tabular}{rcl}\r
+Invoice date & & Invoice number \\\r
+\vspace{0.2cm}\r
+\textbf{$date} & & \textbf{$invnum} \\\hline\r
+\rule{0pt}{5ex} &~~ \huge{\textsc{Invoice}}& \\\r
+\vspace{-0.2cm}\r
+ & & \\\hline\r
+\end{tabular}}\r
+%\r
+%% Header & footer changes for subsequent pages\r
+%\r
+\afterpage{ \fancyfoot[RO,RE]{\small{\thepage\ of \pageref{LastPage}}} }\r
+\afterpage{ \fancyfoot[CO,CE]{\small{$smallfooter}} }\r
+\afterpage{ \fancyhead[LO,LE]{\small{}} }\r
+\afterpage{ \fancyhead[RO,RE]{\small{\r
+\begin{tabular}{ll}\r
+Invoice date & Invoice number\\\r
+\textbf{$date} & \textbf{$invnum}\\\r
+\end{tabular}}} }\r
+%\r
+%\r
+\makebox{\r
+\begin{minipage}[t]{2.9in}\r
+\vspace{0.20in}\r
+\textbf{$payname}\\\r
+\addressline{$company}\r
+\addressline{$address1}\r
+\addressline{$address2}\r
+\addressline{$city, $state $zip}\r
+\addressline{$country}\r
+\end{minipage}}\r
+\hfill\r
+\makebox{\r
+\begin{minipage}[t]{2.5in}\r
+\begin{flushright}\r
+Terms: $terms\\\r
+$po_line\\\r
+\end{flushright}\r
+\end{minipage}}\r
+\vspace{0.5cm}\r
+%\r
+\section*{\textsc{Charges}}\r
+\begin{longtable}{|c|l|c|r|r|}\r
+\hline\r
+\rule{0pt}{2.5ex}\r
+\makebox[1.4cm]{\textbf{Ref}} & \r
+\makebox[7.9cm][l]{\textbf{Description}} & \r
+\makebox[1.3cm][c]{\textbf{Quantity}} & \r
+\makebox[2.5cm][r]{\textbf{Unit Price}} & \r
+\makebox[2.5cm][r]{\textbf{Amount}} \\\r
+\hline\r
+\endfirsthead\r
+\multicolumn{5}{r}{\rule{0pt}{2.5ex}Continued from previous page}\\\r
+\hline\r
+\rule{0pt}{2.5ex}\r
+\makebox[1.4cm]{\textbf{Ref}} & \r
+\makebox[7.9cm][l]{\textbf{Description}} & \r
+\makebox[1.3cm][c]{\textbf{Quantity}} & \r
+\makebox[2.5cm][r]{\textbf{Unit Price}} & \r
+\makebox[2.5cm][r]{\textbf{Amount}} \\\r
+\hline\r
+\endhead\r
+\multicolumn{5}{r}{\rule{0pt}{2.5ex}/cont...}\\\r
+\endfoot\r
+%%TotalDetails\r
+ & \multicolumn{3}{l}{$total_item} & $total_amount\\\r
+%%EndTotalDetails\r
+\hline\r
+\endlastfoot\r
+%%Detail\r
+\rule{0pt}{2.5ex}$ref & \r
+\begin{tabular}{l}\r
+$description\tabularnewline\r
+\end{tabular}\r
+& $quantity & \dollar $amount & \dollar $amount\\\hline\r
+%%EndDetail\r
+\end{longtable}\r
+\vfill\r
+$notes\r
+\end{document}\r
--- /dev/null
+Ivan Kohler\\
+P.O. Box 1272\\
+Carnelian Bay, CA~~96140\\
+ivan@sisd.com~~~~+1 415 462 1624\\
+Freeside - open-source billing - http://www.sisd.com/freeside\\
--- /dev/null
+%%
+%% Add any customer specific notes in here
+%%
+\section*{\textsc{Notes}}
+\begin{enumerate}
+\item PLEASE NOTE NEW ADDRESS BELOW!
+\item Please make your check payable to \textbf{Ivan Kohler}.
+\item If you have any questions please email or telephone.
+\end{enumerate}
--- /dev/null
+Ivan Kohler~~~Freeside - open-source billing
Package: freeside
Architecture: any
Depends: freeside-lib
-Recommends: freeside-doc, freeside-ui-web, libterm-query-perl
-Suggests: freeside-passwd-server, freeside-signup-server, freeside-session-server, freeside-selfservice-server
+Recommends: freeside-doc, freeside-ui-web
+Suggests: freeside-selfservice-server
Description: Billing and administration package for ISPs.
Freeside is a billing and account administration package for ISPs. It stores
customer information in an SQL database, and will update UNIX passwd and
This package contains the libraries which implement the business logic and
backend functions of Freeside, a billing and account administration package
for ISPs. This package also contains the manual pages for the library API.
+ (? like a libmodule-perl package)
Package: freeside-ui-web
Architecture: all
-Depends: libstring-approx-perl, freeside-lib, libapache-mod-perl|apache-perl
+Depends: libhtml-mason-perl, libstring-approx-perl, freeside-lib, libapache-mod-perl|apache-perl
Suggests: libapache-mod-ssl|apache-ssl
Description: Easy-to-use web interface for Freeside
This package contains the web interface for Freeside, a billing and account
administration package for ISPs. This is what sales or support folks will
typically use to add new accounts, edit exiting accounts and so on.
-Package: freeside-passwd-server
-Architecture: all
-Depends: freeside-lib
-Description: Freeside password server
- This component of Freeside, a billing and account administration package for
- ISPs,
-
-Package: freeside-passwd-client
-Architecture: all
-Depends:
-Description:
- <rar>
-
-Package: freeside-signup-server
-Architecture: all
-Depends: freeside-lib
-Description:
- <rar>
-
-Package: freeside-signup-client
-Architecture: all
-Depends:
-Description:
- <rar>
-
-Package: freeside-signup-client-webui
-Architecture: all
-Depends: freeside-signup-client-lib, httpd
-Description:
- <rar>
-
-Package: freeside-session-server
+Package: freeside-selfservice-server
Architecture: all
-Depends: freeside-lib
+Depends: freeside-lib, libnet-ssh-perl, ssh
Description:
- <rar>
+ This package contains the server side of the customer self-service interface.
+ It is installed on a private backend machine, and opens an outgoing ssh
+ connection to one or more public web server(s).
-Package: freeside-session-client
+Package: freeside-selfservice-client
Architecture: all
-Depends: ssh
-Description:
- <rar>
-
-Package: freeside-selfservice-server
-Architecture: all
-Depends:
+Depends: libstorable-perl, libhttp-browserdetect-perl, libbusiness-creditcard-perl, ssh
Description:
- <rar>
-
+ This package contains the client side of the customer self-service interface.
+ It is typically installed on a public webserver and interfaces with
+ freeside-selfservice-server installed on a private backend machine.
use FS::svc_Common;
use FS::cust_svc;
-@ISA = qw(svc_Common);
+@ISA = qw(FS::svc_Common);
=head1 NAME
my $part_svc = $x;
- ''; #no error
+ $self->SUPER::check;
}
=back
'INSTALLSCRIPT' => '/usr/local/sbin',
'INSTALLSITEBIN' => '/usr/local/sbin',
'PERM_RWX' => '750',
- 'PREREQ_PM' => {}, # e.g., Module::Name => 1.1
+ 'PREREQ_PM' => {
+ 'Storable' => 0,
+ }, # e.g., Module::Name => 1.1
($] >= 5.005 ? ## Add these new keywords supported since 5.005
(ABSTRACT_FROM => 'SelfService.pm', # retrieve abstract from module
AUTHOR => 'Ivan Kohler <ivan-freeside-selfservice@420.am>') : ()),
package FS::SelfService;
use strict;
-use vars qw($VERSION @ISA @EXPORT_OK $socket %autoload );
+use vars qw($VERSION @ISA @EXPORT_OK $socket %autoload $tag);
use Exporter;
use Socket;
use FileHandle;
@ISA = qw( Exporter );
$socket = "/usr/local/freeside/selfservice_socket";
+$socket .= '.'.$tag if defined $tag && length($tag);
+#maybe should ask ClientAPI for this list
%autoload = (
'passwd' => 'passwd/passwd',
'chfn' => 'passwd/passwd',
'chsh' => 'passwd/passwd',
'login' => 'MyAccount/login',
'customer_info' => 'MyAccount/customer_info',
+ 'edit_info' => 'MyAccount/edit_info',
'invoice' => 'MyAccount/invoice',
'cancel' => 'MyAccount/cancel',
'payment_info' => 'MyAccount/payment_info',
'process_payment' => 'MyAccount/process_payment',
+ 'list_pkgs' => 'MyAccount/list_pkgs',
+ 'order_pkg' => 'MyAccount/order_pkg',
+ 'cancel_pkg' => 'MyAccount/cancel_pkg',
+ 'signup_info' => 'Signup/signup_info',
+ 'new_customer' => 'Signup/new_customer',
);
@EXPORT_OK = keys %autoload;
my $freeside_uid = scalar(getpwnam('freeside'));
die "not running as the freeside user\n" if $> != $freeside_uid;
-=head1 NAME
-
-FS::SelfService - Freeside self-service API
-
-=head1 SYNOPSIS
-
-=head1 DESCRIPTION
-
-Use this API to implement your own client "self-service" module.
-
-If you just want to customize the look of the existing "self-service" module,
-see XXXX instead.
-
-=head1 FUNCTIONS
-
-=over 4
-
-=item passwd
-
-Returns the empty value on success, or an error message on errors.
-
-=cut
-
foreach my $autoload ( keys %autoload ) {
my $eval =
$return;
}
+=head1 NAME
+
+FS::SelfService - Freeside self-service API
+
+=head1 SYNOPSIS
+
+ # password and shell account changes
+ use FS::SelfService qw(passwd chfn chsh);
+
+ # "my account" functionality
+ use FS::SelfService qw( login customer_info invoice cancel payment_info process_payment );
+
+ my $rv = login( { 'username' => $username,
+ 'domain' => $domain,
+ 'password' => $password,
+ }
+ );
+
+ if ( $rv->{'error'} ) {
+ #handle login error...
+ } else {
+ #successful login
+ my $session_id = $rv->{'session_id'};
+ }
+
+ my $customer_info = customer_info( { 'session_id' => $session_id } );
+
+ #payment_info and process_payment are available in 1.5+ only
+ my $payment_info = payment_info( { 'session_id' => $session_id } );
+
+ #!!! process_payment example
+
+ #!!! list_pkgs example
+
+ #!!! order_pkg example
+
+ #!!! cancel_pkg example
+
+ # signup functionality
+ use FS::SelfService qw( signup_info new_customer );
+
+ my $signup_info = signup_info;
+
+ $rv = new_customer( {
+ 'first' => $first,
+ 'last' => $last,
+ 'company' => $company,
+ 'address1' => $address1,
+ 'address2' => $address2,
+ 'city' => $city,
+ 'state' => $state,
+ 'zip' => $zip,
+ 'country' => $country,
+ 'daytime' => $daytime,
+ 'night' => $night,
+ 'fax' => $fax,
+ 'payby' => $payby,
+ 'payinfo' => $payinfo,
+ 'paycvv' => $paycvv,
+ 'paydate' => $paydate,
+ 'payname' => $payname,
+ 'invoicing_list' => $invoicing_list,
+ 'referral_custnum' => $referral_custnum,
+ 'pkgpart' => $pkgpart,
+ 'username' => $username,
+ '_password' => $password,
+ 'popnum' => $popnum,
+ 'agentnum' => $agentnum,
+ }
+ );
+
+ my $error = $rv->{'error'};
+ if ( $error eq '_decline' ) {
+ print_decline();
+ } elsif ( $error ) {
+ reprint_signup();
+ } else {
+ print_success();
+ }
+
+=head1 DESCRIPTION
+
+Use this API to implement your own client "self-service" module.
+
+If you just want to customize the look of the existing "self-service" module,
+see XXXX instead.
+
+=head1 PASSWORD, GECOS, SHELL CHANGING FUNCTIONS
+
+=over 4
+
+=item passwd
+
+=item chfn
+
+=item chsh
+
+=back
+
+=head1 "MY ACCOUNT" FUNCTIONS
+
+=over 4
+
+=item login HASHREF
+
+Creates a user session. Takes a hash reference as parameter with the
+following keys:
+
+=over 4
+
+=item username
+
+=item domain
+
+=item password
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors.
+
+=item session_id
+
+Session identifier for successful logins
+
+=back
+
+=item customer_info HASHREF
+
+Returns general customer information.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item name
+
+Customer name
+
+=item balance
+
+Balance owed
+
+=item open
+
+Array reference of hash references of open inoices. Each hash reference has
+the following keys: invnum, date, owed
+
+=item small_custview
+
+An HTML fragment containing shipping and billing addresses.
+
+=item The following fields are also returned: first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax
+
+=back
+
+=item edit_info HASHREF
+
+Takes a hash reference as parameter with any of the following keys:
+
+first last company address1 address2 city county state zip country daytime night fax ship_first ship_last ship_company ship_address1 ship_address2 ship_city ship_state ship_zip ship_country ship_daytime ship_night ship_fax
+
+If a field exists, the customer record is updated with the new value of that
+field. If a field does not exist, that field is not changed on the customer
+record.
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors
+
+=item invoice HASHREF
+
+Returns an invoice. Takes a hash reference as parameter with two keys:
+session_id and invnum
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item invnum
+
+Invoice number
+
+=item invoice_text
+
+Invoice text
+
+=back
+
+=item cancel HASHREF
+
+Cancels this customer.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference with a single key, B<error>, which is empty on
+success or an error message on errors.
+
+=item payment_info HASHREF
+
+Returns information that may be useful in displaying a payment page.
+
+Takes a hash reference as parameter with a single key: B<session_id>.
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error
+
+Empty on success, or an error message on errors
+
+=item balance
+
+Balance owed
+
+=item payname
+
+Exact name on credit card (CARD/DCRD)
+
+=item address1
+
+=item address2
+
+=item city
+
+=item state
+
+=item zip
+
+=item payby
+
+Customer's current default payment type.
+
+=item card_type
+
+For CARD/DCRD payment types, the card type (Visa card, MasterCard, Discover card, American Express card, etc.)
+
+=item payinfo
+
+For CARD/DCRD payment types, the card number
+
+=item month
+
+For CARD/DCRD payment types, expiration month
+
+=item year
+
+For CARD/DCRD payment types, expiration year
+
+=item cust_main_county
+
+County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>). Note these are not FS::cust_main_county objects, but hash references of columns and values.
+
+=item states
+
+Array reference of all states in the current default country.
+
+=item card_types
+
+Hash reference of card types; keys are card types, values are the exact strings
+passed to the process_payment function
+
+=item paybatch
+
+Unique transaction identifier (prevents multiple charges), passed to the
+process_payment function
+
+=back
+
+=item process_payment HASHREF
+
+Processes a payment and possible change of address or payment type. Takes a
+hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item save
+
+If true, address and card information entered will be saved for subsequent
+transactions.
+
+=item auto
+
+If true, future credit card payments will be done automatically (sets payby to
+CARD). If false, future credit card payments will be done on-demand (sets
+payby to DCRD). This option only has meaning if B<save> is set true.
+
+=item payname
+
+=item address1
+
+=item address2
+
+=item city
+
+=item state
+
+=item zip
+
+=item payinfo
+
+Card number
+
+=item month
+
+Card expiration month
+
+=item year
+
+Card expiration year
+
+=item paybatch
+
+Unique transaction identifier, returned from the payment_info function.
+Prevents multiple charges.
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors
+
+=item list_pkgs
+
+Returns package information for this customer.
+
+Takes a hash reference as parameter with a single key: B<session_id>
+
+Returns a hash reference containing customer package information. The hash reference contains the following keys:
+
+=over 4
+
+=item cust_pkg HASHREF
+
+Array reference of hash references, each of which has the fields of a cust_pkg record (see L<FS::cust_pkg>). Note these are not FS::cust_pkg objects, but hash references of columns and values.
+
+=back
+
+=item order_pkg
+
+Orders a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item pkgpart
+
+=item svcpart
+
+optional svcpart, required only if the package definition does not contain
+one svc_acct service definition with quantity 1 (it may contain others with
+quantity >1)
+
+=item username
+
+=item _password
+
+=item sec_phrase
+
+=item popnum
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors. The special error '_decline' is returned for
+declined transactions.
+
+=item cancel_pkg
+
+Cancels a package for this customer.
+
+Takes a hash reference as parameter with the following keys:
+
+=over 4
+
+=item session_id
+
+=item pkgpart
+
+=back
+
+Returns a hash reference with a single key, B<error>, empty on success, or an
+error message on errors.
+
+=back
+
+=head1 SIGNUP FUNCTIONS
+
+=over 4
+
+=item signup_info
+
+Returns a hash reference containing information that may be useful in
+displaying a signup page. The hash reference contains the following keys:
+
+=over 4
+
+=item cust_main_county
+
+County/state/country data - array reference of hash references, each of which has the fields of a cust_main_county record (see L<FS::cust_main_county>). Note these are not FS::cust_main_county objects, but hash references of columns and values.
+
+=item part_pkg
+
+Available packages - array reference of hash references, each of which has the fields of a part_pkg record (see L<FS::part_pkg>). Each hash reference also has an additional 'payby' field containing an array reference of acceptable payment types specific to this package (see below and L<FS::part_pkg/payby>). Note these are not FS::part_pkg objects, but hash references of columns and values. Requires the 'signup_server-default_agentnum' configuration value to be set.
+
+=item agent
+
+Array reference of hash references, each of which has the fields of an agent record (see L<FS::agent>). Note these are not FS::agent objects, but hash references of columns and values.
+
+=item agentnum2part_pkg
+
+Hash reference; keys are agentnums, values are array references of available packages for that agent, in the same format as the part_pkg arrayref above.
+
+=item svc_acct_pop
+
+Access numbers - array reference of hash references, each of which has the fields of an svc_acct_pop record (see L<FS::svc_acct_pop>). Note these are not FS::svc_acct_pop objects, but hash references of columns and values.
+
+=item security_phrase
+
+True if the "security_phrase" feature is enabled
+
+=item payby
+
+Array reference of acceptable payment types for signup
+
+=over 4
+
+=item CARD (credit card - automatic)
+
+=item DCRD (credit card - on-demand - version 1.5+ only)
+
+=item CHEK (electronic check - automatic)
+
+=item DCHK (electronic check - on-demand - version 1.5+ only)
+
+=item LECB (Phone bill billing)
+
+=item BILL (billing, not recommended for signups)
+
+=item COMP (free, definately not recommended for signups)
+
+=item PREPAY (special billing type: applies a credit (see FS::prepay_credit) and sets billing type to BILL)
+
+=back
+
+=item cvv_enabled
+
+True if CVV features are available (1.5+ or 1.4.2 with CVV schema patch)
+
+=item msgcat
+
+Hash reference of message catalog values, to support error message customization. Currently available keys are: passwords_dont_match, invalid_card, unknown_card_type, and not_a (as in "Not a Discover card"). Values are configured in the web interface under "View/Edit message catalog".
+
+=item statedefault
+
+Default state
+
+=item countrydefault
+
+Default country
+
+=back
+
+=item new_customer HASHREF
+
+Creates a new customer. Takes a hash reference as parameter with the
+following keys:
+
+=over 4
+
+=item first - first name (required)
+
+=item last - last name (required)
+
+=item ss (not typically collected; mostly used for ACH transactions)
+
+=item company
+
+=item address1 (required)
+
+=item address2
+
+=item city (required)
+
+=item county
+
+=item state (required)
+
+=item zip (required)
+
+=item daytime - phone
+
+=item night - phone
+
+=item fax - phone
+
+=item payby - CARD, DCRD, CHEK, DCHK, LECB, BILL, COMP or PREPAY (see L</signup_info> (required)
+
+=item payinfo - Card number for CARD/DCRD, account_number@aba_number for CHEK/DCHK, prepaid "pin" for PREPAY, purchase order number for BILL
+
+=item paycvv - Credit card CVV2 number (1.5+ or 1.4.2 with CVV schema patch)
+
+=item paydate - Expiration date for CARD/DCRD
+
+=item payname - Exact name on credit card for CARD/DCRD, bank name for CHEK/DCHK
+
+=item invoicing_list - comma-separated list of email addresses for email invoices. The special value 'POST' is used to designate postal invoicing (it may be specified alone or in addition to email addresses),
+
+=item referral_custnum - referring customer number
+
+=item pkgpart - pkgpart of initial package
+
+=item username
+
+=item _password
+
+=item sec_phrase - security phrase
+
+=item popnum - access number (index, not the literal number)
+
+=item agentnum - agent number
+
+=back
+
+Returns a hash reference with the following keys:
+
+=over 4
+
+=item error Empty on success, or an error message on errors. The special error '_decline' is returned for declined transactions; other error messages should be suitable for display to the user (and are customizable in under Sysadmin | View/Edit message catalog)
+
+=back
+
+
=back
=head1 BUGS
<TR>
<TH ALIGN="right">Username </TH>
<TD>
- <!-- <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"> -->
- <INPUT TYPE="text" NAME="username" VALUE="hslink">
+ <INPUT TYPE="text" NAME="username" VALUE="<%= $username %>">
</TD>
</TR>
<TR>
<TH ALIGN="right">Domain </TH>
<TD>
- <!-- <INPUT TYPE="text" NAME="domain" VALUE="<%= $domain %>"> -->
- <INPUT TYPE="text" NAME="domain" VALUE="pobox.com">
+ <INPUT TYPE="text" NAME="domain" VALUE="<%= $domain %>">
</TD>
</TR>
<!--<INPUT TYPE="hidden" NAME="domain" VALUE="myisp.com">-->
<TR>
<TH ALIGN="right">Password </TH>
<TD>
- <!-- <INPUT TYPE="password" NAME="password"> -->
- <INPUT TYPE="password" NAME="password" VALUE="UwjM5zdb">
+ <INPUT TYPE="password" NAME="password">
</TD>
</TR>
</TABLE>
# This is run REMOTELY over ssh by freeside-selfservice-server
use strict;
-use subs qw(spawn logmsg);
+use subs qw(spawn logmsg lock_write unlock_write);
use Fcntl qw(:flock);
use POSIX qw(:sys_wait_h);
use Socket;
#STDOUT->setbuf('');
+my $tag = scalar(@ARGV) ? '.'.shift : '';
+
use vars qw( $Debug );
-$Debug = 3; #2 will turn on child logging, 3 will log packet contents,
+$Debug = 2; #2 will turn on child logging, 3 will log packet contents,
#including potentially compromising information
-my $socket = "/usr/local/freeside/selfservice_socket";
+my $socket = "/usr/local/freeside/selfservice_socket$tag";
my $pid_file = "$socket.pid";
-my $log_file = "/usr/local/freeside/selfservice.log";
+my $log_file = "/usr/local/freeside/selfservice$tag.log";
+
+my $lock_file = "/usr/local/freeside/selfservice$tag.writelock";
#my $me = '[client]';
#warn "$me Reading init data\n" if $Debug;
#my $signup_init =
+warn "Creating $lock_file\n" if $Debug;
+open(LOCKFILE,">$lock_file") or die "can't open $lock_file: $!";
+
warn "Creating $socket\n" if $Debug;
my $uaddr = sockaddr_un($socket);
my $proto = getprotobyname('tcp');
#handle some commands weirdly?
$packet->{_token}=$$;
- warn "[child-$$] sending packet to remote server" if $Debug > 1;
- flock(STDOUT, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+ warn "[child-$$] locking write stream\n" if $Debug > 1;
+ lock_write;
+
+ warn "[child-$$] sending packet to remote server\n" if $Debug > 1;
nstore_fd($packet, \*STDOUT) or die "FATAL: can't send response: $!";
+
+ warn "[child-$$] flushing write stream\n" if $Debug > 1;
STDOUT->flush or die "FATAL: can't flush: $!";
- flock(STDOUT, LOCK_UN) or die "FATAL: can't release write lock: $!";
+
+ warn "[child-$$] releasing write lock\n" if $Debug > 1;
+ unlock_write;
+
+ warn "[child-$$] closing write stream\n" if $Debug > 1;
close STDOUT or die "FATAL: can't close write stream: $!"; #??!
- warn "[child-$$] waiting for response from parent" if $Debug > 1;
+ warn "[child-$$] waiting for response from parent\n" if $Debug > 1;
my $w = new IO::Select;
$w->add(\*STDIN);
until ( $w->can_read ) {
flock($log, LOCK_UN);
close $log;
}
+
+sub lock_write {
+ #broken on freebsd?
+ #flock(STDOUT, LOCK_EX) or die "FATAL: can't lock write stream: $!";
+
+ flock(LOCKFILE, LOCK_EX) or die "FATAL: can't lock $lock_file: $!";
+}
+
+sub unlock_write {
+ #broken on freebsd?
+ #flock(STDOUT, LOCK_UN) or die "FATAL: can't release write lock: $!";
+
+ flock(LOCKFILE, LOCK_UN) or die "FATAL: can't unlock $lock_file: $!";
+}
'PREREQ_PM' => {
'Business::CreditCard' => 0,
'HTTP::BrowserDetect' => 0,
- 'HTTP::Headers::UserAgent' => 3,
- 'Storable' => 0,
'Text::Template' => 0,
+ 'FS::SelfService' => 0,
},
);
package FS::SignupClient;
use strict;
-use vars qw($VERSION @ISA @EXPORT_OK $fs_signupd_socket);
+use vars qw($VERSION @ISA @EXPORT_OK); # $fs_signupd_socket);
use Exporter;
-use Socket;
-use FileHandle;
-use IO::Handle;
-use Storable qw(nstore_fd fd_retrieve);
+#use Socket;
+#use FileHandle;
+#use IO::Handle;
+#use Storable qw(nstore_fd fd_retrieve);
+use FS::SelfService; # qw( new_customer signup_info );
-$VERSION = '0.03';
+$VERSION = '0.04';
@ISA = qw( Exporter );
@EXPORT_OK = qw( signup_info new_customer );
-$fs_signupd_socket = "/usr/local/freeside/fs_signupd_socket";
-
-$ENV{'PATH'} ='/usr/bin:/usr/ucb:/bin';
-$ENV{'SHELL'} = '/bin/sh';
-$ENV{'IFS'} = " \t\n";
-$ENV{'CDPATH'} = '';
-$ENV{'ENV'} = '';
-$ENV{'BASH_ENV'} = '';
-
-my $freeside_uid = scalar(getpwnam('freeside'));
-die "not running as the freeside user\n" if $> != $freeside_uid;
-
=head1 NAME
FS::SignupClient - Freeside signup client API
use FS::SignupClient qw( signup_info new_customer );
- ( $locales, $packages, $pops ) = signup_info;
+ #this is the backwards-compatibility bit
+ ( $locales, $packages, $pops, $real_signup_info ) = signup_info;
+ #this is compatible with FS::SelfService::new_customer
$error = new_customer ( {
'first' => $first,
'last' => $last,
'fax' => $fax,
'payby' => $payby,
'payinfo' => $payinfo,
+ 'paycvv' => $paycvv,
'paydate' => $paydate,
'payname' => $payname,
'invoicing_list' => $invoicing_list,
=cut
+#compatibility bit
sub signup_info {
- socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
- connect(SOCK, sockaddr_un($fs_signupd_socket)) or die "connect: $!";
- print SOCK "signup_info\n";
- SOCK->flush;
- my $init_data = fd_retrieve(\*SOCK);
- close SOCK;
+ my $init_data = FS::SelfService::signup_info();
(map { $init_data->{$_} } qw( cust_main_county part_pkg svc_acct_pop ) ),
$init_data;
fax
payby
payinfo
+ paycvv
paydate
payname
invoicing_list
=cut
-sub new_customer {
- my $hashref = shift;
-
- socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!";
- connect(SOCK, sockaddr_un($fs_signupd_socket)) or die "connect: $!";
- print SOCK "new_customer\n";
-
- my $signup_data = { map { $_ => $hashref->{$_} } qw(
- first last ss company address1 address2 city county state zip country
- daytime night fax payby payinfo paydate payname invoicing_list
- referral_custnum comments pkgpart username _password sec_phrase popnum
- ) };
-
- $signup_data->{agentnum} = $hashref->{agentnum} if $hashref->{agentnum};
-
- nstore_fd($signup_data, \*SOCK) or die "can't send customer signup: $!";
- SOCK->flush;
-
- chop( my $error = <SOCK> );
- $error;
+#compatibility bit
+sub new_customer {
+ my $hash = FS::SelfService::new_customer(@_);
+ $hash->{'error'};
}
=back
--- /dev/null
+<HTML>
+ <HEAD>
+ <TITLE>
+ CVV2 information
+ </TITLE>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+ security code used to reduce credit card fraud.<BR><BR>
+ <TABLE BORDER=0 CELLSPACING=4>
+ <TR>
+ <TH>Visa / MasterCard / Discover</TH>
+ <TH>American Express</TH>
+ </TR>
+ <TR>
+ <TD>
+ <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="cvv2.png">
+ </TD>
+ <TD>
+ <IMG BORDER=0 ALT="American Express" SRC="cvv2_amex.png">
+ </TD>
+ </TABLE>
+ <CENTER><A HREF="javascript:close()">(close window)</A></CENTER>
+ </BODY>
+</HTML>
--- /dev/null
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Agent <SELECT NAME="agentnum">
+<%=
+ warn $init_data;
+ warn $init_data->{'agent'};
+ foreach my $agent ( @{$init_data->{'agent'}} ) {
+ $OUT .= '<OPTION VALUE="'. $agent->{'agentnum'}. '"';
+ $OUT .= ' SELECTED' if $agent->{'agentnum'} eq $agentnum;
+ $OUT .= '>'. $agent->{'agent'};
+ }
+%>
+</SELECT><BR><BR>
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right"> </TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">(none)
+
+ <%=
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $package->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+</FORM></BODY></HTML>
--- /dev/null
+<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
+<FORM ACTION="<%= $self_url %>" METHOD=POST>
+<INPUT TYPE="hidden" NAME="magic" VALUE="process">
+<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
+<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Contact Information
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Contact name<BR>(last, first)</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="last" VALUE="<%= $last %>">,
+ <INPUT TYPE="text" NAME="first" VALUE="<%= $first %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Company</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="company" SIZE=70 VALUE="<%= $company %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Address</TH>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address1" SIZE=70 VALUE="<%= $address1 %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right"> </TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="address2" SIZE=70 VALUE="<%= $address2 %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>City</TH>
+ <TD><INPUT TYPE="text" NAME="city" VALUE="<%= $city %>"></TD>
+ <TH ALIGN="right"><font color="#ff0000">*</font>State/Country</TH>
+ <TD>
+ <%=
+ ($county_html, $state_html, $country_html) =
+ regionselector( $county, $state, $country );
+
+ "$county_html $state_html";
+ %>
+ </TD>
+ <TH><font color="#ff0000">*</font>Zip</TH>
+ <TD><INPUT TYPE="text" NAME="zip" SIZE=10 VALUE="<%= $zip %>"></TD>
+</TR>
+<TR>
+ <TH ALIGN="right"><font color="#ff0000">*</font>Country</TH>
+ <TD><%= $country_html %></TD>
+<TR>
+ <TD ALIGN="right">Day Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="daytime" VALUE="<%= $daytime %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Night Phone</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="night" VALUE="<%= $night %>" SIZE=18></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Fax</TD>
+ <TD COLSPAN=5><INPUT TYPE="text" NAME="fax" VALUE="<%= $fax %>" SIZE=12></TD>
+</TR>
+</TABLE><font color="#ff0000">*</font> required fields<BR>
+<BR>Billing information<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR><TD>
+
+ <%=
+ $OUT .= '<INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST"';
+ my @invoicing_list = split(', ', $invoicing_list );
+ $OUT .= ' CHECKED'
+ if ! @invoicing_list || grep { $_ eq 'POST' } @invoicing_list;
+ $OUT .= '>';
+ %>
+
+ Postal mail invoice
+</TD></TR>
+<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="<%= join(', ', grep { $_ ne 'POST' } split(', ', $invoicing_list ) ) %>">
+</TD></TR>
+<%= scalar(@payby) > 1 ? '<TR><TD>Billing type</TD></TR>' : '' %>
+</TABLE>
+<TABLE BGCOLOR="#c0c0c0" BORDER=1 WIDTH="100%">
+<TR>
+
+ <%=
+
+ my $cardselect = '<SELECT NAME="CARD_type"><OPTION></OPTION>';
+ my %types = (
+ 'VISA' => 'VISA card',
+ 'MasterCard' => 'MasterCard',
+ 'Discover' => 'Discover card',
+ 'American Express' => 'American Express card',
+ );
+ foreach ( keys %types ) {
+ $selected = $cgi->param('CARD_type') eq $types{$_} ? 'SELECTED' : '';
+ $cardselect .= qq!<OPTION $selected VALUE="$types{$_}">$_</OPTION>!;
+ }
+ $cardselect .= '</SELECT>';
+
+ my %payby = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD"). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", "12-2037"). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="Accounts Payable">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR><font color="#ff0000">*</font>Exp !. expselect("COMP"),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2 (<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>) <INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ my( $account, $aba ) = split('@', $payinfo);
+ my %paybychecked = (
+ 'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
+ 'DCRD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="DCRD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("DCRD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="DCRD_payname" VALUE="$payname">!,
+ 'CHEK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="CHEK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="CHEK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="CHEK_month" VALUE="12"><INPUT TYPE="hidden" NAME="CHEK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="CHEK_payname" VALUE="$payname">!,
+ 'DCHK' => qq!Electronic check<BR>${r}Account number <INPUT TYPE="text" NAME="DCHK_payinfo1" VALUE="$account" MAXLENGTH=10><BR>${r}ABA/Routing code <INPUT TYPE="text" NAME="DCHK_payinfo2" VALUE="$aba" SIZE=10 MAXLENGTH=9><INPUT TYPE="hidden" NAME="DCHK_month" VALUE="12"><INPUT TYPE="hidden" NAME="DCHK_year" VALUE="2037"><BR>${r}Bank name <INPUT TYPE="text" NAME="DCHK_payname" VALUE="$payname">!,
+ 'LECB' => qq!Phone bill billing<BR>${r}Phone number <INPUT TYPE="text" BANE="LECB_payinfo" VALUE="$payinfo" MAXLENGTH=15 SIZE=16><INPUT TYPE="hidden" NAME="LECB_month" VALUE="12"><INPUT TYPE="hidden" NAME="LECB_year" VALUE="2037"><INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!,
+ 'BILL' => qq!Billing<BR>P.O. <INPUT TYPE="text" NAME="BILL_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("BILL", $paydate). qq!<BR><font color="#ff0000">*</font>Attention<BR><INPUT TYPE="text" NAME="BILL_payname" VALUE="$payname">!,
+ 'COMP' => qq!Complimentary<BR><font color="#ff0000">*</font>Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR><font color="#ff0000">*</font>Exp !. expselect("COMP", $paydate),
+ 'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
+ );
+
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2 (<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>) <INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+ for (@payby) {
+ if ( scalar(@payby) == 1) {
+ $OUT .= '<TD VALIGN=TOP>'.
+ qq!<INPUT TYPE="hidden" NAME="payby" VALUE="$_">!.
+ "$paybychecked{$_}</TD>";
+ } else {
+ $OUT .= qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
+ if ($payby eq $_) {
+ $OUT .= qq! CHECKED> $paybychecked{$_}</TD>!;
+ } else {
+ $OUT .= qq!> $payby{$_}</TD>!;
+ }
+
+ }
+ }
+ %>
+
+</TR></TABLE><font color="#ff0000">*</font> required fields for each billing type
+<BR><BR>First package
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TD COLSPAN=2><SELECT NAME="pkgpart"><OPTION VALUE="">(none)
+
+ <%=
+ foreach my $package ( @{$packages} ) {
+ $OUT .= '<OPTION VALUE="'. $package->{'pkgpart'}. '"';
+ $OUT .= ' SELECTED' if $pkgpart && $package->{'pkgpart'} == $pkgpart;
+ $OUT .= '>'. $package->{'pkg'};
+ }
+ %>
+
+ </SELECT></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $username %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password" VALUE="<%= $password %>"></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Re-enter Password</TD>
+ <TD><INPUT TYPE="password" NAME="_password2" VALUE="<%= $password2 %>"></TD>
+</TR>
+<%=
+ if ( $init_data->{'security_phrase'} ) {
+ $OUT .= <<ENDOUT;
+<TR>
+ <TD ALIGN="right">Security Phrase</TD>
+ <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="$sec_phrase">
+ </TD>
+</TR>
+ENDOUT
+ } else {
+ $OUT .= '<INPUT TYPE="hidden" NAME="sec_phrase" VALUE="">';
+ }
+%>
+<%=
+ if ( scalar(@$pops) ) {
+ $OUT .= '<TR><TD ALIGN="right">Access number</TD><TD>'.
+ popselector($popnum). '</TD></TR>';
+ } else {
+ $OUT .= popselector($popnum);
+ }
+%>
+</TABLE>
+<BR><BR>Enter up to ten external accounts from which to retrieve email
+<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
+<TR>
+ <TH ALIGN="left">Mail server</TH>
+ <TH ALIGN="left">Username</TH>
+ <TH ALIGN="left">Password</TH>
+</TR>
+<%=
+ for my $num ( 1..10 ) {
+ no strict 'vars';
+ $OUT .= qq!<TR><TD><INPUT TYPE="text" NAME="snarf_machine$num" VALUE="${"snarf_machine$num"}"></TD>!.
+ qq!<INPUT TYPE="hidden" NAME="snarf_protocol$num" VALUE="pop3">!.
+ qq!<TD><INPUT TYPE="text" NAME="snarf_username$num" VALUE="${"snarf_username$num"}"></TD>!.
+ qq!<TD><INPUT TYPE="password" NAME="snarf_password$num" VALUE="${"snarf_password$num"}"></TD>!.
+ qq!</TR>!;
+ }
+%>
+</TABLE>
+
+<BR><BR><INPUT TYPE="submit" VALUE="Signup">
+</FORM></BODY></HTML>
#!/usr/bin/perl -Tw
#
-# $Id: signup.cgi,v 1.43 2003-07-04 03:21:42 ivan Exp $
+# $Id: signup.cgi,v 1.50 2004-01-04 03:52:54 ivan Exp $
use strict;
use vars qw( @payby $cgi $locales $packages
$init_data $error
$last $first $ss $company $address1 $address2 $city $state $county
$country $zip $daytime $night $fax $invoicing_list $payby $payinfo
- $paydate $payname $referral_custnum $init_popstate
+ $paycvv $paydate $payname $referral_custnum $init_popstate
$pkgpart $username $password $password2 $sec_phrase $popnum
- $agentnum
- $ieak_file $ieak_template $cck_file $cck_template
+ $agentnum $refnum
+ $ieak_file $ieak_template
$signup_html $signup_template
$success_html $success_template
$decline_html $decline_template
#use CGI::Carp qw(fatalsToBrowser);
use Text::Template;
use Business::CreditCard;
-use HTTP::Headers::UserAgent 2.00;
+use HTTP::BrowserDetect;
use FS::SignupClient 0.03 qw( signup_info new_customer );
#acceptable payment methods
@payby = qw( CARD PREPAY );
$ieak_file = '/usr/local/freeside/ieak.template';
-$cck_file = '/usr/local/freeside/cck.template';
$signup_html = -e 'signup.html'
? 'signup.html'
: '/usr/local/freeside/signup.html';
$ieak_template = '';
}
-if ( -e $cck_file ) {
- my $cck_txt = Text::Template::_load_text($cck_file)
- or die $Text::Template::ERROR;
- $cck_txt =~ /^(.*)$/s; #untaint the template source - it's trusted
- $cck_txt = $1;
- $cck_template = new Text::Template ( TYPE => 'STRING', SOURCE => $cck_txt )
- or die $Text::Template::ERROR;
-} else {
- $cck_template = '';
-}
-
$agentnum = '';
if ( -e $signup_html ) {
my $signup_txt = Text::Template::_load_text($signup_html)
$paydate =
$cgi->param( $payby. '_month' ). '-'. $cgi->param( $payby. '_year' );
$payname = $cgi->param( $payby. '_payname' );
+ $paycvv = defined $cgi->param( $payby. '_paycvv' )
+ ? $cgi->param( $payby. '_paycvv' )
+ : '';
if ( $invoicing_list = $cgi->param('invoicing_list') ) {
$invoicing_list .= ', POST' if $cgi->param('invoicing_list_POST');
$password = $cgi->param('_password');
$popnum = $cgi->param('popnum');
#$agentnum, # = $cgi->param('agentnum'),
+ $agentnum ||= $cgi->param('agentnum');
$init_popstate = $cgi->param('init_popstate');
+ $refnum = $cgi->param('refnum');
if ( $cgi->param('_password') ne $cgi->param('_password2') ) {
$error = $init_data->{msgcat}{passwords_dont_match}; #msgcat
'fax' => $fax,
'payby' => $payby,
'payinfo' => $payinfo,
+ 'paycvv' => $paycvv,
'paydate' => $paydate,
'payname' => $payname,
'invoicing_list' => $invoicing_list,
'_password' => $password,
'popnum' => $popnum,
'agentnum' => $agentnum,
+ 'refnum' => $refnum,
+ map { $_ => $cgi->param($_) } grep { /^snarf_/ } $cgi->param
} );
}
if ( $error eq '_decline' ) {
print_decline();
} elsif ( $error ) {
+ #fudge the snarf info
+ no strict 'refs';
+ ${$_} = $cgi->param($_) foreach grep { /^snarf_/ } $cgi->param;
print_form();
} else {
print_okay();
$popnum = '';
$referral_custnum = $cgi->param('ref') || '';
$init_popstate = $cgi->param('init_popstate') || '';
+ $refnum = $init_data->{'refnum'};
print_form;
}
}
sub print_okay {
- my $user_agent = new HTTP::Headers::UserAgent $ENV{HTTP_USER_AGENT};
+ my $user_agent = new HTTP::BrowserDetect $ENV{HTTP_USER_AGENT};
$cgi->param('username') =~ /^(.+)$/
or die "fatal: invalid username got past FS::SignupClient::new_customer";
#global for template
$pkg = ( grep { $_->{'pkgpart'} eq $pkgpart } @$packages )[0]->{'pkg'};
- if ( $ieak_template
- && $user_agent->platform eq 'ia32'
- && $user_agent->os =~ /^win/
- && ($user_agent->browser)[0] eq 'IE'
- )
- { #send an IEAK config
+ if ( $ieak_template && $user_agent->windows && $user_agent->ie ) {
+ #send an IEAK config
print $cgi->header('application/x-Internet-signup'),
$ieak_template->fill_in();
- } elsif ( $cck_template
- && $user_agent->platform eq 'ia32'
- && $user_agent->os =~ /^win/
- && ($user_agent->browser)[0] eq 'Netscape'
- )
- { #send a Netscape config
- my $cck_data = $cck_template->fill_in();
- print $cgi->header('application/x-netscape-autoconfigure-dialer-v2'),
- map {
- m/(.*)\s+(.*)$/;
- pack("N", length($1)). $1. pack("N", length($2)). $2;
- } split(/\n/, $cck_data);
-
} else { #send a simple confirmation
print $cgi->header( '-expires' => 'now' ),
$success_template->fill_in();
my ( $selected_county, $selected_state, $selected_country,
$prefix, $onchange ) = @_;
- my $prefix = '' unless defined $prefix;
+ $prefix = '' unless defined $prefix;
my $countyflag = 0;
<BR><BR>
Username: <%= $username %><BR>
Password: <%= $password %><BR>
-Access number: (<%= $ac %>) / $exch - $local<BR>
+Access number: (<%= $ac %>) / <%= $exch %> - <%= $local %><BR>
Package: <%= $pkg %><BR>
</BODY></HTML>
END
<HTML><HEAD><TITLE>ISP Signup form</TITLE></HEAD>
-<BODY BGCOLOR="#e8e8e8"><FONT SIZE=7>ISP Signup form</FONT><BR><BR>
+<BODY BGCOLOR="#e8e8e8" onUnload="myclose()">
+<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1
+ }
+//--></script>
+<FONT SIZE=7>ISP Signup form</FONT><BR><BR>
<FONT SIZE="+1" COLOR="#ff0000"><%= $error %></FONT>
<FORM ACTION="<%= $self_url %>" METHOD=POST>
<INPUT TYPE="hidden" NAME="magic" VALUE="process">
<INPUT TYPE="hidden" NAME="ref" VALUE="<%= $referral_custnum %>">
<INPUT TYPE="hidden" NAME="ss" VALUE="">
+Where did you hear about our service? <SELECT NAME="refnum">
+<%=
+ $OUT .= '<OPTION VALUE="">' unless $refnum;
+ foreach my $part_referral ( @{$init_data->{'part_referral'}} ) {
+ $OUT .= '<OPTION VALUE="'. $part_referral->{'refnum'}. '"';
+ $OUT .= ' SELECTED' if $part_referral->{'refnum'} eq $refnum;
+ $OUT .= '>'. $part_referral->{'referral'};
+ }
+%>
+</SELECT><BR><BR>
Contact Information
<TABLE BGCOLOR="#c0c0c0" BORDER=0 CELLSPACING=0 WIDTH="100%">
<TR>
'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="" MAXLENGTH=80>!,
);
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $payby{$payby} .= qq!<BR>CVV2 (<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>) <INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
my( $account, $aba ) = split('@', $payinfo);
my %paybychecked = (
'CARD' => qq!Credit card<BR><font color="#ff0000">*</font>$cardselect<INPUT TYPE="text" NAME="CARD_payinfo" VALUE="$payinfo" MAXLENGTH=19><BR><font color="#ff0000">*</font>Exp !. expselect("CARD", $paydate). qq!<BR><font color="#ff0000">*</font>Name on card<BR><INPUT TYPE="text" NAME="CARD_payname" VALUE="$payname">!,
'PREPAY' => qq!Prepaid card<BR><font color="#ff0000">*</font><INPUT TYPE="text" NAME="PREPAY_payinfo" VALUE="$payinfo" MAXLENGTH=80>!,
);
+ if ( $init_data->{'cvv_enabled'} ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5
+ $paybychecked{$payby} .= qq!<BR>CVV2 (<A HREF="javascript:myopen('cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>) <INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
for (@payby) {
if ( scalar(@payby) == 1) {
$OUT .= '<TD VALIGN=TOP>'.
+++ /dev/null
-#!/usr/bin/perl -Tw
-#
-# fs_signup_server
-#
-
-use strict;
-use vars qw($pid);
-use IO::Handle;
-use Storable qw(nstore_fd fd_retrieve);
-use Tie::RefHash;
-use Net::SSH qw(sshopen2);
-use FS::UID qw(adminsuidsetup);
-use FS::Conf;
-use FS::Record qw( qsearch qsearchs );
-use FS::cust_main_county;
-use FS::cust_main;
-use FS::cust_bill;
-use FS::cust_pkg;
-use FS::Msgcat qw(gettext);
-
-use vars qw( $opt $Debug );
-
-$Debug = 2;
-
-my $user = shift or die &usage;
-&adminsuidsetup( $user );
-
-my $conf = new FS::Conf;
-
-if ($conf->exists('signup_server-quiet')) {
- $FS::cust_bill::quiet = 1;
- $FS::cust_pkg::quiet = 1;
-}
-
-#my @payby = qw(CARD PREPAY);
-my @payby = $conf->config('signup_server-payby');
-my $smtpmachine = $conf->config('smtpmachine');
-
-my $machine = shift or die &usage;
-
-my $agentnum = shift or die &usage;
-my $agent = qsearchs( 'agent', { 'agentnum' => $agentnum } ) or die &usage;
-my $pkgpart_href = $agent->pkgpart_hashref;
-
-my $refnum = shift or die &usage;
-
-#causing trouble for some folks
-#$SIG{CHLD} = sub { wait() };
-
-$SIG{HUP} = \&killssh;
-$SIG{INT} = \&killssh;
-$SIG{QUIT} = \&killssh;
-$SIG{TERM} = \&killssh;
-$SIG{PIPE} = \&killssh;
-sub killssh { kill 'TERM', $pid if $pid; exit; };
-
-my($fs_signupd)="/usr/local/sbin/fs_signupd";
-
-while (1) {
- my($reader,$writer)=(new IO::Handle, new IO::Handle);
- #seems to be broken - calling ->flush explicitly# $writer->autoflush(1);
- warn "[fs_signup_server] Connecting to $machine...\n" if $Debug;
- $pid = sshopen2($machine,$reader,$writer,$fs_signupd);
-
- my @pops = qsearch('svc_acct_pop',{} );
- my $init_data = {
-
- #'_protocol' => 'signup',
- #'_version' => '0.1',
- #'_packet' => 'init'
-
- 'cust_main_county' =>
- [ map { $_->hashref } qsearch('cust_main_county', {}) ],
-
- 'part_pkg' =>
- [
- #map { $_->hashref }
- map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
- grep { $_->svcpart('svc_acct') && $pkgpart_href->{ $_->pkgpart } }
- qsearch( 'part_pkg', { 'disabled' => '' } )
- ],
-
- 'agentnum2part_pkg' =>
- {
- map {
- my $href = $_->pkgpart_hashref;
- $_->agentnum =>
- [
- map { { 'payby' => [ $_->payby ], %{$_->hashref} } }
- grep { $_->svcpart('svc_acct') && $href->{ $_->pkgpart } }
- qsearch( 'part_pkg', { 'disabled' => '' } )
- ];
- } qsearch('agent', {} )
- },
-
- 'svc_acct_pop' => [ map { $_->hashref } @pops ],
-
- 'security_phrase' => $conf->exists('security_phrase'),
-
- 'payby' => [ $conf->config('signup_server-payby') ],
-
- 'msgcat' => { map { $_=>gettext($_) } qw(
- passwords_dont_match invalid_card unknown_card_type not_a
- ) },
-
- 'statedefault' => $conf->config('statedefault') || 'CA',
-
- 'countrydefault' => $conf->config('countrydefault') || 'US',
-
- };
-
- warn "[fs_signup_server] Sending init data...\n" if $Debug;
- nstore_fd($init_data, $writer) or die "can't send init data: $!";
- $writer->flush;
-
- warn "[fs_signup_server] Entering main loop...\n" if $Debug;
- while (1) {
- warn "[fs_signup_server] Reading (waiting for) signup data...\n" if $Debug;
- my $signup_data = fd_retrieve($reader);
-
- if ( $Debug > 1 ) {
- warn join('',
- map { " $_ => ". $signup_data->{$_}. "\n" } keys %$signup_data );
- }
-
- warn "[fs_signup_server] Processing signup...\n" if $Debug;
-
- my $error = '';
-
- #things that aren't necessary in base class, but are for signup server
- #return "Passwords don't match"
- # if $hashref->{'_password'} ne $hashref->{'_password2'}
- $error ||= gettext('empty_password') unless $signup_data->{'_password'};
- $error ||= gettext('no_access_number_selected')
- unless $signup_data->{'popnum'} || !scalar(@pops);
-
- #shares some stuff with htdocs/edit/process/cust_main.cgi... take any
- # common that are still here and library them.
- my $cust_main = new FS::cust_main ( {
- #'custnum' => '',
- 'agentnum' => $signup_data->{agentnum} || $agentnum,
- 'refnum' => $refnum,
-
- map { $_ => $signup_data->{$_} } qw(
- last first ss company address1 address2 city county state zip country
- daytime night fax payby payinfo paydate payname referral_custnum comments
- ),
-
- } );
-
- $error ||= "Illegal payment type"
- unless grep { $_ eq $signup_data->{'payby'} } @payby;
-
- $cust_main->payinfo($cust_main->daytime)
- if $cust_main->payby eq 'LECB' && ! $cust_main->payinfo;
-
- my @invoicing_list = split( /\s*\,\s*/, $signup_data->{'invoicing_list'} );
-
- $signup_data->{'pkgpart'} =~ /^(\d+)$/ or '' =~ /^()$/;
- my $pkgpart = $1;
-
- my $part_pkg =
- qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } )
- or $error ||= "WARNING: unknown pkgpart: $pkgpart";
- my $svcpart = $part_pkg->svcpart('svc_acct') unless $error;
-
- my $cust_pkg = new FS::cust_pkg ( {
- #later#'custnum' => $custnum,
- 'pkgpart' => $signup_data->{'pkgpart'},
- } );
- $error ||= $cust_pkg->check;
-
- my $svc_acct = new FS::svc_acct ( {
- 'svcpart' => $svcpart,
- map { $_ => $signup_data->{$_} }
- qw( username _password sec_phrase popnum ),
- } );
-
- my $y = $svc_acct->setdefault; # arguably should be in new method
- $error ||= $y unless ref($y);
-
- $error ||= $svc_acct->check;
-
- use Tie::RefHash;
- tie my %hash, 'Tie::RefHash';
- %hash = ( $cust_pkg => [ $svc_acct ] );
- $error ||= $cust_main->insert( \%hash, \@invoicing_list ); #msgcat
-
- if ( ! $error && $conf->exists('signup_server-realtime') ) {
-
- warn "[fs_signup_server] Billing customer...\n" if $Debug;
-
- my $bill_error = $cust_main->bill;
- warn "[fs_signup_server] error billing new customer: $bill_error"
- if $bill_error;
-
- $cust_main->apply_payments;
- $cust_main->apply_credits;
-
- $bill_error = $cust_main->collect;
- warn "[fs_signup_server] error collecting from new customer: $bill_error"
- if $bill_error;
-
- if ( $cust_main->balance > 0 ) {
-
- #this makes sense. credit is "un-doing" the invoice
- $cust_main->credit( $cust_main->balance, 'signup server decline' );
- $cust_main->apply_credits;
-
- #should check list for errors...
- #$cust_main->suspend;
- $cust_main->cancel;
-
- $error = '_decline';
- }
- }
-
- warn "[fs_signup_server] Sending results...\n" if $Debug;
- print $writer $error, "\n";
-
- next if $error;
-
- if ( $conf->config('signup_server-email') ) {
- warn "[fs_signup_server] Sending email...\n" if $Debug;
-
- #false laziness w/FS::cust_bill::send & FS::cust_pay::delete
- use Mail::Header;
- use Mail::Internet 1.44;
- use Date::Format;
- my $from = $conf->config('invoice_from'); #??? as good as any
- $ENV{MAILADDRESS} = $from;
- my $header = new Mail::Header ( [
- "From: $from",
- "To: ". $conf->config('signup_server-email'),
- "Sender: $from",
- "Reply-To: $from",
- "Date: ". time2str("%a, %d %b %Y %X %z", time),
- "Subject: FREESIDE NOTIFICATION: Signup Server",
- ] );
- my $body = [
- "This is an automatic message from your Freeside installation\n",
- "informing you a customer has signed up via the signup server:\n",
- "\n",
- 'custnum : '. $cust_main->custnum. "\n",
- 'Name : '. $cust_main->last. ", ". $cust_main->first. "\n",
- 'Agent : '. $cust_main->agent->agent. "\n",
- 'Package : '. $part_pkg->pkg. ' - '. $part_pkg->comment. "\n",
- 'Signup Date : '. time2str('%C', time). "\n",
- 'Username : '. $svc_acct->username. "\n",
- #'Password : '. # config file to turn this on if noment insists
- 'Day phone : '. $cust_main->daytime. "\n",
- 'Night phone : '. $cust_main->night. "\n",
- 'Address : '. $cust_main->address1. "\n",
- ( $cust_main->address2
- ? ' '. $cust_main->address2. "\n"
- : '' ),
- ' '. $cust_main->city. ', '. $cust_main->state. ' '.
- $cust_main->zip. "\n",
- ( $cust_main->country eq 'US'
- ? ''
- : ' '. $cust_main->country. "\n" ),
- "\n",
- ];
- #if ( $cust_main->balance > 0 ) {
- # push @$body,
- # "This customer has an outstanding balance and has been suspended.\n";
- #}
- my $message = new Mail::Internet ( 'Header' => $header, 'Body' => $body );
- $!=0;
- $message->smtpsend( Host => $smtpmachine )
- or $message->smtpsend( Host => $smtpmachine, Debug => 1 )
- or warn "[fs_signup_server] can't send email to ".
- $conf->config('signup_server-email').
- " via server $smtpmachine with SMTP: $!";
- #end-of-send mail
- }
-
- }
- close $writer;
- close $reader;
- warn "connection to $machine lost! waiting 60 seconds...\n";
- sleep 60;
- warn "reconnecting...\n";
-}
-
-sub usage {
- die "Usage:\n\n fs_signup_server user machine agentnum refnum\n";
-}
-
use strict;
use vars qw( $cgi $p );
-use CGI;
+use Apache::ASP 2.55;
+use CGI 2.47;
#use CGI::Carp qw(fatalsToBrowser);
use Date::Format;
use Date::Parse;
use IO::File;
use String::Approx qw(amatch);
use Chart::LinesPoints;
-use HTML::Widgets::SelectLayers 0.02;
+use HTML::Widgets::SelectLayers 0.03;
use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
use FS::Record qw(qsearch qsearchs fields dbdef);
use FS::Conf;
use FS::CGI qw(header menubar popurl table itable ntable idiot eidiot
small_custview myexit http_header);
use FS::Msgcat qw(gettext geterror);
+use FS::Misc qw( send_email );
use FS::agent;
use FS::agent_type;
use FS::router;
use FS::addr_block;
use FS::svc_broadband;
+use FS::svc_external;
use FS::type_pkgs;
use FS::part_export;
use FS::part_export_option;
&cgisuidsetup($cgi);
$p = popurl(2);
#print $cgi->header( '-expires' => 'now' );
- dbh->{'private_profile'} = {} if dbh->can('sprintProfile');
+ #dbh->{'private_profile'} = {} if dbh->can('sprintProfile');
+ dbh->{'private_profile'} = {} if UNIVERSAL::can(dbh, 'sprintProfile');
#really should check for FS::Profiler or something
# Devel::AutoProfiler _our_ VERSION? thanks a fucking lot
#$$ref = $cgi->header() . $$ref;
#warn "Script_OnFlush called with dbh ". dbh. "\n";
#if ( dbh->can('sprintProfile') ) {
- if ( UNIVERSAL::can(dbh,'sprintProfile') ) {
+ if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
#warn "dbh can sprintProfile\n";
if ( lc($Response->{ContentType}) eq 'text/html' ) { #con
#warn "contenttype is sprintProfile\n";
my( $self, $location) = @_;
my $page =
$cgi->header.
- qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A><BR><BR>!.
- '<PRE>'. encode_entities(dbh->sprintProfile()).
+ qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
+ '<BR><BR><PRE>'.
+ ( UNIVERSAL::can(dbh, 'sprintProfile')
+ ? encode_entities(dbh->sprintProfile())
+ : 'DBIx::Profile missing sprintProfile method;'.
+ 'unpatched or too old?' ).
"\n\n". &sprintAutoProfile(). '</PRE>'.
'</BODY></HTML>';
dbh->{'private_profile'} = {};
}
+sub include {
+ $Response->Include(@_);
+}
+
+if ( defined(@DBIx::Profile::ISA) ) {
+
+ #false laziness w/above
+ *redirect = sub {
+ my($location) = @_;
+
+ ${$Response->{BinaryRef}} =
+ $cgi->header.
+ qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
+ '<BR><BR><PRE>'.
+ ( UNIVERSAL::can(dbh, 'sprintProfile')
+ ? encode_entities(dbh->sprintProfile())
+ : 'DBIx::Profile missing sprintProfile method;'.
+ 'unpatched or too old?' ).
+ "\n\n". &sprintAutoProfile(). '</PRE>'.
+ '</BODY></HTML>';
+
+ dbh->{'private_profile'} = {};
+
+ $Response->End;
+
+ };
+
+} else {
+
+ *redirect = sub {
+ $Response->Redirect(@_);
+ }
+
+}
+
1;
package HTML::Mason;
# Bring in main Mason package.
-use HTML::Mason;
+use HTML::Mason 1.1;
# Bring in ApacheHandler, necessary for mod_perl integration.
# Uncomment the second line (and comment the first) to use
{ package HTML::Mason::Commands;
use strict;
use vars qw( $cgi $p );
- use CGI;
+ use CGI 2.47;
#use CGI::Carp qw(fatalsToBrowser);
use Date::Format;
use Date::Parse;
use IO::File;
use String::Approx qw(amatch);
use Chart::LinesPoints;
- use HTML::Widgets::SelectLayers 0.02;
+ use HTML::Widgets::SelectLayers 0.03;
use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
use FS::Record qw(qsearch qsearchs fields dbdef);
use FS::Conf;
use FS::CGI qw(header menubar popurl table itable ntable idiot eidiot
small_custview myexit http_header);
use FS::Msgcat qw(gettext geterror);
+ use FS::Misc qw( send_email );
use FS::agent;
use FS::agent_type;
use FS::session;
use FS::svc_acct;
use FS::svc_acct_pop qw(popselector);
- use FS::svc_acct_sm;
use FS::svc_domain;
use FS::svc_forward;
use FS::svc_www;
use FS::router;
use FS::addr_block;
use FS::svc_broadband;
+ use FS::svc_external;
use FS::type_pkgs;
use FS::part_export;
use FS::part_export_option;
*CGI::redirect = sub {
my( $self, $location ) = @_;
use vars qw($m);
- #http://www.masonhq.com/docs/faq/#how_do_i_do_an_external_redirect
- $m->clear_buffer;
- # The next two lines are necessary to stop Apache from re-reading
- # POSTed data.
- $r->method('GET');
- $r->headers_in->unset('Content-length');
- $r->content_type('text/html');
- #$r->err_header_out('Location' => $location);
- $r->header_out('Location' => $location);
- $r->header_out('Content-Type' => 'text/html');
- $m->abort(302);
-
- '';
+
+ if ( defined(@DBIx::Profile::ISA) ) { #profiling redirect
+
+ my $page =
+ qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
+ '<BR><BR><PRE>'.
+ ( UNIVERSAL::can(dbh, 'sprintProfile')
+ ? encode_entities(dbh->sprintProfile())
+ : 'DBIx::Profile missing sprintProfile method;'.
+ 'unpatched or too old?' ).
+ #"\n\n". &sprintAutoProfile(). '</PRE>'.
+ "\n\n". '</PRE>'.
+ '</BODY></HTML>';
+ dbh->{'private_profile'} = {};
+ return $page;
+
+ } else { #normal redirect
+
+ $m->redirect($location);
+ '';
+
+ }
+
};
$cgi = new CGI;
&cgisuidsetup($cgi);
#&cgisuidsetup($r);
$p = popurl(2);
- }
+
+ sub include {
+ use vars qw($m);
+ $m->scomp(@_);
+ }
+
+ sub redirect {
+ my( $location ) = @_;
+ use vars qw($m);
+ $m->clear_buffer;
+ #false laziness w/above
+ if ( defined(@DBIx::Profile::ISA) ) { #profiling redirect
+
+ $m->print(
+ qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
+ '<BR><BR><PRE>'.
+ ( UNIVERSAL::can(dbh, 'sprintProfile')
+ ? encode_entities(dbh->sprintProfile())
+ : 'DBIx::Profile missing sprintProfile method;'.
+ 'unpatched or too old?' ).
+ #"\n\n". &sprintAutoProfile(). '</PRE>'.
+ "\n\n". '</PRE>'.
+ '</BODY></HTML>'
+ );
+ dbh->{'private_profile'} = {};
+
+ $m->abort(200);
+
+ } else { #normal redirect
+
+ $m->redirect($location);
+
+ }
+
+ }
+
+ } # end package HTML::Mason::Commands;
$r->content_type('text/html');
#eorar
+++ /dev/null
-#!/usr/bin/perl
-#
-# This is a basic, fairly fuctional Mason handler.pl.
-#
-# For something a little more involved, check out session_handler.pl
-
-package HTML::Mason;
-
-# Bring in main Mason package.
-use HTML::Mason;
-
-# Bring in ApacheHandler, necessary for mod_perl integration.
-# Uncomment the second line (and comment the first) to use
-# Apache::Request instead of CGI.pm to parse arguments.
-use HTML::Mason::ApacheHandler;
-# use HTML::Mason::ApacheHandler (args_method=>'mod_perl');
-
-# Uncomment the next line if you plan to use the Mason previewer.
-#use HTML::Mason::Preview;
-
-use strict;
-
-# List of modules that you want to use from components (see Admin
-# manual for details)
-#{ package HTML::Mason::Commands;
-# use CGI;
-#}
-
-# Create Mason objects
-#
-my $parser = new HTML::Mason::Parser;
-my $interp = new HTML::Mason::Interp (parser=>$parser,
- comp_root=>'/var/www/freeside',
- data_dir=>'/usr/local/etc/freeside/masondata',
- out_mode=>'stream',
- );
-my $ah = new HTML::Mason::ApacheHandler ( interp => $interp,
- #auto_send_headers => 0,
- );
-
-# Activate the following if running httpd as root (the normal case).
-# Resets ownership of all files created by Mason at startup.
-#
-chown (Apache->server->uid, Apache->server->gid, $interp->files_written);
-
-sub handler
-{
- my ($r) = @_;
-
- # If you plan to intermix images in the same directory as
- # components, activate the following to prevent Mason from
- # evaluating image files as components.
- #
- #return -1 if $r->content_type && $r->content_type !~ m|^text/|i;
-
- #rar
- { package HTML::Mason::Commands;
- use strict;
- use vars qw( $cgi $p );
- use CGI;
- #use CGI::Carp qw(fatalsToBrowser);
- use Date::Format;
- use Date::Parse;
- use Time::Local;
- use Tie::IxHash;
- use HTML::Entities;
- use IO::Handle;
- use IO::File;
- use String::Approx qw(amatch);
- use Chart::LinesPoints;
- use HTML::Widgets::SelectLayers 0.02;
- use FS::UID qw(cgisuidsetup dbh getotaker datasrc driver_name);
- use FS::Record qw(qsearch qsearchs fields dbdef);
- use FS::Conf;
- use FS::CGI qw(header menubar popurl table itable ntable idiot eidiot
- small_custview myexit http_header);
- use FS::Msgcat qw(gettext geterror);
-
- use FS::agent;
- use FS::agent_type;
- use FS::domain_record;
- use FS::cust_bill;
- use FS::cust_bill_pay;
- use FS::cust_credit;
- use FS::cust_credit_bill;
- use FS::cust_main;
- use FS::cust_main_county;
- use FS::cust_pay;
- use FS::cust_pkg;
- use FS::cust_refund;
- use FS::cust_svc;
- use FS::nas;
- use FS::part_bill_event;
- use FS::part_pkg;
- use FS::part_referral;
- use FS::part_svc;
- use FS::part_svc_router;
- use FS::part_virtual_field;
- use FS::pkg_svc;
- use FS::port;
- use FS::queue qw(joblisting);
- use FS::raddb;
- use FS::session;
- use FS::svc_acct;
- use FS::svc_acct_pop qw(popselector);
- use FS::svc_acct_sm;
- use FS::svc_domain;
- use FS::svc_forward;
- use FS::svc_www;
- use FS::router;
- use FS::addr_block;
- use FS::svc_broadband;
- use FS::type_pkgs;
- use FS::part_export;
- use FS::part_export_option;
- use FS::export_svc;
- use FS::msgcat;
-
- *CGI::redirect = sub {
- my( $self, $location ) = @_;
-
- #http://www.masonhq.com/docs/faq/#how_do_i_do_an_external_redirect
- $m->clear_buffer;
- # The next two lines are necessary to stop Apache from re-reading
- # POSTed data.
- $r->method('GET');
- $r->headers_in->unset('Content-length');
- $r->content_type('text/html');
- #$r->err_header_out('Location' => $location);
- $r->header_out('Location' => $location);
- $r->header_out('Content-Type' => 'text/html');
- $m->abort(302);
-
- '';
- };
-
- $cgi = new CGI;
- &cgisuidsetup($cgi);
- #&cgisuidsetup($r);
- $p = popurl(2);
- }
-
- $r->content_type('text/html');
- #eorar
-
- my $headers = $r->headers_out;
- $headers->{'Pragma'} = $headers->{'Cache-control'} = 'no-cache';
- #$r->no_cache(1);
- $headers->{'Expires'} = '0';
-
-# $r->send_http_header;
-
- my $status = $ah->handle_request($r);
-
- $status;
-}
-
-1;
--- /dev/null
+% $m->call_next;
+<%init>
+ dbh->{'private_profile'} = {} if UNIVERSAL::can(dbh, 'sprintProfile');
+</%init>
+<%filter>
+
+my $profile = '';
+if ( UNIVERSAL::can(dbh, 'sprintProfile') ) {
+
+ if ( lc($r->content_type) eq 'text/html' ) {
+
+ $profile = '<PRE>'. ("\n"x4096). encode_entities(dbh->sprintProfile()).
+ #"\n\n". &sprintAutoProfile(). '</PRE>';
+ "\n\n". '</PRE>';
+ }
+
+ dbh->{'private_profile'} = {};
+}
+
+s/(<\/BODY>[\s\n]*<\/HTML>[\s\n]*)$/$profile$1/i;
+</%filter>
<!-- mason kludge -->
+
<%
-#Begin silliness
-#
-#use FS::UI::CGI;
-#use FS::UI::agent;
-#
-#$ui = new FS::UI::agent;
-#$ui->browse;
-#exit;
-#__END__
-#End silliness
+
+ my %search;
+ if ( $cgi->param('showdisabled')
+ || !dbdef->table('agent')->column('disabled') ) {
+ %search = ();
+ } else {
+ %search = ( 'disabled' => '' );
+ }
+
+ #bad false laziness with search/cust_main.cgi (also needs fixing up for
+ #old mysql)
+ my $ncancelled = "
+ 0 < ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ AND ( cust_pkg.cancel IS NULL
+ OR cust_pkg.cancel = 0
+ )
+ )
+ OR 0 = ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ )
+ ";
+
+ my $ncancelled_sth = dbh->prepare("SELECT COUNT(*) FROM cust_main
+ WHERE agentnum = ?
+ AND ( $ncancelled ) ")
+ or die dbh->errstr;
+
+ my $total_sth = dbh->prepare("SELECT COUNT(*) FROM cust_main
+ WHERE agentnum = ? ")
+ or die dbh->errstr;
+
%>
<%= header('Agent Listing', menubar(
full offerings (via their type).<BR><BR>
<A HREF="<%= $p %>edit/agent.cgi"><I>Add a new agent</I></A><BR><BR>
+<% if ( dbdef->table('agent')->column('disabled') ) { %>
+ <%= $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled agents</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled agents</a> )'; }
+ %>
+<% } %>
+
<%= table() %>
<TR>
- <TH COLSPAN=2>Agent</TH>
+ <TH COLSPAN=<%= ( $cgi->param('showdisabled') || !dbdef->table('agent')->column('disabled') ) ? 2 : 3 %>>Agent</TH>
<TH>Type</TH>
+ <TH>Customers</TH>
<TH><FONT SIZE=-1>Freq.</FONT></TH>
<TH><FONT SIZE=-1>Prog.</FONT></TH>
</TR>
foreach my $agent ( sort {
#$a->getfield('agentnum') <=> $b->getfield('agentnum')
$a->getfield('agent') cmp $b->getfield('agent')
-} qsearch('agent',{}) ) {
- my($hashref)=$agent->hashref;
- my($typenum)=$hashref->{typenum};
- my($agent_type)=qsearchs('agent_type',{'typenum'=>$typenum});
- my($atype)=$agent_type->getfield('atype');
- print <<END;
+} qsearch('agent', \%search ) ) {
+
+ $ncancelled_sth->execute($agent->agentnum) or die $ncancelled_sth->errstr;
+ my $num_ncancelled = $ncancelled_sth->fetchrow_arrayref->[0];
+
+ $total_sth->execute($agent->agentnum) or die $total_sth->errstr;
+ my $num_total = $total_sth->fetchrow_arrayref->[0];
+
+ my $num_cancelled = $num_total - $num_ncancelled;
+
+ my $cust_main_link = $p. 'search/cust_main.cgi?agentnum_on=1&'.
+ 'agentnum='. $agent->agentnum;
+
+%>
+
<TR>
- <TD><A HREF="${p}edit/agent.cgi?$hashref->{agentnum}">
- $hashref->{agentnum}</A></TD>
- <TD><A HREF="${p}edit/agent.cgi?$hashref->{agentnum}">
- $hashref->{agent}</A></TD>
- <TD><A HREF="${p}edit/agent_type.cgi?$typenum">$atype</A></TD>
- <TD>$hashref->{freq}</TD>
- <TD>$hashref->{prog}</TD>
+ <TD><A HREF="<%=$p%>edit/agent.cgi?<%= $agent->agentnum %>">
+ <%= $agent->agentnum %></A></TD>
+<% if ( dbdef->table('agent')->column('disabled')
+ && !$cgi->param('showdisabled') ) { %>
+ <TD><%= $agent->disabled ? 'DISABLED' : '' %></TD>
+<% } %>
+
+ <TD><A HREF="<%=$p%>edit/agent.cgi?<%= $agent->agentnum %>">
+ <%= $agent->agent %></A></TD>
+ <TD><A HREF="<%=$p%>edit/agent_type.cgi?<%= $agent->typenum %>"><%= $agent->agent_type->atype %></A></TD>
+ <TD>
+ <FONT COLOR="#00CC00"><B><%= $num_ncancelled %></B></FONT>
+ <A HREF="<%= $cust_main_link %>&showcancelledcustomers=0">active</A>
+ <BR><FONT COLOR="#FF0000"><B><%= $num_cancelled %></B></FONT>
+ <A HREF="<%= $cust_main_link %>&showcancelledcustomers=1&cancelled=1">cancelled</A>
+ </TD>
+ <TD><%= $agent->freq %></TD>
+ <TD><%= $agent->prog %></TD>
</TR>
-END
-}
+<% } %>
-print <<END;
</TABLE>
</BODY>
</HTML>
-END
-
-%>
foreach my $agent_type ( sort {
$a->getfield('typenum') <=> $b->getfield('typenum')
} qsearch('agent_type',{}) ) {
- my($hashref)=$agent_type->hashref;
- my(@type_pkgs)=qsearch('type_pkgs',{'typenum'=> $hashref->{typenum} });
- my($rowspan)=scalar(@type_pkgs);
+ my $hashref = $agent_type->hashref;
+ #more efficient to do this with SQL...
+ my @type_pkgs = grep { $_->part_pkg and ! $_->part_pkg->disabled }
+ qsearch('type_pkgs',{'typenum'=> $hashref->{typenum} });
+ my $rowspan = scalar(@type_pkgs);
$rowspan = int($rowspan/2+0.5) ;
print <<END;
<TR>
<TH>Taxclass<BR><FONT SIZE=-1>(per-package classification)</FONT></TH>
<TH>Tax name<BR><FONT SIZE=-1>(printed on invoices)</FONT></TH>
<TH><FONT SIZE=-1>Tax</FONT></TH>
- <TH><FONT SIZE=-1>Exempt<BR>per<BR>month</TH>
+ <TH><FONT SIZE=-1>Exemption</TH>
</TR>
END
last if $hashref->{country} ne $regions[$i+$j]->country
|| $hashref->{state} ne $regions[$i+$j]->state
|| $hashref->{tax} != $regions[$i+$j]->tax
- || $hashref->{exempt_amount} != $regions[$i+$j]->exempt_amount;
+ || $hashref->{exempt_amount} != $regions[$i+$j]->exempt_amount
+ || $hashref->{setuptax} ne $regions[$i+$j]->setuptax
+ || $hashref->{recurtax} ne $regions[$i+$j]->recurtax;
}
my $newsup=0;
print "</TD>";
print "<TD BGCOLOR=\"#ffffff\">$hashref->{tax}%</TD>".
- '<TD BGCOLOR="#ffffff">$'.
- sprintf("%.2f", $hashref->{exempt_amount} || 0). '</TD>'.
- '</TR>';
+ '<TD BGCOLOR="#ffffff">';
+ print '$'. sprintf("%.2f", $hashref->{exempt_amount} ).
+ ' per month<BR>'
+ if $hashref->{exempt_amount} > 0;
+ print 'Setup fee<BR>' if $hashref->{setuptax} =~ /^Y$/i;
+ print 'Recurring fee<BR>' if $hashref->{recurtax} =~ /^Y$/i;
+ print '</TD></TR>';
}
<!-- mason kludge -->
+<%= header("Pending credit card batch", menubar( 'Main Menu' => $p,)) %>
+
+<FORM ACTION="<%=$p%>misc/download-batch.cgi" METHOD="POST">
+Download batch in format <SELECT NAME="format">
+<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV file for TD Canada Trust Merchant PC Batch</OPTION>
+</SELECT><INPUT TYPE="submit" VALUE="Download"></FORM>
+<BR><BR>
+
+<FORM ACTION="<%=$p%>misc/upload-batch.cgi" METHOD="POST" ENCTYPE="multipart/form-data">
+Upload results<BR>
+Filename <INPUT TYPE="file" NAME="batch_results"><BR>
+Format <SELECT NAME="format">
+<OPTION VALUE="csv-td_canada_trust-merchant_pc_batch">CSV results from TD Canada Trust Merchant PC Batch</OPTION>
+</SELECT><BR>
+<INPUT TYPE="submit" VALUE="Upload"></FORM>
+<BR>
+
<%
+ my $statement = "SELECT SUM(amount) from cust_pay_batch";
+ my $sth = dbh->prepare($statement) or die dbh->errstr. "doing $statement";
+ $sth->execute or die "Error executing \"$statement\": ". $sth->errstr;
+ my $total = $sth->fetchrow_arrayref->[0];
-print header("Pending credit card batch", menubar(
- 'Main Menu' => $p,
-# 'Add new referral' => "../edit/part_referral.cgi",
-)), &table(), <<END;
+ my $c_statement = "SELECT COUNT(*) from cust_pay_batch";
+ my $c_sth = dbh->prepare($c_statement)
+ or die dbh->errstr. "doing $c_statement";
+ $c_sth->execute or die "Error executing \"$c_statement\": ". $c_sth->errstr;
+ my $cards = $c_sth->fetchrow_arrayref->[0];
+%>
+<%= $cards %> credit card payments batched<BR>
+$<%= sprintf("%.2f", $total) %> total in pending batch<BR>
+
+<BR>
+<%= &table() %>
<TR>
<TH>#</TH>
<TH><font size=-1>inv#</font></TH>
<TH>Exp</TH>
<TH>Amount</TH>
</TR>
-END
-
-foreach my $cust_pay_batch ( sort {
- $a->getfield('paybatchnum') <=> $b->getfield('paybatchnum')
-} qsearch('cust_pay_batch',{}) ) {
-# my $date = time2str( "%a %b %e %T %Y", $queue->_date );
-# my $status = $hashref->{status};
-# if ( $status eq 'failed' || $status eq 'locked' ) {
-# $status .=
-# qq! ( <A HREF="$p/edit/cust_pay_batch.cgi?jobnum=$jobnum&action=new">retry</A> |!.
-# qq! <A HREF="$p/edit/cust_pay_batch.cgi?jobnum$jobnum&action=del">remove </A> )!;
-# }
- my $cardnum = $cust_pay_batch->{cardnum};
- $cardnum =~ s/.{4}$/xxxx/;
- print <<END;
+
+<%
+foreach my $cust_pay_batch ( sort { $a->paybatchnum <=> $b->paybatchnum }
+ qsearch('cust_pay_batch', {} )
+) {
+ my $cardnum = $cust_pay_batch->cardnum;
+ #$cardnum =~ s/.{4}$/xxxx/;
+ $cardnum = 'x'x(length($cardnum)-4). substr($cardnum,(length($cardnum)-4));
+
+ $cust_pay_batch->exp =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+ my( $mon, $year ) = ( $2, $1 );
+ $mon = "0$mon" if $mon < 10;
+ my $exp = "$mon/$year";
+
+%>
+
<TR>
- <TD>$cust_pay_batch->{paybatchnum}</TD>
- <TD><A HREF="../view/cust_bill.cgi?$cust_pay_batch->{invnum}">$cust_pay_batch->{invnum}</TD>
- <TD><A HREF="../view/cust_main.cgi?$cust_pay_batch->{custnum}">$cust_pay_batch->{custnum}</TD>
- <TD>$cust_pay_batch->{last}, $cust_pay_batch->{last}</TD>
- <TD>$cust_pay_batch->{payname}</TD>
- <TD>$cardnum</TD>
- <TD>$cust_pay_batch->{exp}</TD>
- <TD align="right">\$$cust_pay_batch->{amount}</TD>
+ <TD><%= $cust_pay_batch->paybatchnum %></TD>
+ <TD><A HREF="../view/cust_bill.cgi?<%= $cust_pay_batch->invnum %>"><%= $cust_pay_batch->invnum %></TD>
+ <TD><A HREF="../view/cust_main.cgi?<%= $cust_pay_batch->custnum %>"><%= $cust_pay_batch->custnum %></TD>
+ <TD><%= $cust_pay_batch->get('last'). ', '. $cust_pay_batch->first %></TD>
+ <TD><%= $cust_pay_batch->payname %></TD>
+ <TD><%= $cardnum %></TD>
+ <TD><%= $exp %></TD>
+ <TD align="right">$<%= $cust_pay_batch->amount %></TD>
</TR>
-END
-}
+<% } %>
-print <<END;
</TABLE>
</BODY>
</HTML>
-END
-
-%>
<%= itable() %>
<% my %opt = $part_export->options;
foreach my $opt ( keys %opt ) { %>
- <TR><TD><%= $opt %></TD><TD><%= $opt{$opt} %></TD></TR>
+ <TR><TD><%= $opt %></TD><TD><%= encode_entities($opt{$opt}) %></TD></TR>
<% } %>
</TABLE>
</TD>
my $total = scalar(@part_pkg);
my $sortby;
-my %num_active_cust_pkg;
+my %num_active_cust_pkg = ();
+my( $suspended_sth, $canceled_sth ) = ( '', '' );
if ( $cgi->param('active') ) {
my $active_sth = dbh->prepare(
'SELECT COUNT(*) FROM cust_pkg WHERE pkgpart = ?'.
$num_active_cust_pkg{$part_pkg->pkgpart} =
$active_sth->fetchrow_arrayref->[0];
}
- $sortby = \*active_cust_pkg_sort;
+ $sortby = sub {
+ $num_active_cust_pkg{$b->pkgpart} <=> $num_active_cust_pkg{$a->pkgpart};
+ };
+
+ $suspended_sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM cust_pkg WHERE pkgpart = ?'.
+ ' AND ( cancel IS NULL OR cancel = 0 )'.
+ ' AND susp IS NOT NULL AND susp != 0'
+ ) or die dbh->errstr;
+
+ $canceled_sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM cust_pkg WHERE pkgpart = ?'.
+ ' AND cancel IS NOT NULL AND cancel != 0'
+ ) or die dbh->errstr;
+
} else {
$sortby = \*pkgpart_sort;
}
<TH><FONT SIZE=-1>Data</FONT></TH>
<TH>Service</TH>
<TH><FONT SIZE=-1>Quan.</FONT></TH>
- </TR>
END
+print '<TH><FONT SIZE=-1>Primary</FONT></TH>'
+ if dbdef->table('pkg_svc')->column('primary_svc');
+print '</TR>';
foreach my $part_pkg ( sort $sortby @part_pkg ) {
my($hashref)=$part_pkg->hashref;
print " <TD ROWSPAN=$rowspan>";
print '<FONT COLOR="#00CC00"><B>'.
$num_active_cust_pkg{$hashref->{'pkgpart'}}.
- qq!</B></FONT> <A HREF="${p}search/cust_pkg.cgi?magic=active;pkgpart=$hashref->{pkgpart}">active</A>!;
- # suspended/cancelled
+ qq!</B></FONT> <A HREF="${p}search/cust_pkg.cgi?magic=active;pkgpart=$hashref->{pkgpart}">active</A><BR>!;
+
+ $suspended_sth->execute( $part_pkg->pkgpart ) or die $suspended_sth->errstr;
+ my $num_suspended = $suspended_sth->fetchrow_arrayref->[0];
+ print '<FONT COLOR="#FF9900"><B>'. $num_suspended.
+ qq!</B></FONT> <A HREF="${p}search/cust_pkg.cgi?magic=suspended;pkgpart=$hashref->{pkgpart}">suspended</A><BR>!;
+
+ $canceled_sth->execute( $part_pkg->pkgpart ) or die $canceled_sth->errstr;
+ my $num_canceled = $canceled_sth->fetchrow_arrayref->[0];
+ print '<FONT COLOR="#FF0000"><B>'. $num_canceled.
+ qq!</B></FONT> <A HREF="${p}search/cust_pkg.cgi?magic=canceled;pkgpart=$hashref->{pkgpart}">canceled</A>!;
+
+
print '</TD>';
}
print <<END;
my($part_svc) = qsearchs('part_svc',{'svcpart'=> $svcpart });
print $n,qq!<TD><A HREF="${p}edit/part_svc.cgi?$svcpart">!,
$part_svc->getfield('svc'),"</A></TD><TD>",
- $pkg_svc->getfield('quantity'),"</TD></TR>\n";
+ $pkg_svc->getfield('quantity'),"</TD>";
+ if ( dbdef->table('pkg_svc')->column('primary_svc') ) {
+ print '<TD>';
+ print 'PRIMARY' if $pkg_svc->primary_svc =~ /^Y/i;
+ print '</TD>';
+ }
+ print "</TR>\n";
$n="<TR>";
}
</HTML>
END
-
sub pkgpart_sort {
$a->pkgpart <=> $b->pkgpart;
}
-sub active_cust_pkg_sort {
- $num_active_cust_pkg{$b->pkgpart} <=> $num_active_cust_pkg{$a->pkgpart};
-}
-
%>
<A HREF="<%= $p %>edit/part_referral.cgi"><I>Add a new advertising source</I></A>
<BR><BR>
+<%
+ my $today = timelocal(0, 0, 0, (localtime(time))[3..5] );
+ my %past;
+ tie %past, 'Tie::IxHash',
+ 'Today' => 0,
+ 'Past week' => 518400, # 60sec * 60min * 24hrs * 6days
+ 'Past 30 days' => 2505600, # 60sec * 60min * 24hrs * 29days
+ 'Past 60 days' => 5097600, # 60sec * 60min * 24hrs * 29days
+ 'Past 90 days' => 7689600, # 60sec * 60min * 24hrs * 29days
+ 'Past 6 months' => 15724800, # 60sec * 60min * 24hrs * 182days
+ 'Past year' => 31486000, # 60sec * 60min * 24hrs * 364days
+ 'Total' => $today,
+ ;
+
+ my $sth = dbh->prepare("SELECT COUNT(*) FROM h_cust_main
+ WHERE history_action = 'insert'
+ AND refnum = ?
+ AND history_date > ? ")
+ or die dbh->errstr;
+%>
+
<%= table() %>
<TR>
- <TH COLSPAN=2>Advertising source</TH>
+ <TH COLSPAN=2 ROWSPAN=2>Advertising source</TH>
+ <TH COLSPAN=<%= scalar(keys %past) %>>Customers</TH>
+</TR>
+<% for my $period ( keys %past ) { %>
+ <TH><FONT SIZE=-1><%= $period %></FONT></TH>
+<% } %>
</TR>
<%
foreach my $part_referral ( sort {
$a->getfield('refnum') <=> $b->getfield('refnum')
} qsearch('part_referral',{}) ) {
- my($hashref)=$part_referral->hashref;
- print <<END;
+%>
<TR>
- <TD><A HREF="${p}edit/part_referral.cgi?$hashref->{refnum}">
- $hashref->{refnum}</A></TD>
- <TD><A HREF="${p}edit/part_referral.cgi?$hashref->{refnum}">
- $hashref->{referral}</A></TD>
+ <TD><A HREF="<%= $p %>edit/part_referral.cgi?<%= $part_referral->refnum %>">
+ <%= $part_referral->refnum %></A></TD>
+ <TD><A HREF="<%= $p %>edit/part_referral.cgi?<%= $part_referral->refnum %>">
+ <%= $part_referral->referral %></A></TD>
+ <% for my $period ( values %past ) {
+ $sth->execute($part_referral->refnum, $today-$period)
+ or die $sth->errstr;
+ my $number = $sth->fetchrow_arrayref->[0];
+ %>
+ <TD ALIGN="right"><%= $number %></TD>
+ <% } %>
</TR>
-END
-
-}
+<% } %>
-print <<END;
</TABLE>
</BODY>
</HTML>
-END
-
-%>
qsearch('part_svc', \%search );
my $total = scalar(@part_svc);
+my %num_active_cust_svc = ();
+if ( $cgi->param('active') ) {
+ my $active_sth = dbh->prepare(
+ 'SELECT COUNT(*) FROM cust_svc WHERE svcpart = ?'
+ ) or die dbh->errstr;
+ foreach my $part_svc ( @part_svc ) {
+ $active_sth->execute($part_svc->svcpart) or die $active_sth->errstr;
+ $num_active_cust_svc{$part_svc->svcpart} =
+ $active_sth->fetchrow_arrayref->[0];
+ }
+ @part_svc = sort { $num_active_cust_svc{$b->svcpart} <=>
+ $num_active_cust_svc{$a->svcpart} } @part_svc;
+}
+
%>
<%= header('Service Definition Listing', menubar( 'Main Menu' => $p) ) %>
<TR>
<TH COLSPAN=<%= $cgi->param('showdisabled') ? 2 : 3 %>>Service</TH>
<TH>Table</TH>
+<% if ( $cgi->param('active') ) { %>
+ <TH><FONT SIZE=-1>Customer<BR>Services</FONT></TH>
+<% } %>
<TH>Export</TH>
<TH>Field</TH>
<TH COLSPAN=2>Modifier</TH>
<%= $hashref->{svc} %></A></TD>
<TD ROWSPAN=<%= $rowspan %>>
<%= $hashref->{svcdb} %></TD>
+<% if ( $cgi->param('active') ) { %>
+ <TD ROWSPAN=<%= $rowspan %>>
+ <FONT COLOR="#00CC00"><B><%= $num_active_cust_svc{$hashref->{svcpart}} %></B></FONT> <A HREF="<%=$p%>search/<%= $hashref->{svcdb} %>.cgi?svcpart=<%= $hashref->{svcpart} %>">active</A>
+ </TD>
+<% } %>
<TD ROWSPAN=<%= $rowspan %>><%= itable() %>
<%
# my @part_export =
<TD><B>Router name</B></TD>
<TD><B>Address block(s)</B></TD>
</TR>
-<% foreach $router (sort {$a->routernum <=> $b->routernum} @router) {
+<% foreach my $router (sort {$a->routernum <=> $b->routernum} @router) {
my @addr_block = $router->addr_block;
%>
<TR>
<!-- mason kludge -->
+<%
+ my $accounts_sth = dbh->prepare("SELECT COUNT(*) FROM svc_acct
+ WHERE popnum = ? ")
+ or die dbh->errstr;
+%>
<%= header('Access Number Listing', menubar( 'Main Menu' => $p )) %>
Points of Presence<BR><BR>
<A HREF="<%= $p %>edit/svc_acct_pop.cgi"><I>Add new Access Number</I></A><BR><BR>
<TH>Area code</TH>
<TH>Exchange</TH>
<TH>Local</TH>
+ <TH>Accounts</TH>
</TR>
<%
$a->state cmp $b->state || $a->city cmp $b->city
|| $a->ac <=> $b->ac || $a->exch <=> $b->exch || $a->loc <=> $b->loc
} qsearch('svc_acct_pop',{}) ) {
- my($hashref)=$svc_acct_pop->hashref;
- print <<END;
+
+ my $svc_acct_pop_link = $p . 'edit/svc_acct_pop.cgi?'. $svc_acct_pop->popnum;
+
+ $accounts_sth->execute($svc_acct_pop->popnum) or die $accounts_sth->errstr;
+ my $num_accounts = $accounts_sth->fetchrow_arrayref->[0];
+
+ my $svc_acct_link = $p. 'search/svc_acct.cgi?popnum='. $svc_acct_pop->popnum;
+
+%>
<TR>
- <TD><A HREF="${p}edit/svc_acct_pop.cgi?$hashref->{popnum}">
- $hashref->{popnum}</A></TD>
- <TD><A HREF="${p}edit/svc_acct_pop.cgi?$hashref->{popnum}">
- $hashref->{city}</A></TD>
- <TD><A HREF="${p}edit/svc_acct_pop.cgi?$hashref->{popnum}">
- $hashref->{state}</A></TD>
- <TD><A HREF="${p}edit/svc_acct_pop.cgi?$hashref->{popnum}">
- $hashref->{ac}</A></TD>
- <TD><A HREF="${p}edit/svc_acct_pop.cgi?$hashref->{popnum}">
- $hashref->{exch}</A></TD>
- <TD><A HREF="${p}edit/svc_acct_pop.cgi?$hashref->{popnum}">
- $hashref->{loc}</A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->popnum %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->city %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->state %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->ac %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->exch %></A></TD>
+ <TD><A HREF="<%= $svc_acct_pop_link %>">
+ <%= $svc_acct_pop->loc %></A></TD>
+ <TD>
+ <FONT COLOR="#00CC00"><B><%= $num_accounts %></B></FONT>
+ <% if ( $num_accounts ) { %><A HREF="<%= $svc_acct_link %>"><% } %>
+ active
+ <% if ( $num_accounts ) { %></A><% } %>
+ </TD>
</TR>
-END
+<% } %>
-}
-
-print <<END;
<TR>
</TR>
</TABLE>
</BODY>
</HTML>
-END
-%>
<li>Credit card decline alerts: Customize the <a href="../config/config.cgi#declinetemplate">declinetemplate</a> configuration option and set the <a href="../config/config.cgi#emaildecline">emaildecline</a> configuration option.
<li>Optional: Invoice template customization
<ul>
- <li>See the <a href="http://search.cpan.org/doc/MJD/Text-Template-1.42/Template.pm">Text::Template</a> documentation for details on the substitution language.
+ <li>See the <a href="http://search.cpan.org/~mjd/Text-Template/lib/Text/Template.pm">Text::Template</a> documentation for details on the substitution language.
<li>You <b>must</b> call the invoice_lines() function at least once - pass it a number of lines, and it returns a list of array references, each of two elements: a service description column, and a price column. Alternatively, call invoice_lines() with no arguments, and pagination will be disabled - all invoice line items will print on one page, with no padding (recommended for email invoices).
<li>In addition, the following variables are available:
<ul>
--- /dev/null
+<HTML>
+ <HEAD>
+ <TITLE>
+ CVV2 information
+ </TITLE>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ The CVV2 number (also called CVC2 or CID) is a three- or four-digit
+ security code used to reduce credit card fraud.<BR><BR>
+ <TABLE BORDER=0 CELLSPACING=4>
+ <TR>
+ <TH>Visa / MasterCard / Discover</TH>
+ <TH>American Express</TH>
+ </TR>
+ <TR>
+ <TD>
+ <IMG BORDER=0 ALT="Visa/MasterCard/Discover" SRC="../images/cvv2.png">
+ </TD>
+ <TD>
+ <IMG BORDER=0 ALT="American Express" SRC="../images/cvv2_amex.png">
+ </TD>
+ </TABLE>
+ <CENTER><A HREF="javascript:close()">(close window)</A></CENTER>
+ </BODY>
+</HTML>
--- /dev/null
+<pre>
+this is incomplete
+mostly it should be merged into signup.html and fs_signup/ieak.template
+
+- download and install the IEAK from
+ http://www.microsoft.com/windows/ieak/default.asp
+
+- Good examples may be found in
+ C:\Program Files\IEAK\toolkit\isp\server\ICW\signup\perl\signup08.pl
+ C:\Program Files\IEAK\toolkit\isp\server\ICW\reconfig\perl\reconfig04.pl
+ C:\Program Files\IEAK6\toolkit\isp\servless\basic\sample.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\4567.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\4568.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\7890.ins
+ C:\Program Files\IEAK6\toolkit\isp\servless\advanced\7891.ins
+
+- Full documentation on all the settings available in .INS files is
+ avaialble under Program Files | Microsoft IEAK 6 | IEAK Help
+ | Reference | Internet Settings (.ins) Files
+
+- Freeside will make the following substitutions before sending the file
+ to the user:
+
+ { $ac } - area code of selected POP
+ { $exch } - exchange of selected POP
+ { $loc } - local part of selected POP
+ { $username }
+ { $password }
+ { $email_name } - first and last name
+ { $pkg } - package name
+
+- Simple example follows:
+
+[Entry]
+Entry Name = IEAK Sample
+[Phone]
+Dial_As_Is = No
+Phone_Number = { $exch }{ $loc }
+Area_Code = { $ac }
+Country_Code = 1
+Country_Id = 1
+[Server]
+Type = PPP
+SW_Compress = Yes
+PW_Encrypt = Yes
+Negotiate_TCP/IP = Yes
+Disable_LCP = No
+[TCP/IP]
+Specity_IP_Address = No
+Specity_Server_Address = No
+IP_Header_Compress = Yes
+Gateway_On_Remote = Yes
+[User]
+Name = { $username }
+Passowrd = { $password }
+Display_Password = Yes
+[Internet_Mail]
+Email_Name = { $email_name }
+Email_Address = { $username }@example.com
+POP_Server = mail.example.com
+POP_Server_Port_Number = 110
+POP_Logon_Password = { $password }
+SMTP_Server = mail.example.com
+SMTP_Server_Port_Number = 25
+Install_Mail = 1
+[URL]
+Help_Page = http://www.ieaksample.net/helpdesk
+Home_Page = http://www.ieaksample.net
+Search_Page = http://www.ieaksample,net/search
+[Favorites]
+IEAK Sample \\ IEAK Sample Home Page.url = http://acme.ieaksample.net/
+[Branding]
+Window_Title = Internet Explorer from Acme Internet Services
+
+</pre>
<li><a href="upgrade7.html">Upgrading from 1.3.0 to 1.3.1</a>
<li><a href="upgrade8.html">Upgrading from 1.3.1 to 1.4.0</a>
<li><a href="upgrade9.html">Upgrading from 1.4.0 to 1.4.1</a>
+ <li><a href="upgrade-1.4.2.html">Upgrading from 1.4.1 to 1.4.2</a>
<li><a href="upgrade10.html">Upgrading from 1.4.1 (or 1.4.2?) to 1.5.0</a>
<!--
<li><a href="config.html">Configuration files</a>
</head>
<body>
<h1>Installation</h1>
+<i>Note: Install Freeside on a firewalled, private server, not a public (web, RADIUS, etc.) server.</i><br><br>
Before installing, you need:
<ul>
- <li><a href="http://www.perl.com/">Perl</a> Don't enable experimental features like threads or the PerlIO abstraction layer.
+ <li><a href="http://www.perl.com/">Perl</a>
<li><a href="http://www.apache.org">Apache</a> (<a href="http://www.modssl.org/">mod_ssl</a> or <a href="http://www.apache-ssl.org">Apache-SSL</a> highly recommended)
<li><a href="http://perl.apache.org/">mod_perl</a> (if compiling your own mod_perl, make sure you set the <a href="http://perl.apache.org/guide/install.html#EVERYTHING">EVERYTHING</a>=1 compile-time option)
<li><a href="http://www.openssh.com/">SSH</a> (<a href="http://www.openssh.com//">OpenSSH</a> is recommended. SSH Communications Security <a href="http://www.ssh.com/products/ssh/download.cfm">commercial SSH version 3</a> has been reported incompatible with Freeside.)
<!-- <li>MySQL has been reported to work. -->
<b>MySQL's default <a href="http://www.mysql.com/doc/M/y/MyISAM.html">MyISAM</a> and <a href="http://www.mysql.com/doc/I/S/ISAM.html">ISAM</a> table types are not supported</b>. If you want to use MySQL, you <b>must</b> use one of the new <a href="http://www.mysql.com/doc/T/a/Table_types.html">transaction-safe table types</a> such as <a href="http://www.mysql.com/doc/B/D/BDB.html">BDB</a> or <a href="http://www.mysql.com/doc/I/n/InnoDB.html">InnoDB</a>, and set it as the default table type using the <code>--default-table-type=BDB</code> or <code>--default-table-type=InnoDB</code> <a href="http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Database_Administration.html#Command-line_options">mysqld command-line option</a> or by setting <code>default-table-type=BDB</code> or <code>default-table-type=InnoDB</code> in the <a href="http://www.mysql.com/documentation/mysql/bychapter/manual_MySQL_Database_Administration.html#Option_files">my.cnf option file</a>.
</ul>
- <li>Perl modules (<a href="http://theoryx5.uwinnipeg.ca/CPAN/perl/CPAN.html">CPAN</a> will query, download and build perl modules automatically)
+ <li>Perl modules (<a href="http://search.cpan.org/~andk/CPAN/lib/CPAN.pm">CPAN</a> will query, download and build perl modules automatically)
<ul>
<!-- <li><a href="http://search.cpan.org/search?dist=Array-PrintCols">Array-PrintCols</a>
<li><a href="http://search.cpan.org/search?dist=Term-Query">Term-Query</a> (make test broken; install manually) -->
<font size="-1"><pre>
PerlModule Apache::ASP
<Directory /usr/local/apache/htdocs/freeside-asp>
-<Files ~ (\.cgi)>
-AddHandler perl-script .cgi
+<Files ~ (\.cgi|\.html)>
+SetHandler perl-script
PerlHandler Apache::ASP
</Files>
<Perl>
$MLDBM::RemoveTaint = 1;
</Perl>
PerlSetVar Global /usr/local/etc/freeside/asp-global/
-PerlSetVar Debug 2
+PerlSetVar Debug 2
+PerlSetVar RequestBinaryRead Off
</Directory>
</pre></font>
</ul></td>
<td><ul>
<li>Run <tt>make masondocs</tt>
<li>Copy <tt>masondocs/</tt> to your web server's document space. (For example: <tt>/usr/local/apache/htdocs/freeside-mason</tt>)
- <li>Copy <tt>htetc/handler.pl</tt> to <tt>/usr/local/etc/freeside</tt> (use htetc/handler.pl-1.0x for Mason versions before 1.10).
+ <li>Copy <tt>htetc/handler.pl</tt> to <tt>/usr/local/etc/freeside</tt>
<li>Edit <tt>handler.pl</tt> and:
<ul>
<li> set an appropriate <tt>comp_root</tt>, such as <tt>/usr/local/apache/htdocs/freeside-mason</tt>
<font size="-1"><pre>
PerlModule HTML::Mason
<Directory /usr/local/apache/htdocs/freeside-mason>
-<Files ~ (\.cgi)>
-AddHandler perl-script .cgi
+<Files ~ (\.cgi|.html)>
+SetHandler perl-script
PerlHandler HTML::Mason
</Files>
<Perl>
</head>
<body>
<h1>Importing legacy data</h1>
-<font size="+2">In most cases, legacy data import all cases will require writing custom code to deal with your particular legacy data. The example scripts here will not work "out-of-the-box". Importing your legacy data will most probably involve some hacking on the example scripts noted below. Contributions to the import process are welcome.</font>
+<font size="+2">In almost all cases, legacy data import will require writing custom code to deal with your particular legacy data. The example scripts here will probably <b>not</b> work "out-of-the-box", and are provided <b>as a starting point only</b>.</font>
<br><br><i>Some import scripts may require installation of the <a href="http://search.cpan.org/search?dist=Array-PrintCols">Array-PrintCols</a> and <a href="http://search.cpan.org/search?dist=Term-Query">Term-Query</a> (make test broken; install manually) modules.</i><br>
<ul>
<li><a name="bind">bin/bind.import</a> - Import domain information from BIND named
<li>typenum - <a href="#agent_type">agent type</a>
<li>prog - (unimplemented)
<li>freq - (unimplemented)
+ <li>disabled - Disabled flag, empty or 'Y'
+ <li>username - Username for the Agent interface
+ <li>_password - Password for the Agent interface
</ul>
<li><a name="agent_type" href="man/FS/agent_type.html">agent_type</a> - Agent types define groups of packages that you can then assign to particular agents.
<ul>
<li><i>ship_fax</i>
<li>payby - CARD, DCHK, CHEK, DCHK, LECB, BILL, or COMP
<li>payinfo - card number, P.O.#, or comp issuer
+ <li>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
<li>paydate - expiration date
<li>payname - billing name (name on card)
<li>tax - tax exempt, Y or null
<li>taxclass
<li>exempt_amount
<li>taxname - if defined, printed on invoices instead of "Tax"
+ <li>setuptax - if 'Y', this tax does not apply to setup fees
+ <li>recurtax - if 'Y', this tax does not apply to recurring fees
</ul>
<li><a name="cust_tax_exempt" href="man/FS/cust_tax_exempt.html">cust_tax_exempt</a> - Tax exemption record
<ul>
<li>pkgpart - <a href="#part_pkg">Package definition</a>
<li>svcpart - <a href="#part_svc">Service definition</a>
<li>quantity - quantity of this service that this package includes
+ <li>primary_svc - blank or Y: primary service
</ul>
<li><a name="export_svc" href="man/FS/export_svc.html">export_svc</a>
<ul>
<ul>
<li>svcnum - <a href="#cust_svc">primary key</a>
<li>srcsvc - <a href="#svc_acct">svcnum of the source of this forward</a>
+ <li>src - literal source (username or full email address)
<li>dstsvc - <a href="#svc_acct">svcnum of the destination of this forward</a>
- <li>dst - foreign destination (email address) - forward not local to freeside
+ <li>dst - literal destination (username or full email address)
</ul>
<li><a name="domain_record" href="man/FS/domain_record.html">domain_record</a> - Domain zone detail
<ul>
<li><a href="http://search.cpan.org/search?dist=Text-Template">Text::Template</a>
<li><a href="http://search.cpan.org/search?dist=Storable">Storable</a>
<li><a href="http://search.cpan.org/search?dist=Business-CreditCard">Business-CreditCard</a>
- <li><a href="http://www.sisd.com/useragent">HTTP::Headers::UserAgent</a> (version 2.0 or higher; not yet indexed correctly on CPAN)
+ <li><a href="http://search.cpan.org/search?dist=HTTP-BrowserDetect">HTTP::BrowserDetect</a>
<li><a href="man/FS/SignupClient.html">FS::SignupClient</a> (copy the fs_signup/FS-SignupClient directory to the external machine, then: perl Makefile.PL; make; make install)
</ul>
Optional:
<ul>
<li>If you create a <b>/usr/local/freeside/ieak.template</b> file on the external machine, it will be sent to IE users with MIME type <i>application/x-Internet-signup</i>. This file will be processed with <a href="http://search.cpan.org/doc/MJD/Text-Template-1.23/Template.pm">Text::Template</a> with the variables listed below available.
- (an example file is included as <b>fs_signup/ieak.template</b>) See the <a href="http://www.microsoft.com/windows/ieak/techinfo/deploy/60/en/toc.asp">IEAK documentation</a> for more information.
- <li>If you create a <b>/usr/local/freeside/cck.template</b> file on the external machine, the variables defined will be sent to Netscape users with MIME type <i>application/x-netscape-autoconfigure-dialer-v2</i>. This file will be processed with <a href="http://search.cpan.org/doc/MJD/Text-Template-1.23/Template.pm">Text::Template</a> with the variables listed below available.
- (an example file is included as <b>fs_signup/cck.template</b>). See the <a href="http://help.netscape.com/products/client/mc/acctproc4.html">Netscape documentation</a> for more information.
+ (an example file is included as <b>fs_signup/ieak.template</b>) See the section on <a href="http://www.microsoft.com/windows/ieak/techinfo/deploy/60/en/INS.HTM">internet settings files</a> in the <a href="http://www.microsoft.com/windows/ieak/techinfo/deploy/60/en/toc.asp">IEAK documentation</a> for more information.
<li>If you create a <b>/usr/local/freeside/success.html</b> file on the external machine, it will be used as the success HTML page. Although template substiutions are available, a regular HTML file will work fine here, unlike signup.html. An example file is included as <b>fs_signup/FS-SignupClient/cgi/success.html</b>
<li>Variable substitutions available in <b>ieak.template</b>, <b>cck.template</b> and <b>success.html</b>:
<ul>
--- /dev/null
+<head>
+ <title>Upgrading to 1.4.2</title>
+</head>
+<body>
+<h1>Upgrading to 1.4.2 from 1.4.1</h1>
+<ul>
+ <li>If migrating from less than 1.4.1, see these <a href="upgrade9.html">instructions</a> first.
+ <li>Back up your data and current Freeside installation.
+ <li>Install <a href="http://search.cpan.org/search?dist=Locale-SubCountry">Locale::SubCountry</a>
+ <li>Install <a href="http://search.cpan.org/search?dist=IPC-ShareLite">IPC::ShareLite</a>
+ <li>Install <a href="http://search.cpan.org/search?dist=HTML-Widgets-SelectLayers">HTML::Widgets::SelectLayers</a> 0.03.
+ <li>CGI.pm minimum version 2.47 is required. You will probably need to install a current CGI.pm from CPAN if you are using Perl 5.005 or earlier.
+ <li>If using Apache::ASP, add <code>PerlSetVar RequestBinaryRead Off</code> to your Apache configuration and make sure you are using Apache::ASP minimum version 2.55.
+ <li>Run <code>make aspdocs</code> or <code>make masondocs</code>.
+ <li>Copy <code>aspdocs/</code> or <code>masondocs/</code> to your web server's document space.
+ <li>Run <code>make install-perl-modules</code>.
+ <li>The signup server and password server are deprecated in 1.4.2. Their functionality has been incorperated into the self-service server. Edit or reinstall your init script, and set the "signup_server-default_agentnum" and "signup_server-default_refnum" configuration options. The FS::SignupClient interface is still available as a compatibility wrapper, so you should be able to continue to use your current signup.cgi.
+ <li>Optional: To use typeset invoices, install tetex and ghostscript, and copy conf/invoice_latex, conf/invoice_latexnotes, and conf/invoice_latexfooter to /usr/local/etc/freeside/conf.<datasrc>/
+ <li>Restart Apache and freeside-queued.
+</body>
<pre>
this is incomplete
-install DBIx::DBSchema 0.21
+install DBIx::DBSchema 0.22
-install NetAddr::IP and Chart::Base
+install NetAddr::IP, Chart::Base, IPC::ShareLite and Locale::SubCountry
CREATE TABLE cust_bill_pkg_detail (
detailnum serial,
PRIMARY KEY (svcnum)
);
-DELETE INDEX cust_bill_pkg1;
+CREATE TABLE acct_snarf (
+ snarfnum serial,
+ svcnum int NOT NULL,
+ machine varchar(255) NULL,
+ protocol varchar(80) NULL,
+ username varchar(80) NULL,
+ _password varchar(80) NULL,
+ PRIMARY KEY (snarfnum)
+);
+CREATE INDEX acct_snarf1 ON acct_snarf ( svcnum );
+
+CREATE TABLE svc_external (
+ svcnum int NOT NULL,
+ id int NOT NULL,
+ title varchar(80),
+ PRIMARY KEY (svcnum)
+);
+
+CREATE TABLE part_pkg_temp (
+ pkgpart serial NOT NULL,
+ pkg varchar(80) NOT NULL,
+ "comment" varchar(80) NOT NULL,
+ setup text NULL,
+ freq varchar(80) NOT NULL,
+ recur text NULL,
+ setuptax char(1) NULL,
+ recurtax char(1) NULL,
+ plan varchar(80) NULL,
+ plandata text NULL,
+ disabled char(1) NULL,
+ taxclass varchar(80) NULL,
+ PRIMARY KEY (pkgpart),
+);
+INSERT INTO part_pkg_temp SELECT * from part_pkg;
+DROP TABLE part_pkg;
+ALTER TABLE part_pkg_temp RENAME TO part_pkg;
+ALTER TABLE part_pkg DROP CONSTRAINT part_pkg_temp_pkey;
+ALTER TABLE part_pkg ADD PRIMARY KEY (pkgpart);
+CREATE INDEX part_pkg1 ON part_pkg(disabled);
+select setval('public.part_pkg_temp_pkgpart_seq', ( select max(pkgpart) from part_pkg) ); #?
+
+CREATE TABLE h_part_pkg_temp (
+ historynum serial NOT NULL,
+ history_date int,
+ history_user varchar(80) NOT NULL,
+ history_action varchar(80) NOT NULL,
+ pkgpart int NOT NULL,
+ pkg varchar(80) NOT NULL,
+ "comment" varchar(80) NOT NULL,
+ setup text NULL,
+ freq varchar(80) NOT NULL,
+ recur text NULL,
+ setuptax char(1) NULL,
+ recurtax char(1) NULL,
+ plan varchar(80) NULL,
+ plandata text NULL,
+ disabled char(1) NULL,
+ taxclass varchar(80) NULL,
+ PRIMARY KEY (historynum)
+);
+INSERT INTO h_part_pkg_temp SELECT * from h_part_pkg;
+DROP TABLE h_part_pkg;
+ALTER TABLE h_part_pkg_temp RENAME TO h_part_pkg;
+ALTER TABLE h_part_pkg DROP CONSTRAINT h_part_pkg_temp_pkey;
+ALTER TABLE h_part_pkg ADD PRIMARY KEY (historynum);
+CREATE INDEX h_part_pkg1 ON h_part_pkg(disabled);
+select setval('public.h_part_pkg_temp_historynum_seq', ( select max(historynum) from h_part_pkg) );
+
+
+DROP INDEX cust_bill_pkg1;
ALTER TABLE cust_bill_pkg ADD itemdesc varchar(80) NULL;
ALTER TABLE h_cust_bill_pkg ADD itemdesc varchar(80) NULL;
ALTER TABLE cust_main_county ADD taxname varchar(80) NULL;
ALTER TABLE h_cust_main_county ADD taxname varchar(80) NULL;
+ALTER TABLE cust_main_county ADD setuptax char(1) NULL;
+ALTER TABLE h_cust_main_county ADD setuptax char(1) NULL;
+ALTER TABLE cust_main_county ADD recurtax char(1) NULL;
+ALTER TABLE h_cust_main_county ADD recurtax char(1) NULL;
ALTER TABLE cust_pkg ADD last_bill int NULL;
ALTER TABLE h_cust_pkg ADD last_bill int NULL;
+ALTER TABLE agent ADD disabled char(1) NULL;
+ALTER TABLE h_agent ADD disabled char(1) NULL;
+ALTER TABLE agent ADD username varchar(80) NULL;
+ALTER TABLE h_agent ADD username varchar(80) NULL;
+ALTER TABLE agent ADD _password varchar(80) NULL;
+ALTER TABLE h_agent ADD _password varchar(80) NULL;
+ALTER TABLE cust_main ADD paycvv varchar(4) NULL;
+ALTER TABLE h_cust_main ADD paycvv varchar(4) NULL;
+ALTER TABLE part_referral ADD disabled char(1) NULL;
+ALTER TABLE h_part_referral ADD disabled char(1) NULL;
+CREATE INDEX part_referral1 ON part_referral ( disabled );
+ALTER TABLE pkg_svc ADD primary_svc char(1) NULL;
+ALTER TABLE h_pkg_svc ADD primary_svc char(1) NULL;
+ALTER TABLE svc_forward ADD src varchar(255) NULL;
+ALTER TABLE h_svc_forward ADD src varchar(255) NULL;
+
+On recent Pg versions:
+
+ALTER TABLE svc_forward ALTER COLUMN srcsvc DROP NOT NULL;
+ALTER TABLE h_svc_forward ALTER COLUMN srcsvc DROP NOT NULL;
+ALTER TABLE svc_forward ALTER COLUMN dstsvc DROP NOT NULL;
+ALTER TABLE h_svc_forward ALTER COLUMN dstsvc DROP NOT NULL;
+
+Or on Pg versions that don't support DROP NOT NULL (tested only on 7.2 so far):
+UPDATE pg_attribute SET attnotnull = FALSE WHERE ( attname = 'srcsvc' OR attname = 'dstsvc' ) AND ( attrelid = ( SELECT oid FROM pg_class WHERE relname = 'svc_forward' ) OR attrelid = ( SELECT oid FROM pg_class WHERE relname = 'h_svc_forward' ) );
dump database, edit:
- cust_main: increase otaker from 8 to 32
CREATE INDEX cust_main9 ON cust_main ( ship_daytime );
CREATE INDEX cust_main10 ON cust_main ( ship_night );
CREATE INDEX cust_main11 ON cust_main ( ship_fax );
+ CREATE INDEX agent2 ON agent ( disabled );
+ CREATE INDEX part_bill_event2 ON part_bill_event ( disabled );
serial columns
mandatory again:
dbdef-create username
-create-history-tables username cust_bill_pkg_detail router part_svc_router addr_block svc_broadband
+create-history-tables username cust_bill_pkg_detail router part_svc_router addr_block svc_broadband acct_snarf svc_external
dbdef-create username
-
+apache - fix <Files> sections to include .html also
</pre>
CREATE INDEX part_svc1 ON part_svc ( disabled );
CREATE INDEX cust_bill2 ON cust_bill ( _date );
</pre>
- <li>If you want to use ACH (electronic checks), you will need to make changes to your database. The easiest way to make these changes is to dump your database (with pg_dump), change the payinfo field in the cust_pay, cust_refund, h_cust_pay and h_cust_refund tables from varchar(16) to varchar(80), reload the database from the dump, and run dbdef-create
+ <li>If you want to use ACH (electronic checks), you will need to make changes to your database. The easiest way to make these changes is to dump your database (with pg_dump), change the payinfo field in the cust_pay, cust_refund, h_cust_pay and h_cust_refund tables from varchar(16) to varchar(80), reload the database from the dump.
+ <li>If you will be doing bind exports you should make additional changes to your database. Follow the directions above to dump the database and change the reczone and recdata fields in the domain_record and h_domain_record tables from varchar(80) to varchar(255).
+ <li>If you made changes to your db schema from a dump as listed above run dbdef-create.
<li>Restart Apache and freeside-queued.
</body>
<!-- mason kludge -->
<%
-# <!-- $Id: REAL_cust_pkg.cgi,v 1.5 2003-04-01 01:22:24 ivan Exp $ -->
+# <!-- $Id: REAL_cust_pkg.cgi,v 1.7 2003-11-19 12:21:09 ivan Exp $ -->
my $error ='';
my $pkgnum = '';
# 'Main Menu' => popurl(2)
#));
+%>
+
+ <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
+
+<%
+
#print info
my($susp,$cancel,$expire)=(
$cust_pkg->getfield('susp'),
print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: $error</FONT>!
if $error;
+#my $format = "%c %z (%Z)";
+my $format = "%m/%d/%Y %T %z (%Z)";
+
print ntable("#cccccc",2),
'<TR><TD ALIGN="right">Package number</TD><TD BGCOLOR="#ffffff">',
$pkgnum, '</TD></TR>',
'<TR><TD ALIGN="right">Order taker</TD><TD BGCOLOR="#ffffff">',
$otaker, '</TD></TR>',
'<TR><TD ALIGN="right">Setup date</TD><TD>'.
- '<INPUT TYPE="text" NAME="setup" SIZE=32 VALUE="',
- ( $setup ? time2str("%c %z (%Z)",$setup) : "" ), '"></TD></TR>';
+ '<INPUT TYPE="text" NAME="setup" SIZE=32 ID="setup_text" VALUE="',
+ ( $setup ? time2str($format, $setup) : "" ), '">'.
+ ' <IMG SRC="../images/calendar.png" ID="setup_button" STYLE="cursor: pointer" TITLE="Select date">'.
+ '</TD></TR>';
print '<TR><TD ALIGN="right">Last bill date</TD><TD>',
- '<INPUT TYPE="text" NAME="last_bill" SIZE=32 VALUE="',
+ '<INPUT TYPE="text" NAME="last_bill" SIZE=32 ID="last_bill_text" VALUE="',
( $cust_pkg->last_bill
- ? time2str("%c %z (%Z)", $cust_pkg->last_bill)
+ ? time2str($format, $cust_pkg->last_bill)
: "" ),
- '"></TD></TR>'
+ '">'.
+ ' <IMG SRC="../images/calendar.png" ID="last_bill_button" STYLE="cursor: pointer" TITLE="Select date">'.
+ '</TD></TR>'
if $cust_pkg->dbdef_table->column('last_bill');
print '<TR><TD ALIGN="right">Next bill date</TD><TD>',
- '<INPUT TYPE="text" NAME="bill" SIZE=32 VALUE="',
- ( $bill ? time2str("%c %z (%Z)",$bill) : "" ), '"></TD></TR>';
+ '<INPUT TYPE="text" NAME="bill" SIZE=32 ID="bill_text" VALUE="',
+ ( $bill ? time2str($format, $bill) : "" ), '">'.
+ ' <IMG SRC="../images/calendar.png" ID="bill_button" STYLE="cursor: pointer" TITLE="Select date">'.
+ '</TD></TR>';
print '<TR><TD ALIGN="right">Suspension date</TD><TD BGCOLOR="#ffffff">',
- time2str("%D",$susp), '</TD></TR>'
+ time2str($format, $susp), '</TD></TR>'
if $susp;
#print '<TR><TD ALIGN="right">Expiration date</TD><TD BGCOLOR="#ffffff">',
# if $expire;
print '<TR><TD ALIGN="right">Expiration date'.
'</TD><TD>',
- '<INPUT TYPE="text" NAME="expire" SIZE=32 VALUE="',
- ( $expire ? time2str("%c %z (%Z)",$expire) : "" ), '">'.
+ '<INPUT TYPE="text" NAME="expire" SIZE=32 ID="expire_text" VALUE="',
+ ( $expire ? time2str($format, $expire) : "" ), '">'.
+ ' <IMG SRC="../images/calendar.png" ID="expire_button" STYLE="cursor: pointer" TITLE="Select date">'.
'<BR><FONT SIZE=-1>(will <b>cancel</b> this package'.
' when the date is reached)</FONT>'.
'</TD></TR>';
print '<TR><TD ALIGN="right">Cancellation date</TD><TD BGCOLOR="#ffffff">',
- time2str("%D",$cancel), '</TD></TR>'
+ time2str($format, $cancel), '</TD></TR>'
if $cancel;
%>
</TABLE>
+<SCRIPT TYPE="text/javascript">
+<%
+ my @cal = qw( setup bill expire );
+ push @cal, 'last_bill'
+ if $cust_pkg->dbdef_table->column('last_bill');
+ foreach my $cal (@cal) {
+%>
+ Calendar.setup({
+ inputField: "<%= $cal %>_text",
+ ifFormat: "%m/%d/%Y",
+ button: "<%= $cal %>_button",
+ align: "BR"
+ });
+<% } %>
+</SCRIPT>
<BR><INPUT TYPE="submit" VALUE="Apply Changes">
</FORM>
</BODY>
my $action = $agent->agentnum ? 'Edit' : 'Add';
my $hashref = $agent->hashref;
-print header("$action Agent", menubar(
+%>
+
+<%= header("$action Agent", menubar(
'Main Menu' => $p,
'View all agents' => $p. 'browse/agent.cgi',
-));
+)) %>
-print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
- "</FONT>"
- if $cgi->param('error');
+<% if ( $cgi->param('error') ) { %>
+<FONT SIZE="+1" COLOR="#ff0000">Error: <%= $cgi->param('error') %></FONT>
+<% } %>
-print '<FORM ACTION="', popurl(1), 'process/agent.cgi" METHOD=POST>',
- qq!<INPUT TYPE="hidden" NAME="agentnum" VALUE="$hashref->{agentnum}">!,
- "Agent #", $hashref->{agentnum} ? $hashref->{agentnum} : "(NEW)";
+<FORM ACTION="<%=popurl(1)%>process/agent.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="agentnum" VALUE="<%= $hashref->{agentnum} %>">
+Agent #<%= $hashref->{agentnum} ? $hashref->{agentnum} : "(NEW)" %>
-print &ntable("#cccccc", 2, ''), <<END;
+<%= &ntable("#cccccc", 2, '') %>
<TR>
<TH ALIGN="right">Agent</TH>
- <TD><INPUT TYPE="text" NAME="agent" SIZE=32 VALUE="$hashref->{agent}"></TD>
+ <TD><INPUT TYPE="text" NAME="agent" SIZE=32 VALUE="<%= $hashref->{agent} %>"></TD>
</TR>
<TR>
<TH ALIGN="right">Agent type</TH>
<TD><SELECT NAME="typenum" SIZE=1>
-END
-foreach my $agent_type (qsearch('agent_type',{})) {
- print "<OPTION VALUE=". $agent_type->typenum;
- print " SELECTED"
- if $hashref->{typenum} && ( $hashref->{typenum} == $agent_type->typenum );
- print ">", $agent_type->getfield('typenum'), ": ",
- $agent_type->getfield('atype'),"\n";
-}
+<% foreach my $agent_type (qsearch('agent_type',{})) { %>
+ <OPTION VALUE="<%= $agent_type->typenum %>"<%= ( $hashref->{typenum} && ( $hashref->{typenum} == $agent_type->typenum ) ) ? ' SELECTED' : '' %>>
+ <%= $agent_type->getfield('typenum') %>: <%= $agent_type->getfield('atype') %>
+<% } %>
-print <<END;
</SELECT></TD>
</TR>
+<% if ( dbdef->table('agent')->column('disabled') ) { %>
+ <TR>
+ <TD ALIGN="right">Disable</TD>
+ <TD><INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<%= $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>></TD>
+ </TR>
+<% } %>
<TR>
<TD ALIGN="right"><!--Frequency--></TD>
- <TD><INPUT TYPE="hidden" NAME="freq" VALUE="$hashref->{freq}"></TD>
+ <TD><INPUT TYPE="hidden" NAME="freq" VALUE="<%= $hashref->{freq} %>"></TD>
</TR>
<TR>
<TD ALIGN="right"><!--Program--></TD>
- <TD><INPUT TYPE="hidden" NAME="prog" VALUE="$hashref->{prog}"></TD>
+ <TD><INPUT TYPE="hidden" NAME="prog" VALUE="<%= $hashref->{prog} %>"></TD>
</TR>
+<% if ( dbdef->table('agent')->column('username') ) { %>
+ <TR>
+ <TD ALIGN="right">Agent interface username</TD>
+ <TD><INPUT TYPE="text" NAME="username" VALUE="<%= $hashref->{username} %>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Agent interface password</TD>
+ <TD><INPUT TYPE="text" NAME="_password" VALUE="<%= $hashref->{_password} %>"></TD>
+ </TR>
+<% } %>
</TABLE>
-END
-print qq!<BR><INPUT TYPE="submit" VALUE="!,
- $hashref->{agentnum} ? "Apply changes" : "Add agent",
- qq!">!;
-
-print <<END;
+<BR><INPUT TYPE="submit" VALUE="<%= $hashref->{agentnum} ? "Apply changes" : "Add agent" %>">
</FORM>
</BODY>
</HTML>
-END
-
-%>
$query =~ /^(\d+)$/;
$custnum=$1;
$cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+ if ( $cust_main->dbdef_table->column('paycvv')
+ && length($cust_main->paycvv) ) {
+ my $paycvv = $cust_main->paycvv;
+ $paycvv =~ s/./*/g;
+ $cust_main->paycvv($paycvv);
+ }
$saved_pkgpart = 0;
$username = '';
$password = '';
# top
my $p1 = popurl(1);
-print header("Customer $action", '');
+print header("Customer $action", '', ' onUnload="myclose()"');
print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $error, "</FONT>"
if $error;
my $r = qq!<font color="#ff0000">*</font> !;
-my @agents = qsearch( 'agent', {} );
+my %agent_search = dbdef->table('agent')->column('disabled')
+ ? ( 'disabled' => '' ) : ();
+my @agents = qsearch( 'agent', \%agent_search );
#die "No agents created!" unless @agents;
-eidiot "You have not created any agents. You must create at least one agent before adding a customer. Go to ". popurl(2). "browse/agent.cgi and create one or more agents." unless @agents;
+eidiot "You have not created any agents (or all agents are disabled). You must create at least one agent before adding a customer. Go to ". popurl(2). "browse/agent.cgi and create one or more agents." unless @agents;
my $agentnum = $cust_main->agentnum || $agents[0]->agentnum; #default to first
if ( scalar(@agents) == 1 ) {
print qq!<INPUT TYPE="hidden" NAME="agentnum" VALUE="$agentnum">!;
my $countrydefault = $conf->config('countrydefault') || 'US';
$cust_main->country( $countrydefault ) unless $cust_main->country;
-$cust_main->state( $conf->config('statedefault') || 'CA' )
- unless $cust_main->state || $cust_main->country ne 'US';
+my $statedefault = $conf->config('statedefault')
+ || ($countrydefault eq 'US' ? 'CA' : '');
+$cust_main->state( $statedefault )
+ unless $cust_main->state || $cust_main->country ne $countrydefault;
my($county_html, $state_html, $country_html) =
FS::cust_main_county::regionselector( $cust_main->county,
#false laziness with regular state
$cust_main->ship_country( $countrydefault ) unless $cust_main->ship_country;
- $cust_main->ship_state( $conf->config('statedefault') || 'CA' )
- unless $cust_main->ship_state || $cust_main->ship_country ne 'US';
+ $cust_main->ship_state( $statedefault )
+ unless $cust_main->ship_state
+ || $cust_main->ship_country ne $countrydefault;
my($ship_county_html, $ship_state_html, $ship_country_html) =
FS::cust_main_county::regionselector( $cust_main->ship_county,
$return .= ">$_";
}
$return .= qq!</SELECT>/<SELECT NAME="$prefix!. qq!_year" SIZE="1">!;
- for ( 2001 .. 2037 ) {
+ my @t = localtime;
+ my $thisYear = $t[5] + 1900;
+ for ( ($thisYear > $y && $y > 0 ? $y : $thisYear) .. 2037 ) {
$return .= "<OPTION";
$return .= " SELECTED" if $_ == $y;
$return .= ">$_";
print qq!<TR><TD>Email invoice <INPUT TYPE="text" NAME="invoicing_list" VALUE="$invoicing_list"></TD></TR>!;
print "<TR><TD>Billing type</TD></TR>",
- "</TABLE>",
+ "</TABLE>", '<script language="JavaScript"><!--
+ var mywindow = -1;
+ function myopen(filename,windowname,properties) {
+ myclose();
+ mywindow = window.open(filename,windowname,properties);
+ }
+ function myclose() {
+ if ( mywindow != -1 )
+ mywindow.close();
+ mywindow = -1;
+ }
+
+ //--></script>',
&table("#cccccc"), "<TR>";
my($payinfo, $payname)=(
'COMP' => qq!Complimentary<BR>${r}Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""><BR>${r}Exp !. expselect("COMP"),
);
+ if ( $cust_main->dbdef_table->column('paycvv') ) {
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5 bs
+ $payby{$payby} .= qq!<BR>CVV2 (<A HREF="javascript:myopen('../docs/cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>) <INPUT TYPE="text" NAME=${payby}_paycvv VALUE="" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
my( $account, $aba ) = split('@', $payinfo);
my %paybychecked = (
'COMP' => qq!Complimentary<BR>${r}Approved by<INPUT TYPE="text" NAME="COMP_payinfo" VALUE="$payinfo"><BR>${r}Exp !. expselect("COMP", $cust_main->paydate),
);
+ if ( $cust_main->dbdef_table->column('paycvv') ) {
+ my $paycvv = $cust_main->paycvv;
+
+ foreach my $payby ( grep { exists $payby{$_} } qw(CARD DCRD) ) { #1.4/1.5 bs
+ $paybychecked{$payby} .= qq!<BR>CVV2 (<A HREF="javascript:myopen('../docs/cvv2.html','cvv2','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=yes,copyhistory=no,width=480,height=288')">help</A>) <INPUT TYPE="text" NAME=${payby}_paycvv VALUE="$paycvv" SIZE=4 MAXLENGTH=4>!;
+ }
+ }
+
+
$cust_main->payby($payby_default) unless $cust_main->payby;
for (qw(CARD DCRD CHEK DCHK LECB BILL COMP)) {
print qq!<TD VALIGN=TOP><INPUT TYPE="radio" NAME="payby" VALUE="$_"!;
<TH><FONT SIZE=-1>State</FONT></TH>
<TH><FONT SIZE=-1>County</FONT></TH>
<TH><FONT SIZE=-1>Taxclass</FONT><BR><FONT SIZE=-2>(per-package classification)</FONT></TH>
- <TH><FONT SIZE=-1>Tax name</FONT><BR><FONT SIZE=-2>(printed on invoices)</FONT></TH>
+END
+
+if ( dbdef->table('cust_main_county')->column('taxname') ) {
+ print '<TH><FONT SIZE=-1>Tax name</FONT><BR><FONT SIZE=-2>(printed on invoices)</FONT></TH>';
+}
+
+print <<END;
<TH><FONT SIZE=-1>Tax</FONT></TH>
<TH><FONT SIZE=-1>Exempt<BR>per<BR>month</TH>
- </TR>
END
+if ( dbdef->table('cust_main_county')->column('setuptax') ) {
+ print '<TH><FONT SIZE=-1>Setup<BR>fee<BR>exempt</TH>';
+}
+if ( dbdef->table('cust_main_county')->column('recurtax') ) {
+ print '<TH><FONT SIZE=-1>Recurring<BR>fee<BR>exempt</TH>';
+}
+
+print '</TR>';
+
foreach my $cust_main_county ( sort { $a->country cmp $b->country
or $a->state cmp $b->state
or $a->county cmp $b->county
, "</TD>";
print qq!<TD><INPUT TYPE="text" NAME="taxname!, $hashref->{taxnum},
- qq!" VALUE="!, $hashref->{taxname}, qq!"></TD>!;
- print qq!<TD><INPUT TYPE="text" NAME="tax!, $hashref->{taxnum},
- qq!" VALUE="!, $hashref->{tax}, qq!" SIZE=6 MAXLENGTH=6>%</TD>!;
- print qq!<TD>\$<INPUT TYPE="text" NAME="exempt_amount!, $hashref->{taxnum},
- qq!" VALUE="!, $hashref->{exempt_amount}||0, qq!" SIZE=6></TD>!;
+ qq!" VALUE="!, $hashref->{taxname}, qq!"></TD>!
+ if dbdef->table('cust_main_county')->column('taxname');
+
+ print qq!<TD><TABLE><TR><TD><INPUT TYPE="text" NAME="tax!, $hashref->{taxnum},
+ qq!" VALUE="!, $hashref->{tax}, qq!" SIZE=6 MAXLENGTH=6></TD><TD>%</TD></TR></TABLE></TD>!;
+ print qq!<TD><TABLE><TR><TD>\$</TD><TD><INPUT TYPE="text" NAME="exempt_amount!, $hashref->{taxnum},
+ qq!" VALUE="!, $hashref->{exempt_amount}||0, qq!" SIZE=6></TD></TR></TABLE></TD>!;
+
+ print qq!<TD><INPUT TYPE="checkbox" NAME="setuptax!. $hashref->{taxnum}.
+ '" VALUE="Y"'.
+ ( $hashref->{setuptax} =~ /^Y$/i ? ' CHECKED' : '' ).
+ '></TD>'
+ if dbdef->table('cust_main_county')->column('setuptax');
+
+ print qq!<TD><INPUT TYPE="checkbox" NAME="recurtax!. $hashref->{taxnum}.
+ '" VALUE="Y"'.
+ ( $hashref->{recurtax} =~ /^Y$/i ? ' CHECKED' : '' ).
+ '></TD>'
+ if dbdef->table('cust_main_county')->column('recurtax');
+
print '</TR>';
}
} else {
$part_bill_event ||= new FS::part_bill_event {};
}
-$action ||= $part_bill_event->pkgpart ? 'Edit' : 'Add';
+$action ||= $part_bill_event->eventpart ? 'Edit' : 'Add';
my $hashref = $part_bill_event->hashref;
print header("$action Invoice Event Definition", menubar(
$html .= '</SELECT>';
} elsif ( $type eq 'textarea' ) {
$html .= qq!<TEXTAREA NAME="$option" COLS=80 ROWS=8 WRAP="virtual">!.
- qq!$value</TEXTAREA>!;
+ encode_entities($value). '</TEXTAREA>';
} elsif ( $type eq 'text' ) {
- $html .= qq!<INPUT TYPE="text" NAME="$option" VALUE="$value" SIZE=64>!;
+ $html .= qq!<INPUT TYPE="text" NAME="$option" VALUE="!.
+ encode_entities($value). '" SIZE=64>';
} elsif ( $type eq 'checkbox' ) {
$html .= qq!<INPUT TYPE="checkbox" NAME="$option" VALUE="1"!;
$html .= ' CHECKED' if $value;
#print qq!<INPUT TYPE="hidden" NAME="pkgpart" VALUE="$hashref->{pkgpart}">!,
print "Package Part #", $hashref->{pkgpart} ? $hashref->{pkgpart} : "(NEW)";
-print ntable("#cccccc",2), <<END;
-<TR><TD ALIGN="right">Package (customer-visible)</TD><TD><INPUT TYPE="text" NAME="pkg" SIZE=32 VALUE="$hashref->{pkg}"></TD></TR>
-<TR><TD ALIGN="right">Comment (customer-hidden)</TD><TD><INPUT TYPE="text" NAME="comment" SIZE=32 VALUE="$hashref->{comment}"></TD></TR>
-<TR><TD ALIGN="right">Frequency (months) of recurring fee</TD><TD><INPUT TYPE="text" NAME="freq" VALUE="$hashref->{freq}" SIZE=3> <I>0=no recurring fee, 1=monthly, 3=quarterly, 12=yearly</TD></TR>
-<TR><TD ALIGN="right">Setup fee tax exempt</TD><TD>
-END
+#false laziness w/view/cust_main.cgi
+my %freq;
+tie %freq, 'Tie::IxHash',
+ '0' => '(no recurring fee)',
+ '1d' => 'daily',
+ '1w' => 'weekly',
+ '2w' => 'biweekly (every 2 weeks)',
+ '1' => 'monthly',
+ '2' => 'bimonthly (every 2 months)',
+ '3' => 'quarterly (every 3 months)',
+ '6' => 'semiannually (every 6 months)',
+ '12' => 'annually',
+ '24' => 'biannually (every 2 years)',
+;
+if ( $part_pkg->dbdef_table->column('freq')->type =~ /(int)/i ) {
+ delete $freq{$_} foreach grep { ! /^\d+$/ } keys %freq;
+}
+
+%>
+<%= ntable("#cccccc",2) %>
+ <TR>
+ <TD ALIGN="right">Package (customer-visible)</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="pkg" SIZE=32 VALUE="<%= $part_pkg->pkg %>">
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Comment (customer-hidden)</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="comment" SIZE=32 VALUE="<%=$part_pkg->comment%>">
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Recurring fee frequency </TD>
+ <TD>
+ <SELECT NAME="freq">
+ <% foreach my $freq ( keys %freq ) { %>
+ <OPTION VALUE="<%= $freq %>"<%= $freq eq $part_pkg->freq ? ' SELECTED' : '' %>><%= $freq{$freq} %>
+ <% } %>
+ </SELECT>
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Setup fee tax exempt</TD>
+ <TD>
+<%
print '<INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y"';
print ' CHECKED' if $hashref->{setuptax} eq "Y";
print '>';
print '</TD></TR></TABLE>';
-my $thead = "\n\n". ntable('#cccccc', 2). <<END;
-<TR><TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Quan.</FONT></TH><TH BGCOLOR="#dcdcdc">Service</TH></TR>
-END
+my $thead = "\n\n". ntable('#cccccc', 2).
+ '<TR><TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Quan.</FONT></TH>';
+$thead .= '<TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Primary</FONT></TH>'
+ if dbdef->table('pkg_svc')->column('primary_svc');
+$thead .= '<TH BGCOLOR="#dcdcdc">Service</TH></TR>';
#unless ( $cgi->param('clone') ) {
#dunno why...
'pkgpart' => $pkgpart,
'svcpart' => $svcpart,
} ) || new FS::pkg_svc ( {
- 'pkgpart' => $pkgpart,
- 'svcpart' => $svcpart,
- 'quantity' => 0,
+ 'pkgpart' => $pkgpart,
+ 'svcpart' => $svcpart,
+ 'quantity' => 0,
+ 'primary_svc' => '',
});
#? #next unless $pkg_svc;
print '<TR>'; # if $count == 0 ;
print qq!<TD><INPUT TYPE="text" NAME="pkg_svc$svcpart" SIZE=4 MAXLENGTH=3 VALUE="!,
$cgi->param("pkg_svc$svcpart") || $pkg_svc->quantity || 0,
- qq!"></TD><TD><A HREF="part_svc.cgi?!,$part_svc->svcpart,
+ qq!"></TD>!;
+ if ( dbdef->table('pkg_svc')->column('primary_svc') ) {
+ print qq!<TD><INPUT TYPE="radio" NAME="pkg_svc_primary" VALUE="$svcpart"!;
+ print ' CHECKED' if $pkg_svc->primary_svc =~ /^Y/i;
+ print '></TD>';
+ }
+ print qq!<TD><A HREF="part_svc.cgi?!,$part_svc->svcpart,
qq!">!, $part_svc->getfield('svc'), "</A></TD></TR>";
# print "</TABLE></TD><TD>$thead" if ++$count == int(scalar(@part_svc) / 2);
$count+=1;
'recur' => '\'my $last_bill = $cust_pkg->last_bill; my $hours = $cust_pkg->seconds_since_sqlradacct($last_bill, $sdate ) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; my $input = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctInputOctets\" ) / 1048576; my $output = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctOutputOctets\" ) / 1048576; my $total = $input + $output - \' + what.recur_included_total.value + \'; $total = 0 if $total < 0; my $input = $input - \' + what.recur_included_input.value + \'; $input = 0 if $input < 0; my $output = $output - \' + what.recur_included_output.value + \'; $output = 0 if $output < 0; my $totalcharge = sprintf(\"%.2f\", \' + what.recur_total_charge.value + \' * $total); my $hourscharge = sprintf(\"%.2f\", \' + what.recur_hourly_charge.value + \' * $hours); push @details, \"Last month\\\'s excess data \". sprintf(\"%.1f\", $total). \" megs: \\\$$totalcharge\", \"Last month\\\'s excess time \". sprintf(\"%.1f\", $hours). \" hours: \\\$$hourscharge\"; \' + what.recur_flat.value + \' + $hourscharge + \' + what.recur_input_charge.value + \' * $input + \' + what.recur_output_charge.value + \' * $output + $totalcharge ;\'',
},
+ 'sql_generic' => {
+ 'name' => 'Base charge plus a metered rate from a configurable SQL query',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_flat' => { 'name' => 'Base monthly charge for this package',
+ 'default' => 0,
+ },
+ 'recur_included' => { 'name' => 'Units included',
+ 'default' => 0,
+ },
+ 'recur_unit_charge' => { 'name' => 'Additional charge per unit',
+ 'default' => 0,
+ },
+ 'datasrc' => { 'name' => 'DBI data source',
+ 'default' => '',
+ },
+ 'db_username' => { 'name' => 'Database username',
+ 'default' => '',
+ },
+ 'db_password' => { 'name' => 'Database username',
+ 'default' => '',
+ },
+ 'query' => { 'name' => 'SQL query',
+ 'default' => '',
+ },
+ },
+ 'fieldorder' => [qw( setup_fee recur_flat recur_included recur_unit_charge datasrc db_username db_password query )],
+ 'setup' => 'what.setup_fee.value',
+ # 'recur' => '\'my $dbh = DBI->connect(\"\' + what.datasrc.value + \'\", \"\' + what.db_username.value + \'\") or die $DBI::errstr; \'',
+ 'recur' => '\'my $dbh = DBI->connect(\"\' + what.datasrc.value + \'\", \"\' + what.db_username.value + \'\", \"\' + what.db_password.value + \'\" ) or die $DBI::errstr; my $sth = $dbh->prepare(\"\' + what.query.value + \'\") or die $dbh->errstr; my $units = 0; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq \"svc_domain\" } $cust_pkg->cust_svc ) { my $domain = $cust_svc->svc_x->domain; $sth->execute($domain) or die $sth->errstr; $units += $sth->fetchrow_arrayref->[0]; } $units -= \' + what.recur_included.value + \'; $units = 0 if $units < 0; \' + what.recur_flat.value + \' + $units * \' + what.recur_unit_charge.value + \';\'',
+ #'recur' => '\'my $dbh = DBI->connect("\' + what.datasrc.value + \'", "\' + what.db_username.value + \'", "\' what.db_password.value + \'" ) or die $DBI::errstr; my $sth = $dbh->prepare("\' + what.query.value + \'") or die $dbh->errstr; my $units = 0; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq "svc_domain" } $cust_pkg->cust_svc ) { my $domain = $cust_svc->svc_x->domain; $sth->execute($domain) or die $sth->errstr; $units += $sth->fetchrow_arrayref->[0]; } $units -= \' + what.recur_included.value + \'; $units = 0 if $units < 0; \' + what.recur_flat.value + \' + $units * \' + what.recur_unit_charge + \';\'',
+ },
+
+
+
+ 'sql_external' => {
+ 'name' => 'Base charge plus additional fees for external services from a configurable SQL query',
+ 'fields' => {
+ 'setup_fee' => { 'name' => 'Setup fee for this package',
+ 'default' => 0,
+ },
+ 'recur_flat' => { 'name' => 'Base monthly charge for this package',
+ 'default' => 0,
+ },
+ 'datasrc' => { 'name' => 'DBI data source',
+ 'default' => '',
+ },
+ 'db_username' => { 'name' => 'Database username',
+ 'default' => '',
+ },
+ 'db_password' => { 'name' => 'Database password',
+ 'default' => '',
+ },
+ 'query' => { 'name' => 'SQL query',
+ 'default' => '',
+ },
+ },
+ 'fieldorder' => [qw( setup_fee recur_flat datasrc db_username db_password query )],
+ 'setup' => 'what.setup_fee.value',
+ 'recur' => q!'my $dbh = DBI->connect("' + what.datasrc.value + '", "' + what.db_username.value + '", "' + what.db_password.value + '" ) or die $DBI::errstr; my $sth = $dbh->prepare("' + what.query.value + '") or die $dbh->errstr; my $price = ' + what.recur_flat.value + '; foreach my $cust_svc ( grep { $_->part_svc->svcdb eq "svc_external" } $cust_pkg->cust_svc ){ my $id = $cust_svc->svc_x->id; $sth->execute($id) or die $sth->errstr; $price += $sth->fetchrow_arrayref->[0]; } $price;'!,
+
+ },
+
;
my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }
push @fixups, 'taxclass'; #hidden
}
+my @form_radio = ();
+if ( dbdef->table('pkg_svc')->column('primary_svc') ) {
+ push @form_radio, 'pkg_svc_primary';
+}
my $widget = new HTML::Widgets::SelectLayers(
'selected_layer' => $part_pkg->plan,
'form_action' => 'process/part_pkg.cgi',
'form_text' => [ qw(pkg comment freq clone pkgnum pkgpart), @fixups ],
'form_checkbox' => [ qw(setuptax recurtax disabled) ],
- 'form_select' => [ @form_select ],
+ 'form_radio' => \@form_radio,
+ 'form_select' => \@form_select,
'fixup_callback' => sub {
#my $ = @_;
my $html = '';
<LI>svc_forward - mail forwarding
<LI>svc_www - Virtual domain website
<LI>svc_broadband - Broadband/High-speed Internet service
+ <LI>svc_external - Externally-tracked service
<!-- <LI>svc_charge - One-time charges (Partially unimplemented)
<LI>svc_wo - Work orders (Partially unimplemented)
-->
'ip_addr' => 'IP address. Leave blank for automatic assignment.',
'blocknum' => 'Address block.',
},
+ 'svc_external' => {
+ #'id' => '',
+ #'title' => '',
+ },
);
- foreach $svcdb (keys(%defs)) {
+ foreach my $svcdb (grep dbdef->table($_), keys %defs ) {
my $self = "FS::$svcdb"->new;
$vfields{$svcdb} = {};
foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them
my @dbs = $hashref->{svcdb}
? ( $hashref->{svcdb} )
- : qw( svc_acct svc_domain svc_forward svc_www svc_broadband );
+ : qw( svc_acct svc_domain svc_forward svc_www svc_broadband svc_external );
- tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } @dbs;
+ tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs;
my $widget = new HTML::Widgets::SelectLayers(
#'selected_layer' => $p_svcdb,
'selected_layer' => $hashref->{svcdb} || 'svc_acct',
$html .= '<BR><BR>'. table().
table(). "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>";
foreach my $part_export ( @part_export ) {
- $html .= '<TD><INPUT TYPE="checbox"'.
+ $html .= '<TD><INPUT TYPE="checkbox"'.
' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" ';
$html .= 'CHECKED'
if ( $clone || $part_svc->svcpart ) #null svcpart search causing error
$cgi->param('paydate',
$cgi->param( $payby. '_month' ). '-'. $cgi->param( $payby. '_year' ) );
$cgi->param('payname', $cgi->param( $payby. '_payname' ) );
+ $cgi->param('paycvv', $cgi->param( $payby. '_paycvv' ) )
+ if defined $cgi->param( $payby. '_paycvv' );
}
$cgi->param('otaker', &getotaker );
push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
$cgi->param('invoicing_list', join(',', @invoicing_list) );
+
#create new record object
my $new = new FS::cust_main ( {
} else { #create old record object
my $old = qsearchs( 'cust_main', { 'custnum' => $new->custnum } );
$error ||= "Old record not found!" unless $old;
+ if ( defined dbdef->table('cust_main')->column('paycvv')
+ && length($old->paycvv)
+ && $new->paycvv =~ /^\s*\*+\s*$/ ) {
+ $new->paycvv($old->paycvv);
+ }
$error ||= $new->replace($old, \@invoicing_list);
}
my($query) = $cgi->keywords;
$query =~ /^(\d+)$/ or die "Illegal taxnum!";
my $taxnum = $1;
-my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum})
- or die ("Unknown taxnum!");
+my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } )
+ or die "Unknown taxnum $taxnum";
#really should do this in a .pm & start transaction
or die "Couldn't find taxnum $taxnum!";
next unless $old->tax != $cgi->param("tax$taxnum")
|| $old->exempt_amount != $cgi->param("exempt_amount$taxnum")
- || $old->taxname ne $cgi->param("taxname$taxnum");
+ || $old->taxname ne $cgi->param("taxname$taxnum")
+ || $old->setuptax ne $cgi->param("setuptax$taxnum")
+ || $old->recurtax ne $cgi->param("recurtax$taxnum");
my %hash = $old->hash;
$hash{tax} = $cgi->param("tax$taxnum");
$hash{exempt_amount} = $cgi->param("exempt_amount$taxnum");
$hash{taxname} = $cgi->param("taxname$taxnum");
+ $hash{setuptax} = $cgi->param("setuptax$taxnum");
+ $hash{recurtax} = $cgi->param("recurtax$taxnum");
my $new = new FS::cust_main_county \%hash;
my $error = $new->replace($old);
if ( $error ) {
foreach my $part_svc (qsearch('part_svc',{})) {
my $quantity = $cgi->param('pkg_svc'. $part_svc->svcpart) || 0;
+ my $primary_svc =
+ $cgi->param('pkg_svc_primary') == $part_svc->svcpart ? 'Y' : '';
my $old_pkg_svc = qsearchs('pkg_svc', {
'pkgpart' => $pkgpart,
'svcpart' => $part_svc->svcpart,
} );
my $old_quantity = $old_pkg_svc ? $old_pkg_svc->quantity : 0;
- next unless $old_quantity != $quantity; #!here
+ my $old_primary_svc =
+ ( $old_pkg_svc && $old_pkg_svc->dbdef_table->column('primary_svc') )
+ ? $old_pkg_svc->primary_svc
+ : '';
+ next unless $old_quantity != $quantity || $old_primary_svc ne $primary_svc;
+
my $new_pkg_svc = new FS::pkg_svc( {
- 'pkgpart' => $pkgpart,
- 'svcpart' => $part_svc->svcpart,
- 'quantity' => $quantity,
+ 'pkgpart' => $pkgpart,
+ 'svcpart' => $part_svc->svcpart,
+ 'quantity' => $quantity,
+ 'primary_svc' => $primary_svc,
} );
if ( $old_pkg_svc ) {
my $myerror = $new_pkg_svc->replace($old_pkg_svc);
<%
-use FS::UID qw(dbh);
-
-my $dbh = dbh;
local $FS::UID::AutoCommit=0;
sub check {
if($error) {
$cgi->param('error', $error);
print $cgi->redirect(popurl(3) . "edit/router.cgi?". $cgi->query_string);
- $dbh->rollback;
+ dbh->rollback;
exit;
}
}
if ($old) {
@old_psr = $old->part_svc_router;
- foreach $psr (@old_psr) {
+ foreach my $psr (@old_psr) {
if($cgi->param('svcpart_'.$psr->svcpart) eq 'ON') {
# do nothing
} else {
# Yay, everything worked!
-$dbh->commit or die $dbh->errstr;
+dbh->commit or die dbh->errstr;
print $cgi->redirect(popurl(3). "browse/router.cgi");
%>
--- /dev/null
+<%
+
+$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!";
+my $svcnum =$1;
+
+my $old = qsearchs('svc_external',{'svcnum'=>$svcnum}) if $svcnum;
+
+my $new = new FS::svc_external ( {
+ map {
+ ($_, scalar($cgi->param($_)));
+ } ( fields('svc_external'), qw( pkgnum svcpart ) )
+} );
+
+my $error = '';
+if ( $svcnum ) {
+ $error = $new->replace($old);
+} else {
+ $error = $new->insert;
+ $svcnum = $new->getfield('svcnum');
+}
+
+if ($error) {
+ $cgi->param('error', $error);
+ print $cgi->redirect(popurl(2). "svc_external.cgi?". $cgi->query_string );
+} else {
+ print $cgi->redirect(popurl(3). "view/svc_external.cgi?$svcnum");
+}
+
+%>
'View all routers' => "${p}browse/router.cgi",
));
+my $p3 = popurl(3);
+
if($cgi->param('error')) {
%> <FONT SIZE="+1" COLOR="#ff0000">Error: <%=$cgi->param('error')%></FONT>
<% } %>
<INPUT TYPE="hidden" NAME="redirect_ok" VALUE="<%=$p3%>/browse/router.cgi">
<INPUT TYPE="hidden" NAME="redirect_error" VALUE="<%=$p3%>/edit/router.cgi">
<INPUT TYPE="hidden" NAME="routernum" VALUE="<%=$routernum%>">
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%=$router->svcnum%>">
Router #<%=$routernum or "(NEW)"%>
<BR><BR>Name <INPUT TYPE="text" NAME="routername" SIZE=32 VALUE="<%=$router->routername%>">
</TABLE>
-
+<%
+unless ($router->svcnum) {
+%>
<BR><BR>Select the service types available on this router<BR>
<%
-foreach my $part_svc ( qsearch('part_svc', { svcdb => 'svc_broadband',
- disabled => '' }) ) {
+ foreach my $part_svc ( qsearch('part_svc', { svcdb => 'svc_broadband',
+ disabled => '' }) ) {
%>
<BR>
<INPUT TYPE="checkbox" NAME="svcpart_<%=$part_svc->svcpart%>"<%=
<%=$part_svc->svcpart%>: <%=$part_svc->svc%></A>
<% } %>
+<% } %>
+
<BR><BR><INPUT TYPE="submit" VALUE="Apply changes">
</FORM>
</BODY></HTML>
}
print '</TD></TR>';
-foreach $field ($svc_acct->virtual_fields) {
+foreach my $field ($svc_acct->virtual_fields) {
if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) {
# If the flag is X, it won't even show up in $svc_acct->virtual_fields.
print $svc_acct->pvf($field)->widget('HTML', 'edit',
<% } %>
<%
-foreach $field ($svc_broadband->virtual_fields) {
+foreach my $field ($svc_broadband->virtual_fields) {
if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) {
print $svc_broadband->pvf($field)->widget('HTML', 'edit',
$svc_broadband->getfield($field));
--- /dev/null
+<!-- mason kludge -->
+<%
+
+my( $svcnum, $pkgnum, $svcpart, $part_svc, $svc_external );
+if ( $cgi->param('error') ) {
+ $svc_external = new FS::svc_external ( {
+ map { $_, scalar($cgi->param($_)) } fields('svc_external')
+ } );
+ $svcnum = $svc_external->svcnum;
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+} else {
+ my($query) = $cgi->keywords;
+ if ( $query =~ /^(\d+)$/ ) { #editing
+ $svcnum=$1;
+ $svc_external=qsearchs('svc_external',{'svcnum'=>$svcnum})
+ or die "Unknown (svc_external) svcnum!";
+
+ my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum})
+ or die "Unknown (cust_svc) svcnum!";
+
+ $pkgnum=$cust_svc->pkgnum;
+ $svcpart=$cust_svc->svcpart;
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ } else { #adding
+
+ foreach $_ (split(/-/,$query)) { #get & untaint pkgnum & svcpart
+ $pkgnum=$1 if /^pkgnum(\d+)$/;
+ $svcpart=$1 if /^svcpart(\d+)$/;
+ }
+ $svc_external = new FS::svc_external { svcpart => $svcpart };
+
+ $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart});
+ die "No part_svc entry!" unless $part_svc;
+
+ $svcnum='';
+
+ #set fixed and default fields from part_svc
+ foreach my $part_svc_column (
+ grep { $_->columnflag } $part_svc->all_part_svc_column
+ ) {
+ $svc_external->setfield( $part_svc_column->columnname,
+ $part_svc_column->columnvalue,
+ );
+ }
+
+ }
+}
+my $action = $svc_external->svcnum ? 'Edit' : 'Add';
+
+my $p1 = popurl(1);
+print header("External service $action", '');
+
+print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
+ "</FONT>"
+ if $cgi->param('error');
+
+print qq!<FORM ACTION="${p1}process/svc_external.cgi" METHOD=POST>!;
+
+#display
+
+
+#svcnum
+print qq!<INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">!;
+print qq!Service #<B>!, $svcnum ? $svcnum : "(NEW)", "</B><BR><BR>";
+
+#pkgnum
+print qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!;
+
+#svcpart
+print qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!;
+
+my($id,$title)=(
+ $svc_external->id,
+ $svc_external->title,
+);
+
+print &ntable("#cccccc",2),
+ '<TR><TD ALIGN="right">External ID</TD><TD>'.
+ qq!<INPUT TYPE="text" NAME="id" VALUE="$id">!.
+ '</TD></TR>'.
+ '<TR><TD ALIGN="right">Title</TD><TD>'.
+ qq!<INPUT TYPE="text" NAME="title" VALUE="$title">!.
+ '</TD></TR>';
+
+foreach my $field ($svc_external->virtual_fields) {
+ if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) {
+ # If the flag is X, it won't even show up in $svc_acct->virtual_fields.
+ print $svc_external->pvf($field)->widget('HTML', 'edit',
+ $svc_external->getfield($field));
+ }
+}
+
+%>
+
+</TABLE><BR><INPUT TYPE="submit" VALUE="Submit">
+ </FORM>
+ </BODY>
+</HTML>
+
my $action = $svc_forward->svcnum ? 'Edit' : 'Add';
my %email;
+
+#starting with those currently attached
+foreach my $method (qw( srcsvc_acct dstsvc_acct )) {
+ my $svc_acct = $svc_forward->$method();
+ $email{$svc_acct->svcnum} = $svc_acct->email if $svc_acct;
+}
+
if ($pkgnum) {
#find all possible user svcnums (and emails)
- #starting with those currently attached
- if ( $svc_forward->srcsvc ) {
- my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_forward->srcsvc } );
- $email{$svc_forward->srcsvc} = $svc_acct->email;
- }
- if ( $svc_forward->dstsvc ) {
- my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_forward->dstsvc } );
- $email{$svc_forward->dstsvc} = $svc_acct->email;
- }
-
#and including the rest for this customer
my($u_part_svc,@u_acct_svcparts);
foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) {
}
}
-} elsif ( $action eq 'Edit' ) {
-
- my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$svc_forward->srcsvc});
- $email{$svc_forward->srcsvc} = $svc_acct->email;
-
- $svc_acct=qsearchs('svc_acct',{'svcnum'=>$svc_forward->dstsvc});
- $email{$svc_forward->dstsvc} = $svc_acct->email;
-
-} else {
+} elsif ( $action eq 'Add' ) {
die "\$action eq Add, but \$pkgnum is null!\n";
}
$svc_forward->dstsvc,
$svc_forward->dst,
);
+my $src = $svc_forward->dbdef_table->column('src') ? $svc_forward->src : '';
#display
Service #<%= $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR>
Service: <B><%= $part_svc->svc %></B><BR><BR>
-<FORM NAME="dummy">
+<FORM ACTION="process/svc_forward.cgi" METHOD="POST">
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<%= $svcnum %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%= $pkgnum %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<%= $svcpart %>">
+
+<SCRIPT TYPE="text/javascript">
+function srcchanged(what) {
+ if ( what.options[what.selectedIndex].value == 0 ) {
+ what.form.src.disabled = false;
+ } else {
+ what.form.src.disabled = true;
+ }
+}
+function dstchanged(what) {
+ if ( what.options[what.selectedIndex].value == 0 ) {
+ what.form.dst.disabled = false;
+ } else {
+ what.form.dst.disabled = true;
+ }
+}
+</SCRIPT>
<%= ntable("#cccccc",2) %>
-<TR><TD ALIGN="right">Email to</TD><TD><SELECT NAME="srcsvc" SIZE=1>
+<TR><TD ALIGN="right">Email to</TD>
+<TD><SELECT NAME="srcsvc" SIZE=1 onChange="srcchanged(this)">
<% foreach $_ (keys %email) { %>
<OPTION<%= $_ eq $srcsvc ? " SELECTED" : "" %> VALUE="<%= $_ %>"><%= $email{$_} %></OPTION>
<% } %>
-</SELECT></TD></TR>
-
-<%
- tie my %tied_email, 'Tie::IxHash',
- '' => 'SELECT DESTINATION',
- %email,
- '0' => '(other email address)';
- my $widget = new HTML::Widgets::SelectLayers(
- 'selected_layer' => $dstsvc,
- 'options' => \%tied_email,
- 'form_name' => 'dummy',
- 'form_action' => 'process/svc_forward.cgi',
- 'form_select' => ['srcsvc'],
- 'html_between' => '</TD></TR></TABLE>',
- 'layer_callback' => sub {
- my $layer = shift;
- my $html = qq!<INPUT TYPE="hidden" NAME="svcnum" VALUE="$svcnum">!.
- qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!.
- qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!.
- qq!<INPUT TYPE="hidden" NAME="dstsvc" VALUE="$layer">!;
- if ( $layer eq '0' ) {
- $html .= ntable("#cccccc",2).
- '<TR><TD ALIGN="right">Destination email</TD>'.
- qq!<TD><INPUT TYPE="text" NAME="dst" VALUE="$dst"></TD>!.
- '</TR></TABLE>';
- }
- $html .= '<BR><INPUT TYPE="submit" VALUE="Submit">';
- $html;
- },
- );
-%>
+<% if ( $svc_forward->dbdef_table->column('src') ) { %>
+ <OPTION <%= $src ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION>
+<% } %>
+</SELECT>
+<% if ( $svc_forward->dbdef_table->column('src') ) { %>
+<INPUT TYPE="text" NAME="src" VALUE="<%= $src %>" <%= ( $src || !scalar(%email) ) ? '' : 'DISABLED' %>>
+<% } %>
+</TD></TR>
<TR><TD ALIGN="right">Forwards to</TD>
-<TD><%= $widget->html %>
+<TD><SELECT NAME="dstsvc" SIZE=1 onChange="dstchanged(this)">
+<% foreach $_ (keys %email) { %>
+ <OPTION<%= $_ eq $dstsvc ? " SELECTED" : "" %> VALUE="<%= $_ %>"><%= $email{$_} %></OPTION>
+<% } %>
+<OPTION <%= $dst ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION>
+</SELECT>
+<INPUT TYPE="text" NAME="dst" VALUE="<%= $dst %>" <%= ( $dst || !scalar(%email) ) ? '' : 'DISABLED' %>>
+</TD></TR>
+ </TABLE>
+<BR><INPUT TYPE="submit" VALUE="Submit">
+</FORM>
</BODY>
</HTML>
}
print "</SELECT></TD></TR>";
-foreach $field ($svc_www->virtual_fields) {
+foreach my $field ($svc_www->virtual_fields) {
if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) {
# If the flag is X, it won't even show up in $svc_acct->virtual_fields.
print $svc_www->pvf($field)->widget('HTML', 'edit',
--- /dev/null
+// ** I18N
+
+// Calendar EN language
+// Author: Mihai Bazon, <mishoo@infoiasi.ro>
+// Encoding: any
+// Distributed under the same terms as the calendar itself.
+
+// For translators: please use UTF-8 if possible. We strongly believe that
+// Unicode is the answer to a real internationalized world. Also please
+// include your contact information in the header, as can be seen above.
+
+// full day names
+Calendar._DN = new Array
+("Sunday",
+ "Monday",
+ "Tuesday",
+ "Wednesday",
+ "Thursday",
+ "Friday",
+ "Saturday",
+ "Sunday");
+
+// Please note that the following array of short day names (and the same goes
+// for short month names, _SMN) isn't absolutely necessary. We give it here
+// for exemplification on how one can customize the short day names, but if
+// they are simply the first N letters of the full name you can simply say:
+//
+// Calendar._SDN_len = N; // short day name length
+// Calendar._SMN_len = N; // short month name length
+//
+// If N = 3 then this is not needed either since we assume a value of 3 if not
+// present, to be compatible with translation files that were written before
+// this feature.
+
+// short day names
+Calendar._SDN = new Array
+("Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat",
+ "Sun");
+
+// full month names
+Calendar._MN = new Array
+("January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December");
+
+// short month names
+Calendar._SMN = new Array
+("Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec");
+
+// tooltips
+Calendar._TT = {};
+Calendar._TT["INFO"] = "About the calendar";
+
+Calendar._TT["ABOUT"] =
+"DHTML Date/Time Selector\n" +
+"(c) dynarch.com 2002-2003\n" + // don't translate this this ;-)
+"For latest version visit: http://dynarch.com/mishoo/calendar.epl\n" +
+"Distributed under GNU LGPL. See http://gnu.org/licenses/lgpl.html for details." +
+"\n\n" +
+"Date selection:\n" +
+"- Use the \xab, \xbb buttons to select year\n" +
+"- Use the " + String.fromCharCode(0x2039) + ", " + String.fromCharCode(0x203a) + " buttons to select month\n" +
+"- Hold mouse button on any of the above buttons for faster selection.";
+Calendar._TT["ABOUT_TIME"] = "\n\n" +
+"Time selection:\n" +
+"- Click on any of the time parts to increase it\n" +
+"- or Shift-click to decrease it\n" +
+"- or click and drag for faster selection.";
+
+Calendar._TT["PREV_YEAR"] = "Prev. year (hold for menu)";
+Calendar._TT["PREV_MONTH"] = "Prev. month (hold for menu)";
+Calendar._TT["GO_TODAY"] = "Go Today";
+Calendar._TT["NEXT_MONTH"] = "Next month (hold for menu)";
+Calendar._TT["NEXT_YEAR"] = "Next year (hold for menu)";
+Calendar._TT["SEL_DATE"] = "Select date";
+Calendar._TT["DRAG_TO_MOVE"] = "Drag to move";
+Calendar._TT["PART_TODAY"] = " (today)";
+Calendar._TT["MON_FIRST"] = "Display Monday first";
+Calendar._TT["SUN_FIRST"] = "Display Sunday first";
+Calendar._TT["CLOSE"] = "Close";
+Calendar._TT["TODAY"] = "Today";
+Calendar._TT["TIME_PART"] = "(Shift-)Click or drag to change value";
+
+// date formats
+Calendar._TT["DEF_DATE_FORMAT"] = "%Y-%m-%d";
+Calendar._TT["TT_DATE_FORMAT"] = "%a, %b %e";
+
+Calendar._TT["WK"] = "wk";
--- /dev/null
+/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
+ * ---------------------------------------------------------------------------
+ *
+ * The DHTML Calendar
+ *
+ * Details and latest version at:
+ * http://dynarch.com/mishoo/calendar.epl
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ *
+ * This file defines helper functions for setting up the calendar. They are
+ * intended to help non-programmers get a working calendar on their site
+ * quickly. This script should not be seen as part of the calendar. It just
+ * shows you what one can do with the calendar, while in the same time
+ * providing a quick and simple method for setting it up. If you need
+ * exhaustive customization of the calendar creation process feel free to
+ * modify this code to suit your needs (this is recommended and much better
+ * than modifying calendar.js itself).
+ */
+
+// $Id: calendar-setup.js,v 1.3 2003-11-07 10:53:35 ivan Exp $
+
+/**
+ * This function "patches" an input field (or other element) to use a calendar
+ * widget for date selection.
+ *
+ * The "params" is a single object that can have the following properties:
+ *
+ * prop. name | description
+ * -------------------------------------------------------------------------------------------------
+ * inputField | the ID of an input field to store the date
+ * displayArea | the ID of a DIV or other element to show the date
+ * button | ID of a button or other element that will trigger the calendar
+ * eventName | event that will trigger the calendar, without the "on" prefix (default: "click")
+ * ifFormat | date format that will be stored in the input field
+ * daFormat | the date format that will be used to display the date in displayArea
+ * singleClick | (true/false) wether the calendar is in single click mode or not (default: true)
+ * mondayFirst | (true/false) if true Monday is the first day of week, Sunday otherwise (default: true)
+ * align | alignment (default: "Bl"); if you don't know what's this see the calendar documentation
+ * range | array with 2 elements. Default: [1900, 2999] -- the range of years available
+ * weekNumbers | (true/false) if it's true (default) the calendar will display week numbers
+ * flat | null or element ID; if not null the calendar will be a flat calendar having the parent with the given ID
+ * flatCallback | function that receives a JS Date object and returns an URL to point the browser to (for flat calendar)
+ * disableFunc | function that receives a JS Date object and should return true if that date has to be disabled in the calendar
+ * onSelect | function that gets called when a date is selected. You don't _have_ to supply this (the default is generally okay)
+ * onClose | function that gets called when the calendar is closed. [default]
+ * onUpdate | function that gets called after the date is updated in the input field. Receives a reference to the calendar.
+ * date | the date that the calendar will be initially displayed to
+ * showsTime | default: false; if true the calendar will include a time selector
+ * timeFormat | the time format; can be "12" or "24", default is "12"
+ *
+ * None of them is required, they all have default values. However, if you
+ * pass none of "inputField", "displayArea" or "button" you'll get a warning
+ * saying "nothing to setup".
+ */
+Calendar.setup = function (params) {
+ function param_default(pname, def) { if (typeof params[pname] == "undefined") { params[pname] = def; } };
+
+ param_default("inputField", null);
+ param_default("displayArea", null);
+ param_default("button", null);
+ param_default("eventName", "click");
+ param_default("ifFormat", "%Y/%m/%d");
+ param_default("daFormat", "%Y/%m/%d");
+ param_default("singleClick", true);
+ param_default("disableFunc", null);
+ param_default("dateStatusFunc", params["disableFunc"]); // takes precedence if both are defined
+ param_default("mondayFirst", true);
+ param_default("align", "Bl");
+ param_default("range", [1900, 2999]);
+ param_default("weekNumbers", true);
+ param_default("flat", null);
+ param_default("flatCallback", null);
+ param_default("onSelect", null);
+ param_default("onClose", null);
+ param_default("onUpdate", null);
+ param_default("date", null);
+ param_default("showsTime", false);
+ param_default("timeFormat", "24");
+
+ var tmp = ["inputField", "displayArea", "button"];
+ for (var i in tmp) {
+ if (typeof params[tmp[i]] == "string") {
+ params[tmp[i]] = document.getElementById(params[tmp[i]]);
+ }
+ }
+ if (!(params.flat || params.inputField || params.displayArea || params.button)) {
+ alert("Calendar.setup:\n Nothing to setup (no fields found). Please check your code");
+ return false;
+ }
+
+ function onSelect(cal) {
+ if (cal.params.flat) {
+ if (typeof cal.params.flatCallback == "function") {
+ cal.params.flatCallback(cal);
+ } else {
+ alert("No flatCallback given -- doing nothing.");
+ }
+ return false;
+ }
+ if (cal.params.inputField) {
+ cal.params.inputField.value = cal.date.print(cal.params.ifFormat);
+ }
+ if (cal.params.displayArea) {
+ cal.params.displayArea.innerHTML = cal.date.print(cal.params.daFormat);
+ }
+ if (cal.params.singleClick && cal.dateClicked) {
+ cal.callCloseHandler();
+ }
+ if (typeof cal.params.onUpdate == "function") {
+ cal.params.onUpdate(cal);
+ }
+ };
+
+ if (params.flat != null) {
+ params.flat = document.getElementById(params.flat);
+ if (!params.flat) {
+ alert("Calendar.setup:\n Flat specified but can't find parent.");
+ return false;
+ }
+ var cal = new Calendar(params.mondayFirst, params.date, params.onSelect || onSelect);
+ cal.showsTime = params.showsTime;
+ cal.time24 = (params.timeFormat == "24");
+ cal.params = params;
+ cal.weekNumbers = params.weekNumbers;
+ cal.setRange(params.range[0], params.range[1]);
+ cal.setDateStatusHandler(params.dateStatusFunc);
+ cal.create(params.flat);
+ cal.show();
+ return false;
+ }
+
+ var triggerEl = params.button || params.displayArea || params.inputField;
+ triggerEl["on" + params.eventName] = function() {
+ var dateEl = params.inputField || params.displayArea;
+ var dateFmt = params.inputField ? params.ifFormat : params.daFormat;
+ var mustCreate = false;
+ var cal = window.calendar;
+ if (!window.calendar) {
+ window.calendar = cal = new Calendar(params.mondayFirst,
+ params.date,
+ params.onSelect || onSelect,
+ params.onClose || function(cal) { cal.hide(); });
+ cal.showsTime = params.showsTime;
+ cal.time24 = (params.timeFormat == "24");
+ cal.weekNumbers = params.weekNumbers;
+ mustCreate = true;
+ } else {
+ cal.hide();
+ }
+ cal.setRange(params.range[0], params.range[1]);
+ cal.params = params;
+ cal.setDateStatusHandler(params.dateStatusFunc);
+ cal.setDateFormat(dateFmt);
+ if (mustCreate)
+ cal.create();
+ cal.parseDate(dateEl.value || dateEl.innerHTML);
+ cal.refresh();
+ cal.showAtElement(params.displayArea || params.inputField, params.align);
+ return false;
+ };
+};
--- /dev/null
+/* The main calendar widget. DIV containing a table. */
+
+.calendar {
+ position: relative;
+ display: none;
+ border-top: 2px solid #fff;
+ border-right: 2px solid #000;
+ border-bottom: 2px solid #000;
+ border-left: 2px solid #fff;
+ font-size: 11px;
+ color: #000;
+ cursor: default;
+ background: #d4c8d0;
+ font-family: tahoma,verdana,sans-serif;
+}
+
+.calendar table {
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+ font-size: 11px;
+ color: #000;
+ cursor: default;
+ background: #d4c8d0;
+ font-family: tahoma,verdana,sans-serif;
+}
+
+/* Header part -- contains navigation buttons and day names. */
+
+.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */
+ text-align: center;
+ padding: 1px;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+}
+
+.calendar .nav {
+ background: transparent url(menuarrow.gif) no-repeat 100% 100%;
+}
+
+.calendar thead .title { /* This holds the current "month, year" */
+ font-weight: bold;
+ padding: 1px;
+ border: 1px solid #000;
+ background: #847880;
+ color: #fff;
+ text-align: center;
+}
+
+.calendar thead .headrow { /* Row <TR> containing navigation buttons */
+}
+
+.calendar thead .daynames { /* Row <TR> containing the day names */
+}
+
+.calendar thead .name { /* Cells <TD> containing the day names */
+ border-bottom: 1px solid #000;
+ padding: 2px;
+ text-align: center;
+ background: #f4e8f0;
+}
+
+.calendar thead .weekend { /* How a weekend day name shows in header */
+ color: #f00;
+}
+
+.calendar thead .hilite { /* How do the buttons in header appear when hover */
+ border-top: 2px solid #fff;
+ border-right: 2px solid #000;
+ border-bottom: 2px solid #000;
+ border-left: 2px solid #fff;
+ padding: 0px;
+ background-color: #e4d8e0;
+}
+
+.calendar thead .active { /* Active (pressed) buttons in header */
+ padding: 2px 0px 0px 2px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+ background-color: #c4b8c0;
+}
+
+/* The body part -- contains all the days in month. */
+
+.calendar tbody .day { /* Cells <TD> containing month days dates */
+ width: 2em;
+ text-align: right;
+ padding: 2px 4px 2px 2px;
+}
+
+.calendar table .wn {
+ padding: 2px 3px 2px 2px;
+ border-right: 1px solid #000;
+ background: #f4e8f0;
+}
+
+.calendar tbody .rowhilite td {
+ background: #e4d8e0;
+}
+
+.calendar tbody .rowhilite td.wn {
+ background: #d4c8d0;
+}
+
+.calendar tbody td.hilite { /* Hovered cells <TD> */
+ padding: 1px 3px 1px 1px;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+}
+
+.calendar tbody td.active { /* Active (pressed) cells <TD> */
+ padding: 2px 2px 0px 2px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+.calendar tbody td.selected { /* Cell showing selected date */
+ font-weight: bold;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+ padding: 2px 2px 0px 2px;
+ background: #e4d8e0;
+}
+
+.calendar tbody td.weekend { /* Cells showing weekend days */
+ color: #f00;
+}
+
+.calendar tbody td.today { /* Cell showing today date */
+ font-weight: bold;
+ color: #00f;
+}
+
+.calendar tbody .disabled { color: #999; }
+
+.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */
+ visibility: hidden;
+}
+
+.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */
+ display: none;
+}
+
+/* The footer part -- status bar and "Close" button */
+
+.calendar tfoot .footrow { /* The <TR> in footer (only one right now) */
+}
+
+.calendar tfoot .ttip { /* Tooltip (status bar) cell <TD> */
+ background: #f4e8f0;
+ padding: 1px;
+ border: 1px solid #000;
+ background: #847880;
+ color: #fff;
+ text-align: center;
+}
+
+.calendar tfoot .hilite { /* Hover style for buttons in footer */
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+ padding: 1px;
+ background: #e4d8e0;
+}
+
+.calendar tfoot .active { /* Active (pressed) style for buttons in footer */
+ padding: 2px 0px 0px 2px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+/* Combo boxes (menus that display months/years for direct selection) */
+
+.combo {
+ position: absolute;
+ display: none;
+ width: 4em;
+ top: 0px;
+ left: 0px;
+ cursor: default;
+ border-top: 1px solid #fff;
+ border-right: 1px solid #000;
+ border-bottom: 1px solid #000;
+ border-left: 1px solid #fff;
+ background: #e4d8e0;
+ font-size: smaller;
+ padding: 1px;
+}
+
+.combo .label,
+.combo .label-IEfix {
+ text-align: center;
+ padding: 1px;
+}
+
+.combo .label-IEfix {
+ width: 4em;
+}
+
+.combo .active {
+ background: #d4c8d0;
+ padding: 0px;
+ border-top: 1px solid #000;
+ border-right: 1px solid #fff;
+ border-bottom: 1px solid #fff;
+ border-left: 1px solid #000;
+}
+
+.combo .hilite {
+ background: #408;
+ color: #fea;
+}
+
+.calendar td.time {
+ border-top: 1px solid #000;
+ padding: 1px 0px;
+ text-align: center;
+ background-color: #f4f0e8;
+}
+
+.calendar td.time .hour,
+.calendar td.time .minute,
+.calendar td.time .ampm {
+ padding: 0px 3px 0px 4px;
+ border: 1px solid #889;
+ font-weight: bold;
+ background-color: #fff;
+}
+
+.calendar td.time .ampm {
+ text-align: center;
+}
+
+.calendar td.time .colon {
+ padding: 0px 2px 0px 3px;
+ font-weight: bold;
+}
+
+.calendar td.time span.hilite {
+ border-color: #000;
+ background-color: #766;
+ color: #fff;
+}
+
+.calendar td.time span.active {
+ border-color: #f00;
+ background-color: #000;
+ color: #0f0;
+}
--- /dev/null
+/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
+ * ------------------------------------------------------------------
+ *
+ * The DHTML Calendar, version 0.9.5 "Your favorite time, bis"
+ *
+ * Details and latest version at:
+ * http://dynarch.com/mishoo/calendar.epl
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ */
+
+// $Id: calendar.js,v 1.3 2003-11-07 10:53:35 ivan Exp $
+
+/** The Calendar object constructor. */
+Calendar = function (mondayFirst, dateStr, onSelected, onClose) {
+ // member variables
+ this.activeDiv = null;
+ this.currentDateEl = null;
+ this.getDateStatus = null;
+ this.timeout = null;
+ this.onSelected = onSelected || null;
+ this.onClose = onClose || null;
+ this.dragging = false;
+ this.hidden = false;
+ this.minYear = 1970;
+ this.maxYear = 2050;
+ this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
+ this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
+ this.isPopup = true;
+ this.weekNumbers = true;
+ this.mondayFirst = mondayFirst;
+ this.dateStr = dateStr;
+ this.ar_days = null;
+ this.showsTime = false;
+ this.time24 = true;
+ // HTML elements
+ this.table = null;
+ this.element = null;
+ this.tbody = null;
+ this.firstdayname = null;
+ // Combo boxes
+ this.monthsCombo = null;
+ this.yearsCombo = null;
+ this.hilitedMonth = null;
+ this.activeMonth = null;
+ this.hilitedYear = null;
+ this.activeYear = null;
+ // Information
+ this.dateClicked = false;
+
+ // one-time initializations
+ if (typeof Calendar._SDN == "undefined") {
+ // table of short day names
+ if (typeof Calendar._SDN_len == "undefined")
+ Calendar._SDN_len = 3;
+ var ar = new Array();
+ for (var i = 8; i > 0;) {
+ ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
+ }
+ Calendar._SDN = ar;
+ // table of short month names
+ if (typeof Calendar._SMN_len == "undefined")
+ Calendar._SMN_len = 3;
+ ar = new Array();
+ for (var i = 12; i > 0;) {
+ ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
+ }
+ Calendar._SMN = ar;
+ }
+};
+
+// ** constants
+
+/// "static", needed for event handlers.
+Calendar._C = null;
+
+/// detect a special case of "web browser"
+Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
+ !/opera/i.test(navigator.userAgent) );
+
+/// detect Opera browser
+Calendar.is_opera = /opera/i.test(navigator.userAgent);
+
+/// detect KHTML-based browsers
+Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
+
+// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
+// library, at some point.
+
+Calendar.getAbsolutePos = function(el) {
+ var SL = 0, ST = 0;
+ var is_div = /^div$/i.test(el.tagName);
+ if (is_div && el.scrollLeft)
+ SL = el.scrollLeft;
+ if (is_div && el.scrollTop)
+ ST = el.scrollTop;
+ var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
+ if (el.offsetParent) {
+ var tmp = Calendar.getAbsolutePos(el.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+};
+
+Calendar.isRelated = function (el, evt) {
+ var related = evt.relatedTarget;
+ if (!related) {
+ var type = evt.type;
+ if (type == "mouseover") {
+ related = evt.fromElement;
+ } else if (type == "mouseout") {
+ related = evt.toElement;
+ }
+ }
+ while (related) {
+ if (related == el) {
+ return true;
+ }
+ related = related.parentNode;
+ }
+ return false;
+};
+
+Calendar.removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+Calendar.addClass = function(el, className) {
+ Calendar.removeClass(el, className);
+ el.className += " " + className;
+};
+
+Calendar.getElement = function(ev) {
+ if (Calendar.is_ie) {
+ return window.event.srcElement;
+ } else {
+ return ev.currentTarget;
+ }
+};
+
+Calendar.getTargetElement = function(ev) {
+ if (Calendar.is_ie) {
+ return window.event.srcElement;
+ } else {
+ return ev.target;
+ }
+};
+
+Calendar.stopEvent = function(ev) {
+ ev || (ev = window.event);
+ if (Calendar.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ return false;
+};
+
+Calendar.addEvent = function(el, evname, func) {
+ if (el.attachEvent) { // IE
+ el.attachEvent("on" + evname, func);
+ } else if (el.addEventListener) { // Gecko / W3C
+ el.addEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = func;
+ }
+};
+
+Calendar.removeEvent = function(el, evname, func) {
+ if (el.detachEvent) { // IE
+ el.detachEvent("on" + evname, func);
+ } else if (el.removeEventListener) { // Gecko / W3C
+ el.removeEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = null;
+ }
+};
+
+Calendar.createElement = function(type, parent) {
+ var el = null;
+ if (document.createElementNS) {
+ // use the XHTML namespace; IE won't normally get here unless
+ // _they_ "fix" the DOM2 implementation.
+ el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
+ } else {
+ el = document.createElement(type);
+ }
+ if (typeof parent != "undefined") {
+ parent.appendChild(el);
+ }
+ return el;
+};
+
+// END: UTILITY FUNCTIONS
+
+// BEGIN: CALENDAR STATIC FUNCTIONS
+
+/** Internal -- adds a set of events to make some element behave like a button. */
+Calendar._add_evs = function(el) {
+ with (Calendar) {
+ addEvent(el, "mouseover", dayMouseOver);
+ addEvent(el, "mousedown", dayMouseDown);
+ addEvent(el, "mouseout", dayMouseOut);
+ if (is_ie) {
+ addEvent(el, "dblclick", dayMouseDblClick);
+ el.setAttribute("unselectable", true);
+ }
+ }
+};
+
+Calendar.findMonth = function(el) {
+ if (typeof el.month != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.month != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.findYear = function(el) {
+ if (typeof el.year != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.year != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.showMonthsCombo = function () {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var mc = cal.monthsCombo;
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ if (cal.activeMonth) {
+ Calendar.removeClass(cal.activeMonth, "active");
+ }
+ var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
+ Calendar.addClass(mon, "active");
+ cal.activeMonth = mon;
+ var s = mc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else
+ s.left = (cd.offsetLeft + cd.offsetWidth - mc.offsetWidth) + "px";
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+};
+
+Calendar.showYearsCombo = function (fwd) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var yc = cal.yearsCombo;
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ if (cal.activeYear) {
+ Calendar.removeClass(cal.activeYear, "active");
+ }
+ cal.activeYear = null;
+ var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
+ var yr = yc.firstChild;
+ var show = false;
+ for (var i = 12; i > 0; --i) {
+ if (Y >= cal.minYear && Y <= cal.maxYear) {
+ yr.firstChild.data = Y;
+ yr.year = Y;
+ yr.style.display = "block";
+ show = true;
+ } else {
+ yr.style.display = "none";
+ }
+ yr = yr.nextSibling;
+ Y += fwd ? 2 : -2;
+ }
+ if (show) {
+ var s = yc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else
+ s.left = (cd.offsetLeft + cd.offsetWidth - yc.offsetWidth) + "px";
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+ }
+};
+
+// event handlers
+
+Calendar.tableMouseUp = function(ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ if (cal.timeout) {
+ clearTimeout(cal.timeout);
+ }
+ var el = cal.activeDiv;
+ if (!el) {
+ return false;
+ }
+ var target = Calendar.getTargetElement(ev);
+ ev || (ev = window.event);
+ Calendar.removeClass(el, "active");
+ if (target == el || target.parentNode == el) {
+ Calendar.cellClick(el, ev);
+ }
+ var mon = Calendar.findMonth(target);
+ var date = null;
+ if (mon) {
+ date = new Date(cal.date);
+ if (mon.month != date.getMonth()) {
+ date.setMonth(mon.month);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ } else {
+ var year = Calendar.findYear(target);
+ if (year) {
+ date = new Date(cal.date);
+ if (year.year != date.getFullYear()) {
+ date.setFullYear(year.year);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ }
+ }
+ with (Calendar) {
+ removeEvent(document, "mouseup", tableMouseUp);
+ removeEvent(document, "mouseover", tableMouseOver);
+ removeEvent(document, "mousemove", tableMouseOver);
+ cal._hideCombos();
+ _C = null;
+ return stopEvent(ev);
+ }
+};
+
+Calendar.tableMouseOver = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return;
+ }
+ var el = cal.activeDiv;
+ var target = Calendar.getTargetElement(ev);
+ if (target == el || target.parentNode == el) {
+ Calendar.addClass(el, "hilite active");
+ Calendar.addClass(el.parentNode, "rowhilite");
+ } else {
+ if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
+ Calendar.removeClass(el, "active");
+ Calendar.removeClass(el, "hilite");
+ Calendar.removeClass(el.parentNode, "rowhilite");
+ }
+ ev || (ev = window.event);
+ if (el.navtype == 50 && target != el) {
+ var pos = Calendar.getAbsolutePos(el);
+ var w = el.offsetWidth;
+ var x = ev.clientX;
+ var dx;
+ var decrease = true;
+ if (x > pos.x + w) {
+ dx = x - pos.x - w;
+ decrease = false;
+ } else
+ dx = pos.x - x;
+
+ if (dx < 0) dx = 0;
+ var range = el._range;
+ var current = el._current;
+ var count = Math.floor(dx / 10) % range.length;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ while (count-- > 0)
+ if (decrease) {
+ if (!(--i in range))
+ i = range.length - 1;
+ } else if (!(++i in range))
+ i = 0;
+ var newval = range[i];
+ el.firstChild.data = newval;
+
+ cal.onUpdateTime();
+ }
+ var mon = Calendar.findMonth(target);
+ if (mon) {
+ if (mon.month != cal.date.getMonth()) {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ Calendar.addClass(mon, "hilite");
+ cal.hilitedMonth = mon;
+ } else if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ } else {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ var year = Calendar.findYear(target);
+ if (year) {
+ if (year.year != cal.date.getFullYear()) {
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ Calendar.addClass(year, "hilite");
+ cal.hilitedYear = year;
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.tableMouseDown = function (ev) {
+ if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
+ return Calendar.stopEvent(ev);
+ }
+};
+
+Calendar.calDragIt = function (ev) {
+ var cal = Calendar._C;
+ if (!(cal && cal.dragging)) {
+ return false;
+ }
+ var posX;
+ var posY;
+ if (Calendar.is_ie) {
+ posY = window.event.clientY + document.body.scrollTop;
+ posX = window.event.clientX + document.body.scrollLeft;
+ } else {
+ posX = ev.pageX;
+ posY = ev.pageY;
+ }
+ cal.hideShowCovered();
+ var st = cal.element.style;
+ st.left = (posX - cal.xOffs) + "px";
+ st.top = (posY - cal.yOffs) + "px";
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.calDragEnd = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ cal.dragging = false;
+ with (Calendar) {
+ removeEvent(document, "mousemove", calDragIt);
+ removeEvent(document, "mouseover", stopEvent);
+ removeEvent(document, "mouseup", calDragEnd);
+ tableMouseUp(ev);
+ }
+ cal.hideShowCovered();
+};
+
+Calendar.dayMouseDown = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (el.disabled) {
+ return false;
+ }
+ var cal = el.calendar;
+ cal.activeDiv = el;
+ Calendar._C = cal;
+ if (el.navtype != 300) with (Calendar) {
+ if (el.navtype == 50)
+ el._current = el.firstChild.data;
+ addClass(el, "hilite active");
+ addEvent(document, "mouseover", tableMouseOver);
+ addEvent(document, "mousemove", tableMouseOver);
+ addEvent(document, "mouseup", tableMouseUp);
+ } else if (cal.isPopup) {
+ cal._dragStart(ev);
+ }
+ if (el.navtype == -1 || el.navtype == 1) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
+ } else if (el.navtype == -2 || el.navtype == 2) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
+ } else {
+ cal.timeout = null;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseDblClick = function(ev) {
+ Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
+ if (Calendar.is_ie) {
+ document.selection.empty();
+ }
+};
+
+Calendar.dayMouseOver = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
+ return false;
+ }
+ if (el.ttip) {
+ if (el.ttip.substr(0, 1) == "_") {
+ var date = null;
+ with (el.calendar.date) {
+ date = new Date(getFullYear(), getMonth(), el.caldate);
+ }
+ el.ttip = date.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
+ }
+ el.calendar.tooltips.firstChild.data = el.ttip;
+ }
+ if (el.navtype != 300) {
+ Calendar.addClass(el, "hilite");
+ if (el.caldate) {
+ Calendar.addClass(el.parentNode, "rowhilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseOut = function(ev) {
+ with (Calendar) {
+ var el = getElement(ev);
+ if (isRelated(el, ev) || _C || el.disabled) {
+ return false;
+ }
+ removeClass(el, "hilite");
+ if (el.caldate) {
+ removeClass(el.parentNode, "rowhilite");
+ }
+ el.calendar.tooltips.firstChild.data = _TT["SEL_DATE"];
+ return stopEvent(ev);
+ }
+};
+
+/**
+ * A generic "click" handler :) handles all types of buttons defined in this
+ * calendar.
+ */
+Calendar.cellClick = function(el, ev) {
+ var cal = el.calendar;
+ var closing = false;
+ var newdate = false;
+ var date = null;
+ if (typeof el.navtype == "undefined") {
+ Calendar.removeClass(cal.currentDateEl, "selected");
+ Calendar.addClass(el, "selected");
+ closing = (cal.currentDateEl == el);
+ if (!closing) {
+ cal.currentDateEl = el;
+ }
+ cal.date.setDate(el.caldate);
+ date = cal.date;
+ newdate = true;
+ // a date was clicked
+ cal.dateClicked = true;
+ } else {
+ if (el.navtype == 200) {
+ Calendar.removeClass(el, "hilite");
+ cal.callCloseHandler();
+ return;
+ }
+ date = (el.navtype == 0) ? new Date() : new Date(cal.date);
+ // unless "today" was clicked, we assume no date was clicked so
+ // the selected handler will know not to close the calenar when
+ // in single-click mode.
+ // cal.dateClicked = (el.navtype == 0);
+ cal.dateClicked = false;
+ var year = date.getFullYear();
+ var mon = date.getMonth();
+ function setMonth(m) {
+ var day = date.getDate();
+ var max = date.getMonthDays(m);
+ if (day > max) {
+ date.setDate(max);
+ }
+ date.setMonth(m);
+ };
+ switch (el.navtype) {
+ case 400:
+ Calendar.removeClass(el, "hilite");
+ var text = Calendar._TT["ABOUT"];
+ if (typeof text != "undefined") {
+ text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
+ } else {
+ // FIXME: this should be removed as soon as lang files get updated!
+ text = "Help and about box text is not translated into this language.\n" +
+ "If you know this language and you feel generous please update\n" +
+ "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
+ "and send it back to <mishoo@infoiasi.ro> to get it into the distribution ;-)\n\n" +
+ "Thank you!\n" +
+ "http://dynarch.com/mishoo/calendar.epl\n";
+ }
+ alert(text);
+ return;
+ case -2:
+ if (year > cal.minYear) {
+ date.setFullYear(year - 1);
+ }
+ break;
+ case -1:
+ if (mon > 0) {
+ setMonth(mon - 1);
+ } else if (year-- > cal.minYear) {
+ date.setFullYear(year);
+ setMonth(11);
+ }
+ break;
+ case 1:
+ if (mon < 11) {
+ setMonth(mon + 1);
+ } else if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ setMonth(0);
+ }
+ break;
+ case 2:
+ if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ }
+ break;
+ case 100:
+ cal.setMondayFirst(!cal.mondayFirst);
+ return;
+ case 50:
+ var range = el._range;
+ var current = el.firstChild.data;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ if (ev && ev.shiftKey) {
+ if (!(--i in range))
+ i = range.length - 1;
+ } else if (!(++i in range))
+ i = 0;
+ var newval = range[i];
+ el.firstChild.data = newval;
+ cal.onUpdateTime();
+ return;
+ case 0:
+ // TODAY will bring us here
+ if ((typeof cal.getDateStatus == "function") && cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
+ // remember, "date" was previously set to new
+ // Date() if TODAY was clicked; thus, it
+ // contains today date.
+ return false;
+ }
+ break;
+ }
+ if (!date.equalsTo(cal.date)) {
+ cal.setDate(date);
+ newdate = true;
+ }
+ }
+ if (newdate) {
+ cal.callHandler();
+ }
+ if (closing) {
+ Calendar.removeClass(el, "hilite");
+ cal.callCloseHandler();
+ }
+};
+
+// END: CALENDAR STATIC FUNCTIONS
+
+// BEGIN: CALENDAR OBJECT FUNCTIONS
+
+/**
+ * This function creates the calendar inside the given parent. If _par is
+ * null than it creates a popup calendar inside the BODY element. If _par is
+ * an element, be it BODY, then it creates a non-popup calendar (still
+ * hidden). Some properties need to be set before calling this function.
+ */
+Calendar.prototype.create = function (_par) {
+ var parent = null;
+ if (! _par) {
+ // default parent is the document body, in which case we create
+ // a popup calendar.
+ parent = document.getElementsByTagName("body")[0];
+ this.isPopup = true;
+ } else {
+ parent = _par;
+ this.isPopup = false;
+ }
+ this.date = this.dateStr ? new Date(this.dateStr) : new Date();
+
+ var table = Calendar.createElement("table");
+ this.table = table;
+ table.cellSpacing = 0;
+ table.cellPadding = 0;
+ table.calendar = this;
+ Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
+
+ var div = Calendar.createElement("div");
+ this.element = div;
+ div.className = "calendar";
+ if (this.isPopup) {
+ div.style.position = "absolute";
+ div.style.display = "none";
+ }
+ div.appendChild(table);
+
+ var thead = Calendar.createElement("thead", table);
+ var cell = null;
+ var row = null;
+
+ var cal = this;
+ var hh = function (text, cs, navtype) {
+ cell = Calendar.createElement("td", row);
+ cell.colSpan = cs;
+ cell.className = "button";
+ if (navtype != 0 && Math.abs(navtype) <= 2)
+ cell.className += " nav";
+ Calendar._add_evs(cell);
+ cell.calendar = cal;
+ cell.navtype = navtype;
+ if (text.substr(0, 1) != "&") {
+ cell.appendChild(document.createTextNode(text));
+ }
+ else {
+ // FIXME: dirty hack for entities
+ cell.innerHTML = text;
+ }
+ return cell;
+ };
+
+ row = Calendar.createElement("tr", thead);
+ var title_length = 6;
+ (this.isPopup) && --title_length;
+ (this.weekNumbers) && ++title_length;
+
+ hh("?", 1, 400).ttip = Calendar._TT["INFO"];
+ this.title = hh("", title_length, 300);
+ this.title.className = "title";
+ if (this.isPopup) {
+ this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ this.title.style.cursor = "move";
+ hh("×", 1, 200).ttip = Calendar._TT["CLOSE"];
+ }
+
+ row = Calendar.createElement("tr", thead);
+ row.className = "headrow";
+
+ this._nav_py = hh("«", 1, -2);
+ this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
+
+ this._nav_pm = hh("‹", 1, -1);
+ this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
+
+ this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
+ this._nav_now.ttip = Calendar._TT["GO_TODAY"];
+
+ this._nav_nm = hh("›", 1, 1);
+ this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
+
+ this._nav_ny = hh("»", 1, 2);
+ this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
+
+ // day names
+ row = Calendar.createElement("tr", thead);
+ row.className = "daynames";
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ cell.className = "name wn";
+ cell.appendChild(document.createTextNode(Calendar._TT["WK"]));
+ }
+ for (var i = 7; i > 0; --i) {
+ cell = Calendar.createElement("td", row);
+ cell.appendChild(document.createTextNode(""));
+ if (!i) {
+ cell.navtype = 100;
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+ this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
+ this._displayWeekdays();
+
+ var tbody = Calendar.createElement("tbody", table);
+ this.tbody = tbody;
+
+ for (i = 6; i > 0; --i) {
+ row = Calendar.createElement("tr", tbody);
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ cell.appendChild(document.createTextNode(""));
+ }
+ for (var j = 7; j > 0; --j) {
+ cell = Calendar.createElement("td", row);
+ cell.appendChild(document.createTextNode(""));
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+
+ if (this.showsTime) {
+ row = Calendar.createElement("tr", tbody);
+ row.className = "time";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ cell.innerHTML = " ";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = this.weekNumbers ? 4 : 3;
+
+ (function(){
+ function makeTimePart(className, init, range_start, range_end) {
+ var part = Calendar.createElement("span", cell);
+ part.className = className;
+ part.appendChild(document.createTextNode(init));
+ part.calendar = cal;
+ part.ttip = Calendar._TT["TIME_PART"];
+ part.navtype = 50;
+ part._range = [];
+ if (typeof range_start != "number")
+ part._range = range_start;
+ else {
+ for (var i = range_start; i <= range_end; ++i) {
+ var txt;
+ if (i < 10 && range_end >= 10) txt = '0' + i;
+ else txt = '' + i;
+ part._range[part._range.length] = txt;
+ }
+ }
+ Calendar._add_evs(part);
+ return part;
+ };
+ var hrs = cal.date.getHours();
+ var mins = cal.date.getMinutes();
+ var t12 = !cal.time24;
+ var pm = (hrs > 12);
+ if (t12 && pm) hrs -= 12;
+ var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
+ var span = Calendar.createElement("span", cell);
+ span.appendChild(document.createTextNode(":"));
+ span.className = "colon";
+ var M = makeTimePart("minute", mins, 0, 59);
+ var AP = null;
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ if (t12)
+ AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
+ else
+ cell.innerHTML = " ";
+
+ cal.onSetTime = function() {
+ var hrs = this.date.getHours();
+ var mins = this.date.getMinutes();
+ var pm = (hrs > 12);
+ if (pm && t12) hrs -= 12;
+ H.firstChild.data = (hrs < 10) ? ("0" + hrs) : hrs;
+ M.firstChild.data = (mins < 10) ? ("0" + mins) : mins;
+ if (t12)
+ AP.firstChild.data = pm ? "pm" : "am";
+ };
+
+ cal.onUpdateTime = function() {
+ var date = this.date;
+ var h = parseInt(H.firstChild.data, 10);
+ if (t12) {
+ if (/pm/i.test(AP.firstChild.data) && h < 12)
+ h += 12;
+ else if (/am/i.test(AP.firstChild.data) && h == 12)
+ h = 0;
+ }
+ var d = date.getDate();
+ var m = date.getMonth();
+ var y = date.getFullYear();
+ date.setHours(h);
+ date.setMinutes(parseInt(M.firstChild.data, 10));
+ date.setFullYear(y);
+ date.setMonth(m);
+ date.setDate(d);
+ this.dateClicked = false;
+ this.callHandler();
+ };
+ })();
+ } else {
+ this.onSetTime = this.onUpdateTime = function() {};
+ }
+
+ var tfoot = Calendar.createElement("tfoot", table);
+
+ row = Calendar.createElement("tr", tfoot);
+ row.className = "footrow";
+
+ cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
+ cell.className = "ttip";
+ if (this.isPopup) {
+ cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ cell.style.cursor = "move";
+ }
+ this.tooltips = cell;
+
+ div = Calendar.createElement("div", this.element);
+ this.monthsCombo = div;
+ div.className = "combo";
+ for (i = 0; i < Calendar._MN.length; ++i) {
+ var mn = Calendar.createElement("div");
+ mn.className = Calendar.is_ie ? "label-IEfix" : "label";
+ mn.month = i;
+ mn.appendChild(document.createTextNode(Calendar._SMN[i]));
+ div.appendChild(mn);
+ }
+
+ div = Calendar.createElement("div", this.element);
+ this.yearsCombo = div;
+ div.className = "combo";
+ for (i = 12; i > 0; --i) {
+ var yr = Calendar.createElement("div");
+ yr.className = Calendar.is_ie ? "label-IEfix" : "label";
+ yr.appendChild(document.createTextNode(""));
+ div.appendChild(yr);
+ }
+
+ this._init(this.mondayFirst, this.date);
+ parent.appendChild(this.element);
+};
+
+/** keyboard navigation, only for popup calendars */
+Calendar._keyEvent = function(ev) {
+ if (!window.calendar) {
+ return false;
+ }
+ (Calendar.is_ie) && (ev = window.event);
+ var cal = window.calendar;
+ var act = (Calendar.is_ie || ev.type == "keypress");
+ if (ev.ctrlKey) {
+ switch (ev.keyCode) {
+ case 37: // KEY left
+ act && Calendar.cellClick(cal._nav_pm);
+ break;
+ case 38: // KEY up
+ act && Calendar.cellClick(cal._nav_py);
+ break;
+ case 39: // KEY right
+ act && Calendar.cellClick(cal._nav_nm);
+ break;
+ case 40: // KEY down
+ act && Calendar.cellClick(cal._nav_ny);
+ break;
+ default:
+ return false;
+ }
+ } else switch (ev.keyCode) {
+ case 32: // KEY space (now)
+ Calendar.cellClick(cal._nav_now);
+ break;
+ case 27: // KEY esc
+ act && cal.hide();
+ break;
+ case 37: // KEY left
+ case 38: // KEY up
+ case 39: // KEY right
+ case 40: // KEY down
+ if (act) {
+ var date = cal.date.getDate() - 1;
+ var el = cal.currentDateEl;
+ var ne = null;
+ var prev = (ev.keyCode == 37) || (ev.keyCode == 38);
+ switch (ev.keyCode) {
+ case 37: // KEY left
+ (--date >= 0) && (ne = cal.ar_days[date]);
+ break;
+ case 38: // KEY up
+ date -= 7;
+ (date >= 0) && (ne = cal.ar_days[date]);
+ break;
+ case 39: // KEY right
+ (++date < cal.ar_days.length) && (ne = cal.ar_days[date]);
+ break;
+ case 40: // KEY down
+ date += 7;
+ (date < cal.ar_days.length) && (ne = cal.ar_days[date]);
+ break;
+ }
+ if (!ne) {
+ if (prev) {
+ Calendar.cellClick(cal._nav_pm);
+ } else {
+ Calendar.cellClick(cal._nav_nm);
+ }
+ date = (prev) ? cal.date.getMonthDays() : 1;
+ el = cal.currentDateEl;
+ ne = cal.ar_days[date - 1];
+ }
+ Calendar.removeClass(el, "selected");
+ Calendar.addClass(ne, "selected");
+ cal.date.setDate(ne.caldate);
+ cal.callHandler();
+ cal.currentDateEl = ne;
+ }
+ break;
+ case 13: // KEY enter
+ if (act) {
+ cal.callHandler();
+ cal.hide();
+ }
+ break;
+ default:
+ return false;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+/**
+ * (RE)Initializes the calendar to the given date and style (if mondayFirst is
+ * true it makes Monday the first day of week, otherwise the weeks start on
+ * Sunday.
+ */
+Calendar.prototype._init = function (mondayFirst, date) {
+ var today = new Date();
+ var year = date.getFullYear();
+ if (year < this.minYear) {
+ year = this.minYear;
+ date.setFullYear(year);
+ } else if (year > this.maxYear) {
+ year = this.maxYear;
+ date.setFullYear(year);
+ }
+ this.mondayFirst = mondayFirst;
+ this.date = new Date(date);
+ var month = date.getMonth();
+ var mday = date.getDate();
+ var no_days = date.getMonthDays();
+ date.setDate(1);
+ var wday = date.getDay();
+ var MON = mondayFirst ? 1 : 0;
+ var SAT = mondayFirst ? 5 : 6;
+ var SUN = mondayFirst ? 6 : 0;
+ if (mondayFirst) {
+ wday = (wday > 0) ? (wday - 1) : 6;
+ }
+ var iday = 1;
+ var row = this.tbody.firstChild;
+ var MN = Calendar._SMN[month];
+ var hasToday = ((today.getFullYear() == year) && (today.getMonth() == month));
+ var todayDate = today.getDate();
+ var week_number = date.getWeekNumber();
+ var ar_days = new Array();
+ for (var i = 0; i < 6; ++i) {
+ if (iday > no_days) {
+ row.className = "emptyrow";
+ row = row.nextSibling;
+ continue;
+ }
+ var cell = row.firstChild;
+ if (this.weekNumbers) {
+ cell.className = "day wn";
+ cell.firstChild.data = week_number;
+ cell = cell.nextSibling;
+ }
+ ++week_number;
+ row.className = "daysrow";
+ for (var j = 0; j < 7; ++j) {
+ cell.className = "day";
+ if ((!i && j < wday) || iday > no_days) {
+ // cell.className = "emptycell";
+ cell.innerHTML = " ";
+ cell.disabled = true;
+ cell = cell.nextSibling;
+ continue;
+ }
+ cell.disabled = false;
+ cell.firstChild.data = iday;
+ if (typeof this.getDateStatus == "function") {
+ date.setDate(iday);
+ var status = this.getDateStatus(date, year, month, iday);
+ if (status === true) {
+ cell.className += " disabled";
+ cell.disabled = true;
+ } else {
+ if (/disabled/i.test(status))
+ cell.disabled = true;
+ cell.className += " " + status;
+ }
+ }
+ if (!cell.disabled) {
+ ar_days[ar_days.length] = cell;
+ cell.caldate = iday;
+ cell.ttip = "_";
+ if (iday == mday) {
+ cell.className += " selected";
+ this.currentDateEl = cell;
+ }
+ if (hasToday && (iday == todayDate)) {
+ cell.className += " today";
+ cell.ttip += Calendar._TT["PART_TODAY"];
+ }
+ if (wday == SAT || wday == SUN) {
+ cell.className += " weekend";
+ }
+ }
+ ++iday;
+ ((++wday) ^ 7) || (wday = 0);
+ cell = cell.nextSibling;
+ }
+ row = row.nextSibling;
+ }
+ this.ar_days = ar_days;
+ this.title.firstChild.data = Calendar._MN[month] + ", " + year;
+ this.onSetTime();
+ // PROFILE
+ // this.tooltips.firstChild.data = "Generated in " + ((new Date()) - today) + " ms";
+};
+
+/**
+ * Calls _init function above for going to a certain date (but only if the
+ * date is different than the currently selected one).
+ */
+Calendar.prototype.setDate = function (date) {
+ if (!date.equalsTo(this.date)) {
+ this._init(this.mondayFirst, date);
+ }
+};
+
+/**
+ * Refreshes the calendar. Useful if the "disabledHandler" function is
+ * dynamic, meaning that the list of disabled date can change at runtime.
+ * Just * call this function if you think that the list of disabled dates
+ * should * change.
+ */
+Calendar.prototype.refresh = function () {
+ this._init(this.mondayFirst, this.date);
+};
+
+/** Modifies the "mondayFirst" parameter (EU/US style). */
+Calendar.prototype.setMondayFirst = function (mondayFirst) {
+ this._init(mondayFirst, this.date);
+ this._displayWeekdays();
+};
+
+/**
+ * Allows customization of what dates are enabled. The "unaryFunction"
+ * parameter must be a function object that receives the date (as a JS Date
+ * object) and returns a boolean value. If the returned value is true then
+ * the passed date will be marked as disabled.
+ */
+Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
+ this.getDateStatus = unaryFunction;
+};
+
+/** Customization of allowed year range for the calendar. */
+Calendar.prototype.setRange = function (a, z) {
+ this.minYear = a;
+ this.maxYear = z;
+};
+
+/** Calls the first user handler (selectedHandler). */
+Calendar.prototype.callHandler = function () {
+ if (this.onSelected) {
+ this.onSelected(this, this.date.print(this.dateFormat));
+ }
+};
+
+/** Calls the second user handler (closeHandler). */
+Calendar.prototype.callCloseHandler = function () {
+ if (this.onClose) {
+ this.onClose(this);
+ }
+ this.hideShowCovered();
+};
+
+/** Removes the calendar object from the DOM tree and destroys it. */
+Calendar.prototype.destroy = function () {
+ var el = this.element.parentNode;
+ el.removeChild(this.element);
+ Calendar._C = null;
+ window.calendar = null;
+};
+
+/**
+ * Moves the calendar element to a different section in the DOM tree (changes
+ * its parent).
+ */
+Calendar.prototype.reparent = function (new_parent) {
+ var el = this.element;
+ el.parentNode.removeChild(el);
+ new_parent.appendChild(el);
+};
+
+// This gets called when the user presses a mouse button anywhere in the
+// document, if the calendar is shown. If the click was outside the open
+// calendar this function closes it.
+Calendar._checkCalendar = function(ev) {
+ if (!window.calendar) {
+ return false;
+ }
+ var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
+ for (; el != null && el != calendar.element; el = el.parentNode);
+ if (el == null) {
+ // calls closeHandler which should hide the calendar.
+ window.calendar.callCloseHandler();
+ return Calendar.stopEvent(ev);
+ }
+};
+
+/** Shows the calendar. */
+Calendar.prototype.show = function () {
+ var rows = this.table.getElementsByTagName("tr");
+ for (var i = rows.length; i > 0;) {
+ var row = rows[--i];
+ Calendar.removeClass(row, "rowhilite");
+ var cells = row.getElementsByTagName("td");
+ for (var j = cells.length; j > 0;) {
+ var cell = cells[--j];
+ Calendar.removeClass(cell, "hilite");
+ Calendar.removeClass(cell, "active");
+ }
+ }
+ this.element.style.display = "block";
+ this.hidden = false;
+ if (this.isPopup) {
+ window.calendar = this;
+ Calendar.addEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.addEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.hideShowCovered();
+};
+
+/**
+ * Hides the calendar. Also removes any "hilite" from the class of any TD
+ * element.
+ */
+Calendar.prototype.hide = function () {
+ if (this.isPopup) {
+ Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.element.style.display = "none";
+ this.hidden = true;
+ this.hideShowCovered();
+};
+
+/**
+ * Shows the calendar at a given absolute position (beware that, depending on
+ * the calendar element style -- position property -- this might be relative
+ * to the parent's containing rectangle).
+ */
+Calendar.prototype.showAt = function (x, y) {
+ var s = this.element.style;
+ s.left = x + "px";
+ s.top = y + "px";
+ this.show();
+};
+
+/** Shows the calendar near a given element. */
+Calendar.prototype.showAtElement = function (el, opts) {
+ var self = this;
+ var p = Calendar.getAbsolutePos(el);
+ if (!opts || typeof opts != "string") {
+ this.showAt(p.x, p.y + el.offsetHeight);
+ return true;
+ }
+ this.element.style.display = "block";
+ Calendar.continuation_for_the_fucking_khtml_browser = function() {
+ var w = self.element.offsetWidth;
+ var h = self.element.offsetHeight;
+ self.element.style.display = "none";
+ var valign = opts.substr(0, 1);
+ var halign = "l";
+ if (opts.length > 1) {
+ halign = opts.substr(1, 1);
+ }
+ // vertical alignment
+ switch (valign) {
+ case "T": p.y -= h; break;
+ case "B": p.y += el.offsetHeight; break;
+ case "C": p.y += (el.offsetHeight - h) / 2; break;
+ case "t": p.y += el.offsetHeight - h; break;
+ case "b": break; // already there
+ }
+ // horizontal alignment
+ switch (halign) {
+ case "L": p.x -= w; break;
+ case "R": p.x += el.offsetWidth; break;
+ case "C": p.x += (el.offsetWidth - w) / 2; break;
+ case "r": p.x += el.offsetWidth - w; break;
+ case "l": break; // already there
+ }
+ self.showAt(p.x, p.y);
+ };
+ if (Calendar.is_khtml)
+ setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
+ else
+ Calendar.continuation_for_the_fucking_khtml_browser();
+};
+
+/** Customizes the date format. */
+Calendar.prototype.setDateFormat = function (str) {
+ this.dateFormat = str;
+};
+
+/** Customizes the tooltip date format. */
+Calendar.prototype.setTtDateFormat = function (str) {
+ this.ttDateFormat = str;
+};
+
+/**
+ * Tries to identify the date represented in a string. If successful it also
+ * calls this.setDate which moves the calendar to the given date.
+ */
+Calendar.prototype.parseDate = function (str, fmt) {
+ var y = 0;
+ var m = -1;
+ var d = 0;
+ var a = str.split(/\W+/);
+ if (!fmt) {
+ fmt = this.dateFormat;
+ }
+ var b = [];
+ fmt.replace(/(%.)/g, function(str, par) {
+ return b[b.length] = par;
+ });
+ var i = 0, j = 0;
+ var hr = 0;
+ var min = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (b[i] == "%a" || b[i] == "%A") {
+ continue;
+ }
+ if (b[i] == "%d" || b[i] == "%e") {
+ d = parseInt(a[i], 10);
+ }
+ if (b[i] == "%m") {
+ m = parseInt(a[i], 10) - 1;
+ }
+ if (b[i] == "%Y" || b[i] == "%y") {
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ }
+ if (b[i] == "%b" || b[i] == "%B") {
+ for (j = 0; j < 12; ++j) {
+ if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
+ }
+ } else if (/%[HIkl]/.test(b[i])) {
+ hr = parseInt(a[i], 10);
+ } else if (/%[pP]/.test(b[i])) {
+ if (/pm/i.test(a[i]) && hr < 12)
+ hr += 12;
+ } else if (b[i] == "%M") {
+ min = parseInt(a[i], 10);
+ }
+ }
+ if (y != 0 && m != -1 && d != 0) {
+ this.setDate(new Date(y, m, d, hr, min, 0));
+ return;
+ }
+ y = 0; m = -1; d = 0;
+ for (i = 0; i < a.length; ++i) {
+ if (a[i].search(/[a-zA-Z]+/) != -1) {
+ var t = -1;
+ for (j = 0; j < 12; ++j) {
+ if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
+ }
+ if (t != -1) {
+ if (m != -1) {
+ d = m+1;
+ }
+ m = t;
+ }
+ } else if (parseInt(a[i], 10) <= 12 && m == -1) {
+ m = a[i]-1;
+ } else if (parseInt(a[i], 10) > 31 && y == 0) {
+ y = parseInt(a[i], 10);
+ (y < 100) && (y += (y > 29) ? 1900 : 2000);
+ } else if (d == 0) {
+ d = a[i];
+ }
+ }
+ if (y == 0) {
+ var today = new Date();
+ y = today.getFullYear();
+ }
+ if (m != -1 && d != 0) {
+ this.setDate(new Date(y, m, d, hr, min, 0));
+ }
+};
+
+Calendar.prototype.hideShowCovered = function () {
+ var self = this;
+ Calendar.continuation_for_the_fucking_khtml_browser = function() {
+ function getVisib(obj){
+ var value = obj.style.visibility;
+ if (!value) {
+ if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
+ if (!Calendar.is_khtml)
+ value = document.defaultView.
+ getComputedStyle(obj, "").getPropertyValue("visibility");
+ else
+ value = '';
+ } else if (obj.currentStyle) { // IE
+ value = obj.currentStyle.visibility;
+ } else
+ value = '';
+ }
+ return value;
+ };
+
+ var tags = new Array("applet", "iframe", "select");
+ var el = self.element;
+
+ var p = Calendar.getAbsolutePos(el);
+ var EX1 = p.x;
+ var EX2 = el.offsetWidth + EX1;
+ var EY1 = p.y;
+ var EY2 = el.offsetHeight + EY1;
+
+ for (var k = tags.length; k > 0; ) {
+ var ar = document.getElementsByTagName(tags[--k]);
+ var cc = null;
+
+ for (var i = ar.length; i > 0;) {
+ cc = ar[--i];
+
+ p = Calendar.getAbsolutePos(cc);
+ var CX1 = p.x;
+ var CX2 = cc.offsetWidth + CX1;
+ var CY1 = p.y;
+ var CY2 = cc.offsetHeight + CY1;
+
+ if (self.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
+ if (!cc.__msh_save_visibility) {
+ cc.__msh_save_visibility = getVisib(cc);
+ }
+ cc.style.visibility = cc.__msh_save_visibility;
+ } else {
+ if (!cc.__msh_save_visibility) {
+ cc.__msh_save_visibility = getVisib(cc);
+ }
+ cc.style.visibility = "hidden";
+ }
+ }
+ }
+ };
+ if (Calendar.is_khtml)
+ setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
+ else
+ Calendar.continuation_for_the_fucking_khtml_browser();
+};
+
+/** Internal function; it displays the bar with the names of the weekday. */
+Calendar.prototype._displayWeekdays = function () {
+ var MON = this.mondayFirst ? 0 : 1;
+ var SUN = this.mondayFirst ? 6 : 0;
+ var SAT = this.mondayFirst ? 5 : 6;
+ var cell = this.firstdayname;
+ for (var i = 0; i < 7; ++i) {
+ cell.className = "day name";
+ if (!i) {
+ cell.ttip = this.mondayFirst ? Calendar._TT["SUN_FIRST"] : Calendar._TT["MON_FIRST"];
+ cell.navtype = 100;
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ if (i == SUN || i == SAT) {
+ Calendar.addClass(cell, "weekend");
+ }
+ cell.firstChild.data = Calendar._SDN[i + 1 - MON];
+ cell = cell.nextSibling;
+ }
+};
+
+/** Internal function. Hides all combo boxes that might be displayed. */
+Calendar.prototype._hideCombos = function () {
+ this.monthsCombo.style.display = "none";
+ this.yearsCombo.style.display = "none";
+};
+
+/** Internal function. Starts dragging the element. */
+Calendar.prototype._dragStart = function (ev) {
+ if (this.dragging) {
+ return;
+ }
+ this.dragging = true;
+ var posX;
+ var posY;
+ if (Calendar.is_ie) {
+ posY = window.event.clientY + document.body.scrollTop;
+ posX = window.event.clientX + document.body.scrollLeft;
+ } else {
+ posY = ev.clientY + window.scrollY;
+ posX = ev.clientX + window.scrollX;
+ }
+ var st = this.element.style;
+ this.xOffs = posX - parseInt(st.left);
+ this.yOffs = posY - parseInt(st.top);
+ with (Calendar) {
+ addEvent(document, "mousemove", calDragIt);
+ addEvent(document, "mouseover", stopEvent);
+ addEvent(document, "mouseup", calDragEnd);
+ }
+};
+
+// BEGIN: DATE OBJECT PATCHES
+
+/** Adds the number of days array to the Date object. */
+Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
+
+/** Constants used for time computations */
+Date.SECOND = 1000 /* milliseconds */;
+Date.MINUTE = 60 * Date.SECOND;
+Date.HOUR = 60 * Date.MINUTE;
+Date.DAY = 24 * Date.HOUR;
+Date.WEEK = 7 * Date.DAY;
+
+/** Returns the number of days in the current month */
+Date.prototype.getMonthDays = function(month) {
+ var year = this.getFullYear();
+ if (typeof month == "undefined") {
+ month = this.getMonth();
+ }
+ if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
+ return 29;
+ } else {
+ return Date._MD[month];
+ }
+};
+
+/** Returns the number of day in the year. */
+Date.prototype.getDayOfYear = function() {
+ var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var then = new Date(this.getFullYear(), 0, 1, 0, 0, 0);
+ var time = now - then;
+ return Math.floor(time / Date.DAY);
+};
+
+/** Returns the number of the week in year, as defined in ISO 8601. */
+Date.prototype.getWeekNumber = function() {
+ var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
+ var then = new Date(this.getFullYear(), 0, 1, 0, 0, 0);
+ var time = now - then;
+ var day = then.getDay(); // 0 means Sunday
+ if (day == 0) day = 7;
+ (day > 4) && (day -= 4) || (day += 3);
+ return Math.round(((time / Date.DAY) + day) / 7);
+};
+
+/** Checks dates equality (ignores time) */
+Date.prototype.equalsTo = function(date) {
+ return ((this.getFullYear() == date.getFullYear()) &&
+ (this.getMonth() == date.getMonth()) &&
+ (this.getDate() == date.getDate()) &&
+ (this.getHours() == date.getHours()) &&
+ (this.getMinutes() == date.getMinutes()));
+};
+
+/** Prints the date in a string according to the given format. */
+Date.prototype.print = function (str) {
+ var m = this.getMonth();
+ var d = this.getDate();
+ var y = this.getFullYear();
+ var wn = this.getWeekNumber();
+ var w = this.getDay();
+ var s = {};
+ var hr = this.getHours();
+ var pm = (hr >= 12);
+ var ir = (pm) ? (hr - 12) : hr;
+ var dy = this.getDayOfYear();
+ if (ir == 0)
+ ir = 12;
+ var min = this.getMinutes();
+ var sec = this.getSeconds();
+ s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
+ s["%A"] = Calendar._DN[w]; // full weekday name
+ s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
+ s["%B"] = Calendar._MN[m]; // full month name
+ // FIXME: %c : preferred date and time representation for the current locale
+ s["%C"] = 1 + Math.floor(y / 100); // the century number
+ s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
+ s["%e"] = d; // the day of the month (range 1 to 31)
+ // FIXME: %D : american date style: %m/%d/%y
+ // FIXME: %E, %F, %G, %g, %h (man strftime)
+ s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
+ s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
+ s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
+ s["%k"] = hr; // hour, range 0 to 23 (24h format)
+ s["%l"] = ir; // hour, range 1 to 12 (12h format)
+ s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
+ s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
+ s["%n"] = "\n"; // a newline character
+ s["%p"] = pm ? "PM" : "AM";
+ s["%P"] = pm ? "pm" : "am";
+ // FIXME: %r : the time in am/pm notation %I:%M:%S %p
+ // FIXME: %R : the time in 24-hour notation %H:%M
+ s["%s"] = Math.floor(this.getTime() / 1000);
+ s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
+ s["%t"] = "\t"; // a tab character
+ // FIXME: %T : the time in 24-hour notation (%H:%M:%S)
+ s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
+ s["%u"] = w + 1; // the day of the week (range 1 to 7, 1 = MON)
+ s["%w"] = w; // the day of the week (range 0 to 6, 0 = SUN)
+ // FIXME: %x : preferred date representation for the current locale without the time
+ // FIXME: %X : preferred time representation for the current locale without the date
+ s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
+ s["%Y"] = y; // year with the century
+ s["%%"] = "%"; // a literal '%' character
+ var re = Date._msh_formatRegexp;
+ if (typeof re == "undefined") {
+ var tmp = "";
+ for (var i in s)
+ tmp += tmp ? ("|" + i) : i;
+ Date._msh_formatRegexp = re = new RegExp("(" + tmp + ")", 'g');
+ }
+ return str.replace(re, function(match, par) { return s[par]; });
+};
+
+// END: DATE OBJECT PATCHES
+
+// global object that remembers the calendar
+window.calendar = null;
--- /dev/null
+/* Copyright Mihai Bazon, 2002, 2003 | http://dynarch.com/mishoo/
+ * ------------------------------------------------------------------
+ *
+ * The DHTML Calendar, version 0.9.5 "Your favorite time, bis"
+ *
+ * Details and latest version at:
+ * http://dynarch.com/mishoo/calendar.epl
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ */
+ Calendar=function(mondayFirst,dateStr,onSelected,onClose){this.activeDiv=null;this.currentDateEl=null;this.getDateStatus=null;this.timeout=null;this.onSelected=onSelected||null;this.onClose=onClose||null;this.dragging=false;this.hidden=false;this.minYear=1970;this.maxYear=2050;this.dateFormat=Calendar._TT["DEF_DATE_FORMAT"];this.ttDateFormat=Calendar._TT["TT_DATE_FORMAT"];this.isPopup=true;this.weekNumbers=true;this.mondayFirst=mondayFirst;this.dateStr=dateStr;this.ar_days=null;this.showsTime=false;this.time24=true;this.table=null;this.element=null;this.tbody=null;this.firstdayname=null;this.monthsCombo=null;this.yearsCombo=null;this.hilitedMonth=null;this.activeMonth=null;this.hilitedYear=null;this.activeYear=null;this.dateClicked=false;if(typeof Calendar._SDN=="undefined"){if(typeof Calendar._SDN_len=="undefined")Calendar._SDN_len=3;var ar=new Array();for(var i=8;i>0;){ar[--i]=Calendar._DN[i].substr(0,Calendar._SDN_len);}Calendar._SDN=ar;if(typeof Calendar._SMN_len=="undefined")Calendar._SMN_len=3;ar=new Array();for(var i=12;i>0;){ar[--i]=Calendar._MN[i].substr(0,Calendar._SMN_len);}Calendar._SMN=ar;}};Calendar._C=null;Calendar.is_ie=(/msie/i.test(navigator.userAgent)&&!/opera/i.test(navigator.userAgent));Calendar.is_opera=/opera/i.test(navigator.userAgent);Calendar.is_khtml=/Konqueror|Safari|KHTML/i.test(navigator.userAgent);Calendar.getAbsolutePos=function(el){var SL=0,ST=0;var is_div=/^div$/i.test(el.tagName);if(is_div&&el.scrollLeft)SL=el.scrollLeft;if(is_div&&el.scrollTop)ST=el.scrollTop;var r={x:el.offsetLeft-SL,y:el.offsetTop-ST};if(el.offsetParent){var tmp=Calendar.getAbsolutePos(el.offsetParent);r.x+=tmp.x;r.y+=tmp.y;}return r;};Calendar.isRelated=function(el,evt){var related=evt.relatedTarget;if(!related){var type=evt.type;if(type=="mouseover"){related=evt.fromElement;}else if(type=="mouseout"){related=evt.toElement;}}while(related){if(related==el){return true;}related=related.parentNode;}return false;};Calendar.removeClass=function(el,className){if(!(el&&el.className)){return;}var cls=el.className.split(" ");var ar=new Array();for(var i=cls.length;i>0;){if(cls[--i]!=className){ar[ar.length]=cls[i];}}el.className=ar.join(" ");};Calendar.addClass=function(el,className){Calendar.removeClass(el,className);el.className+=" "+className;};Calendar.getElement=function(ev){if(Calendar.is_ie){return window.event.srcElement;}else{return ev.currentTarget;}};Calendar.getTargetElement=function(ev){if(Calendar.is_ie){return window.event.srcElement;}else{return ev.target;}};Calendar.stopEvent=function(ev){ev||(ev=window.event);if(Calendar.is_ie){ev.cancelBubble=true;ev.returnValue=false;}else{ev.preventDefault();ev.stopPropagation();}return false;};Calendar.addEvent=function(el,evname,func){if(el.attachEvent){el.attachEvent("on"+evname,func);}else if(el.addEventListener){el.addEventListener(evname,func,true);}else{el["on"+evname]=func;}};Calendar.removeEvent=function(el,evname,func){if(el.detachEvent){el.detachEvent("on"+evname,func);}else if(el.removeEventListener){el.removeEventListener(evname,func,true);}else{el["on"+evname]=null;}};Calendar.createElement=function(type,parent){var el=null;if(document.createElementNS){el=document.createElementNS("http://www.w3.org/1999/xhtml",type);}else{el=document.createElement(type);}if(typeof parent!="undefined"){parent.appendChild(el);}return el;};Calendar._add_evs=function(el){with(Calendar){addEvent(el,"mouseover",dayMouseOver);addEvent(el,"mousedown",dayMouseDown);addEvent(el,"mouseout",dayMouseOut);if(is_ie){addEvent(el,"dblclick",dayMouseDblClick);el.setAttribute("unselectable",true);}}};Calendar.findMonth=function(el){if(typeof el.month!="undefined"){return el;}else if(typeof el.parentNode.month!="undefined"){return el.parentNode;}return null;};Calendar.findYear=function(el){if(typeof el.year!="undefined"){return el;}else if(typeof el.parentNode.year!="undefined"){return el.parentNode;}return null;};Calendar.showMonthsCombo=function(){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var mc=cal.monthsCombo;if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}if(cal.activeMonth){Calendar.removeClass(cal.activeMonth,"active");}var mon=cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];Calendar.addClass(mon,"active");cal.activeMonth=mon;var s=mc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else s.left=(cd.offsetLeft+cd.offsetWidth-mc.offsetWidth)+"px";s.top=(cd.offsetTop+cd.offsetHeight)+"px";};Calendar.showYearsCombo=function(fwd){var cal=Calendar._C;if(!cal){return false;}var cal=cal;var cd=cal.activeDiv;var yc=cal.yearsCombo;if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}if(cal.activeYear){Calendar.removeClass(cal.activeYear,"active");}cal.activeYear=null;var Y=cal.date.getFullYear()+(fwd?1:-1);var yr=yc.firstChild;var show=false;for(var i=12;i>0;--i){if(Y>=cal.minYear&&Y<=cal.maxYear){yr.firstChild.data=Y;yr.year=Y;yr.style.display="block";show=true;}else{yr.style.display="none";}yr=yr.nextSibling;Y+=fwd?2:-2;}if(show){var s=yc.style;s.display="block";if(cd.navtype<0)s.left=cd.offsetLeft+"px";else s.left=(cd.offsetLeft+cd.offsetWidth-yc.offsetWidth)+"px";s.top=(cd.offsetTop+cd.offsetHeight)+"px";}};Calendar.tableMouseUp=function(ev){var cal=Calendar._C;if(!cal){return false;}if(cal.timeout){clearTimeout(cal.timeout);}var el=cal.activeDiv;if(!el){return false;}var target=Calendar.getTargetElement(ev);ev||(ev=window.event);Calendar.removeClass(el,"active");if(target==el||target.parentNode==el){Calendar.cellClick(el,ev);}var mon=Calendar.findMonth(target);var date=null;if(mon){date=new Date(cal.date);if(mon.month!=date.getMonth()){date.setMonth(mon.month);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}else{var year=Calendar.findYear(target);if(year){date=new Date(cal.date);if(year.year!=date.getFullYear()){date.setFullYear(year.year);cal.setDate(date);cal.dateClicked=false;cal.callHandler();}}}with(Calendar){removeEvent(document,"mouseup",tableMouseUp);removeEvent(document,"mouseover",tableMouseOver);removeEvent(document,"mousemove",tableMouseOver);cal._hideCombos();_C=null;return stopEvent(ev);}};Calendar.tableMouseOver=function(ev){var cal=Calendar._C;if(!cal){return;}var el=cal.activeDiv;var target=Calendar.getTargetElement(ev);if(target==el||target.parentNode==el){Calendar.addClass(el,"hilite active");Calendar.addClass(el.parentNode,"rowhilite");}else{if(typeof el.navtype=="undefined"||(el.navtype!=50&&(el.navtype==0||Math.abs(el.navtype)>2)))Calendar.removeClass(el,"active");Calendar.removeClass(el,"hilite");Calendar.removeClass(el.parentNode,"rowhilite");}ev||(ev=window.event);if(el.navtype==50&&target!=el){var pos=Calendar.getAbsolutePos(el);var w=el.offsetWidth;var x=ev.clientX;var dx;var decrease=true;if(x>pos.x+w){dx=x-pos.x-w;decrease=false;}else dx=pos.x-x;if(dx<0)dx=0;var range=el._range;var current=el._current;var count=Math.floor(dx/10)%range.length;for(var i=range.length;--i>=0;)if(range[i]==current)break;while(count-->0)if(decrease){if(!(--i in range))i=range.length-1;}else if(!(++i in range))i=0;var newval=range[i];el.firstChild.data=newval;cal.onUpdateTime();}var mon=Calendar.findMonth(target);if(mon){if(mon.month!=cal.date.getMonth()){if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}Calendar.addClass(mon,"hilite");cal.hilitedMonth=mon;}else if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}}else{if(cal.hilitedMonth){Calendar.removeClass(cal.hilitedMonth,"hilite");}var year=Calendar.findYear(target);if(year){if(year.year!=cal.date.getFullYear()){if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}Calendar.addClass(year,"hilite");cal.hilitedYear=year;}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}else if(cal.hilitedYear){Calendar.removeClass(cal.hilitedYear,"hilite");}}return Calendar.stopEvent(ev);};Calendar.tableMouseDown=function(ev){if(Calendar.getTargetElement(ev)==Calendar.getElement(ev)){return Calendar.stopEvent(ev);}};Calendar.calDragIt=function(ev){var cal=Calendar._C;if(!(cal&&cal.dragging)){return false;}var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posX=ev.pageX;posY=ev.pageY;}cal.hideShowCovered();var st=cal.element.style;st.left=(posX-cal.xOffs)+"px";st.top=(posY-cal.yOffs)+"px";return Calendar.stopEvent(ev);};Calendar.calDragEnd=function(ev){var cal=Calendar._C;if(!cal){return false;}cal.dragging=false;with(Calendar){removeEvent(document,"mousemove",calDragIt);removeEvent(document,"mouseover",stopEvent);removeEvent(document,"mouseup",calDragEnd);tableMouseUp(ev);}cal.hideShowCovered();};Calendar.dayMouseDown=function(ev){var el=Calendar.getElement(ev);if(el.disabled){return false;}var cal=el.calendar;cal.activeDiv=el;Calendar._C=cal;if(el.navtype!=300)with(Calendar){if(el.navtype==50)el._current=el.firstChild.data;addClass(el,"hilite active");addEvent(document,"mouseover",tableMouseOver);addEvent(document,"mousemove",tableMouseOver);addEvent(document,"mouseup",tableMouseUp);}else if(cal.isPopup){cal._dragStart(ev);}if(el.navtype==-1||el.navtype==1){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout("Calendar.showMonthsCombo()",250);}else if(el.navtype==-2||el.navtype==2){if(cal.timeout)clearTimeout(cal.timeout);cal.timeout=setTimeout((el.navtype>0)?"Calendar.showYearsCombo(true)":"Calendar.showYearsCombo(false)",250);}else{cal.timeout=null;}return Calendar.stopEvent(ev);};Calendar.dayMouseDblClick=function(ev){Calendar.cellClick(Calendar.getElement(ev),ev||window.event);if(Calendar.is_ie){document.selection.empty();}};Calendar.dayMouseOver=function(ev){var el=Calendar.getElement(ev);if(Calendar.isRelated(el,ev)||Calendar._C||el.disabled){return false;}if(el.ttip){if(el.ttip.substr(0,1)=="_"){var date=null;with(el.calendar.date){date=new Date(getFullYear(),getMonth(),el.caldate);}el.ttip=date.print(el.calendar.ttDateFormat)+el.ttip.substr(1);}el.calendar.tooltips.firstChild.data=el.ttip;}if(el.navtype!=300){Calendar.addClass(el,"hilite");if(el.caldate){Calendar.addClass(el.parentNode,"rowhilite");}}return Calendar.stopEvent(ev);};Calendar.dayMouseOut=function(ev){with(Calendar){var el=getElement(ev);if(isRelated(el,ev)||_C||el.disabled){return false;}removeClass(el,"hilite");if(el.caldate){removeClass(el.parentNode,"rowhilite");}el.calendar.tooltips.firstChild.data=_TT["SEL_DATE"];return stopEvent(ev);}};Calendar.cellClick=function(el,ev){var cal=el.calendar;var closing=false;var newdate=false;var date=null;if(typeof el.navtype=="undefined"){Calendar.removeClass(cal.currentDateEl,"selected");Calendar.addClass(el,"selected");closing=(cal.currentDateEl==el);if(!closing){cal.currentDateEl=el;}cal.date.setDate(el.caldate);date=cal.date;newdate=true;cal.dateClicked=true;}else{if(el.navtype==200){Calendar.removeClass(el,"hilite");cal.callCloseHandler();return;}date=(el.navtype==0)?new Date():new Date(cal.date);cal.dateClicked=false;var year=date.getFullYear();var mon=date.getMonth();function setMonth(m){var day=date.getDate();var max=date.getMonthDays(m);if(day>max){date.setDate(max);}date.setMonth(m);};switch(el.navtype){case 400:Calendar.removeClass(el,"hilite");var text=Calendar._TT["ABOUT"];if(typeof text!="undefined"){text+=cal.showsTime?Calendar._TT["ABOUT_TIME"]:"";}else{text="Help and about box text is not translated into this language.\n"+"If you know this language and you feel generous please update\n"+"the corresponding file in \"lang\" subdir to match calendar-en.js\n"+"and send it back to <mishoo@infoiasi.ro> to get it into the distribution ;-)\n\n"+"Thank you!\n"+"http://dynarch.com/mishoo/calendar.epl\n";}alert(text);return;case-2:if(year>cal.minYear){date.setFullYear(year-1);}break;case-1:if(mon>0){setMonth(mon-1);}else if(year-->cal.minYear){date.setFullYear(year);setMonth(11);}break;case 1:if(mon<11){setMonth(mon+1);}else if(year<cal.maxYear){date.setFullYear(year+1);setMonth(0);}break;case 2:if(year<cal.maxYear){date.setFullYear(year+1);}break;case 100:cal.setMondayFirst(!cal.mondayFirst);return;case 50:var range=el._range;var current=el.firstChild.data;for(var i=range.length;--i>=0;)if(range[i]==current)break;if(ev&&ev.shiftKey){if(!(--i in range))i=range.length-1;}else if(!(++i in range))i=0;var newval=range[i];el.firstChild.data=newval;cal.onUpdateTime();return;case 0:if((typeof cal.getDateStatus=="function")&&cal.getDateStatus(date,date.getFullYear(),date.getMonth(),date.getDate())){return false;}break;}if(!date.equalsTo(cal.date)){cal.setDate(date);newdate=true;}}if(newdate){cal.callHandler();}if(closing){Calendar.removeClass(el,"hilite");cal.callCloseHandler();}};Calendar.prototype.create=function(_par){var parent=null;if(!_par){parent=document.getElementsByTagName("body")[0];this.isPopup=true;}else{parent=_par;this.isPopup=false;}this.date=this.dateStr?new Date(this.dateStr):new Date();var table=Calendar.createElement("table");this.table=table;table.cellSpacing=0;table.cellPadding=0;table.calendar=this;Calendar.addEvent(table,"mousedown",Calendar.tableMouseDown);var div=Calendar.createElement("div");this.element=div;div.className="calendar";if(this.isPopup){div.style.position="absolute";div.style.display="none";}div.appendChild(table);var thead=Calendar.createElement("thead",table);var cell=null;var row=null;var cal=this;var hh=function(text,cs,navtype){cell=Calendar.createElement("td",row);cell.colSpan=cs;cell.className="button";if(navtype!=0&&Math.abs(navtype)<=2)cell.className+=" nav";Calendar._add_evs(cell);cell.calendar=cal;cell.navtype=navtype;if(text.substr(0,1)!="&"){cell.appendChild(document.createTextNode(text));}else{cell.innerHTML=text;}return cell;};row=Calendar.createElement("tr",thead);var title_length=6;(this.isPopup)&&--title_length;(this.weekNumbers)&&++title_length;hh("?",1,400).ttip=Calendar._TT["INFO"];this.title=hh("",title_length,300);this.title.className="title";if(this.isPopup){this.title.ttip=Calendar._TT["DRAG_TO_MOVE"];this.title.style.cursor="move";hh("×",1,200).ttip=Calendar._TT["CLOSE"];}row=Calendar.createElement("tr",thead);row.className="headrow";this._nav_py=hh("«",1,-2);this._nav_py.ttip=Calendar._TT["PREV_YEAR"];this._nav_pm=hh("‹",1,-1);this._nav_pm.ttip=Calendar._TT["PREV_MONTH"];this._nav_now=hh(Calendar._TT["TODAY"],this.weekNumbers?4:3,0);this._nav_now.ttip=Calendar._TT["GO_TODAY"];this._nav_nm=hh("›",1,1);this._nav_nm.ttip=Calendar._TT["NEXT_MONTH"];this._nav_ny=hh("»",1,2);this._nav_ny.ttip=Calendar._TT["NEXT_YEAR"];row=Calendar.createElement("tr",thead);row.className="daynames";if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.className="name wn";cell.appendChild(document.createTextNode(Calendar._TT["WK"]));}for(var i=7;i>0;--i){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));if(!i){cell.navtype=100;cell.calendar=this;Calendar._add_evs(cell);}}this.firstdayname=(this.weekNumbers)?row.firstChild.nextSibling:row.firstChild;this._displayWeekdays();var tbody=Calendar.createElement("tbody",table);this.tbody=tbody;for(i=6;i>0;--i){row=Calendar.createElement("tr",tbody);if(this.weekNumbers){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));}for(var j=7;j>0;--j){cell=Calendar.createElement("td",row);cell.appendChild(document.createTextNode(""));cell.calendar=this;Calendar._add_evs(cell);}}if(this.showsTime){row=Calendar.createElement("tr",tbody);row.className="time";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;cell.innerHTML=" ";cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=this.weekNumbers?4:3;(function(){function makeTimePart(className,init,range_start,range_end){var part=Calendar.createElement("span",cell);part.className=className;part.appendChild(document.createTextNode(init));part.calendar=cal;part.ttip=Calendar._TT["TIME_PART"];part.navtype=50;part._range=[];if(typeof range_start!="number")part._range=range_start;else{for(var i=range_start;i<=range_end;++i){var txt;if(i<10&&range_end>=10)txt='0'+i;else txt=''+i;part._range[part._range.length]=txt;}}Calendar._add_evs(part);return part;};var hrs=cal.date.getHours();var mins=cal.date.getMinutes();var t12=!cal.time24;var pm=(hrs>12);if(t12&&pm)hrs-=12;var H=makeTimePart("hour",hrs,t12?1:0,t12?12:23);var span=Calendar.createElement("span",cell);span.appendChild(document.createTextNode(":"));span.className="colon";var M=makeTimePart("minute",mins,0,59);var AP=null;cell=Calendar.createElement("td",row);cell.className="time";cell.colSpan=2;if(t12)AP=makeTimePart("ampm",pm?"pm":"am",["am","pm"]);else cell.innerHTML=" ";cal.onSetTime=function(){var hrs=this.date.getHours();var mins=this.date.getMinutes();var pm=(hrs>12);if(pm&&t12)hrs-=12;H.firstChild.data=(hrs<10)?("0"+hrs):hrs;M.firstChild.data=(mins<10)?("0"+mins):mins;if(t12)AP.firstChild.data=pm?"pm":"am";};cal.onUpdateTime=function(){var date=this.date;var h=parseInt(H.firstChild.data,10);if(t12){if(/pm/i.test(AP.firstChild.data)&&h<12)h+=12;else if(/am/i.test(AP.firstChild.data)&&h==12)h=0;}var d=date.getDate();var m=date.getMonth();var y=date.getFullYear();date.setHours(h);date.setMinutes(parseInt(M.firstChild.data,10));date.setFullYear(y);date.setMonth(m);date.setDate(d);this.dateClicked=false;this.callHandler();};})();}else{this.onSetTime=this.onUpdateTime=function(){};}var tfoot=Calendar.createElement("tfoot",table);row=Calendar.createElement("tr",tfoot);row.className="footrow";cell=hh(Calendar._TT["SEL_DATE"],this.weekNumbers?8:7,300);cell.className="ttip";if(this.isPopup){cell.ttip=Calendar._TT["DRAG_TO_MOVE"];cell.style.cursor="move";}this.tooltips=cell;div=Calendar.createElement("div",this.element);this.monthsCombo=div;div.className="combo";for(i=0;i<Calendar._MN.length;++i){var mn=Calendar.createElement("div");mn.className=Calendar.is_ie?"label-IEfix":"label";mn.month=i;mn.appendChild(document.createTextNode(Calendar._SMN[i]));div.appendChild(mn);}div=Calendar.createElement("div",this.element);this.yearsCombo=div;div.className="combo";for(i=12;i>0;--i){var yr=Calendar.createElement("div");yr.className=Calendar.is_ie?"label-IEfix":"label";yr.appendChild(document.createTextNode(""));div.appendChild(yr);}this._init(this.mondayFirst,this.date);parent.appendChild(this.element);};Calendar._keyEvent=function(ev){if(!window.calendar){return false;}(Calendar.is_ie)&&(ev=window.event);var cal=window.calendar;var act=(Calendar.is_ie||ev.type=="keypress");if(ev.ctrlKey){switch(ev.keyCode){case 37:act&&Calendar.cellClick(cal._nav_pm);break;case 38:act&&Calendar.cellClick(cal._nav_py);break;case 39:act&&Calendar.cellClick(cal._nav_nm);break;case 40:act&&Calendar.cellClick(cal._nav_ny);break;default:return false;}}else switch(ev.keyCode){case 32:Calendar.cellClick(cal._nav_now);break;case 27:act&&cal.hide();break;case 37:case 38:case 39:case 40:if(act){var date=cal.date.getDate()-1;var el=cal.currentDateEl;var ne=null;var prev=(ev.keyCode==37)||(ev.keyCode==38);switch(ev.keyCode){case 37:(--date>=0)&&(ne=cal.ar_days[date]);break;case 38:date-=7;(date>=0)&&(ne=cal.ar_days[date]);break;case 39:(++date<cal.ar_days.length)&&(ne=cal.ar_days[date]);break;case 40:date+=7;(date<cal.ar_days.length)&&(ne=cal.ar_days[date]);break;}if(!ne){if(prev){Calendar.cellClick(cal._nav_pm);}else{Calendar.cellClick(cal._nav_nm);}date=(prev)?cal.date.getMonthDays():1;el=cal.currentDateEl;ne=cal.ar_days[date-1];}Calendar.removeClass(el,"selected");Calendar.addClass(ne,"selected");cal.date.setDate(ne.caldate);cal.callHandler();cal.currentDateEl=ne;}break;case 13:if(act){cal.callHandler();cal.hide();}break;default:return false;}return Calendar.stopEvent(ev);};Calendar.prototype._init=function(mondayFirst,date){var today=new Date();var year=date.getFullYear();if(year<this.minYear){year=this.minYear;date.setFullYear(year);}else if(year>this.maxYear){year=this.maxYear;date.setFullYear(year);}this.mondayFirst=mondayFirst;this.date=new Date(date);var month=date.getMonth();var mday=date.getDate();var no_days=date.getMonthDays();date.setDate(1);var wday=date.getDay();var MON=mondayFirst?1:0;var SAT=mondayFirst?5:6;var SUN=mondayFirst?6:0;if(mondayFirst){wday=(wday>0)?(wday-1):6;}var iday=1;var row=this.tbody.firstChild;var MN=Calendar._SMN[month];var hasToday=((today.getFullYear()==year)&&(today.getMonth()==month));var todayDate=today.getDate();var week_number=date.getWeekNumber();var ar_days=new Array();for(var i=0;i<6;++i){if(iday>no_days){row.className="emptyrow";row=row.nextSibling;continue;}var cell=row.firstChild;if(this.weekNumbers){cell.className="day wn";cell.firstChild.data=week_number;cell=cell.nextSibling;}++week_number;row.className="daysrow";for(var j=0;j<7;++j){cell.className="day";if((!i&&j<wday)||iday>no_days){cell.innerHTML=" ";cell.disabled=true;cell=cell.nextSibling;continue;}cell.disabled=false;cell.firstChild.data=iday;if(typeof this.getDateStatus=="function"){date.setDate(iday);var status=this.getDateStatus(date,year,month,iday);if(status===true){cell.className+=" disabled";cell.disabled=true;}else{if(/disabled/i.test(status))cell.disabled=true;cell.className+=" "+status;}}if(!cell.disabled){ar_days[ar_days.length]=cell;cell.caldate=iday;cell.ttip="_";if(iday==mday){cell.className+=" selected";this.currentDateEl=cell;}if(hasToday&&(iday==todayDate)){cell.className+=" today";cell.ttip+=Calendar._TT["PART_TODAY"];}if(wday==SAT||wday==SUN){cell.className+=" weekend";}}++iday;((++wday)^ 7)||(wday=0);cell=cell.nextSibling;}row=row.nextSibling;}this.ar_days=ar_days;this.title.firstChild.data=Calendar._MN[month]+", "+year;this.onSetTime();};Calendar.prototype.setDate=function(date){if(!date.equalsTo(this.date)){this._init(this.mondayFirst,date);}};Calendar.prototype.refresh=function(){this._init(this.mondayFirst,this.date);};Calendar.prototype.setMondayFirst=function(mondayFirst){this._init(mondayFirst,this.date);this._displayWeekdays();};Calendar.prototype.setDateStatusHandler=Calendar.prototype.setDisabledHandler=function(unaryFunction){this.getDateStatus=unaryFunction;};Calendar.prototype.setRange=function(a,z){this.minYear=a;this.maxYear=z;};Calendar.prototype.callHandler=function(){if(this.onSelected){this.onSelected(this,this.date.print(this.dateFormat));}};Calendar.prototype.callCloseHandler=function(){if(this.onClose){this.onClose(this);}this.hideShowCovered();};Calendar.prototype.destroy=function(){var el=this.element.parentNode;el.removeChild(this.element);Calendar._C=null;window.calendar=null;};Calendar.prototype.reparent=function(new_parent){var el=this.element;el.parentNode.removeChild(el);new_parent.appendChild(el);};Calendar._checkCalendar=function(ev){if(!window.calendar){return false;}var el=Calendar.is_ie?Calendar.getElement(ev):Calendar.getTargetElement(ev);for(;el!=null&&el!=calendar.element;el=el.parentNode);if(el==null){window.calendar.callCloseHandler();return Calendar.stopEvent(ev);}};Calendar.prototype.show=function(){var rows=this.table.getElementsByTagName("tr");for(var i=rows.length;i>0;){var row=rows[--i];Calendar.removeClass(row,"rowhilite");var cells=row.getElementsByTagName("td");for(var j=cells.length;j>0;){var cell=cells[--j];Calendar.removeClass(cell,"hilite");Calendar.removeClass(cell,"active");}}this.element.style.display="block";this.hidden=false;if(this.isPopup){window.calendar=this;Calendar.addEvent(document,"keydown",Calendar._keyEvent);Calendar.addEvent(document,"keypress",Calendar._keyEvent);Calendar.addEvent(document,"mousedown",Calendar._checkCalendar);}this.hideShowCovered();};Calendar.prototype.hide=function(){if(this.isPopup){Calendar.removeEvent(document,"keydown",Calendar._keyEvent);Calendar.removeEvent(document,"keypress",Calendar._keyEvent);Calendar.removeEvent(document,"mousedown",Calendar._checkCalendar);}this.element.style.display="none";this.hidden=true;this.hideShowCovered();};Calendar.prototype.showAt=function(x,y){var s=this.element.style;s.left=x+"px";s.top=y+"px";this.show();};Calendar.prototype.showAtElement=function(el,opts){var self=this;var p=Calendar.getAbsolutePos(el);if(!opts||typeof opts!="string"){this.showAt(p.x,p.y+el.offsetHeight);return true;}this.element.style.display="block";Calendar.continuation_for_the_fucking_khtml_browser=function(){var w=self.element.offsetWidth;var h=self.element.offsetHeight;self.element.style.display="none";var valign=opts.substr(0,1);var halign="l";if(opts.length>1){halign=opts.substr(1,1);}switch(valign){case "T":p.y-=h;break;case "B":p.y+=el.offsetHeight;break;case "C":p.y+=(el.offsetHeight-h)/2;break;case "t":p.y+=el.offsetHeight-h;break;case "b":break;}switch(halign){case "L":p.x-=w;break;case "R":p.x+=el.offsetWidth;break;case "C":p.x+=(el.offsetWidth-w)/2;break;case "r":p.x+=el.offsetWidth-w;break;case "l":break;}self.showAt(p.x,p.y);};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype.setDateFormat=function(str){this.dateFormat=str;};Calendar.prototype.setTtDateFormat=function(str){this.ttDateFormat=str;};Calendar.prototype.parseDate=function(str,fmt){var y=0;var m=-1;var d=0;var a=str.split(/\W+/);if(!fmt){fmt=this.dateFormat;}var b=[];fmt.replace(/(%.)/g,function(str,par){return b[b.length]=par;});var i=0,j=0;var hr=0;var min=0;for(i=0;i<a.length;++i){if(b[i]=="%a"||b[i]=="%A"){continue;}if(b[i]=="%d"||b[i]=="%e"){d=parseInt(a[i],10);}if(b[i]=="%m"){m=parseInt(a[i],10)-1;}if(b[i]=="%Y"||b[i]=="%y"){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}if(b[i]=="%b"||b[i]=="%B"){for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){m=j;break;}}}else if(/%[HIkl]/.test(b[i])){hr=parseInt(a[i],10);}else if(/%[pP]/.test(b[i])){if(/pm/i.test(a[i])&&hr<12)hr+=12;}else if(b[i]=="%M"){min=parseInt(a[i],10);}}if(y!=0&&m!=-1&&d!=0){this.setDate(new Date(y,m,d,hr,min,0));return;}y=0;m=-1;d=0;for(i=0;i<a.length;++i){if(a[i].search(/[a-zA-Z]+/)!=-1){var t=-1;for(j=0;j<12;++j){if(Calendar._MN[j].substr(0,a[i].length).toLowerCase()==a[i].toLowerCase()){t=j;break;}}if(t!=-1){if(m!=-1){d=m+1;}m=t;}}else if(parseInt(a[i],10)<=12&&m==-1){m=a[i]-1;}else if(parseInt(a[i],10)>31&&y==0){y=parseInt(a[i],10);(y<100)&&(y+=(y>29)?1900:2000);}else if(d==0){d=a[i];}}if(y==0){var today=new Date();y=today.getFullYear();}if(m!=-1&&d!=0){this.setDate(new Date(y,m,d,hr,min,0));}};Calendar.prototype.hideShowCovered=function(){var self=this;Calendar.continuation_for_the_fucking_khtml_browser=function(){function getVisib(obj){var value=obj.style.visibility;if(!value){if(document.defaultView&&typeof(document.defaultView.getComputedStyle)=="function"){if(!Calendar.is_khtml)value=document.defaultView. getComputedStyle(obj,"").getPropertyValue("visibility");else value='';}else if(obj.currentStyle){value=obj.currentStyle.visibility;}else value='';}return value;};var tags=new Array("applet","iframe","select");var el=self.element;var p=Calendar.getAbsolutePos(el);var EX1=p.x;var EX2=el.offsetWidth+EX1;var EY1=p.y;var EY2=el.offsetHeight+EY1;for(var k=tags.length;k>0;){var ar=document.getElementsByTagName(tags[--k]);var cc=null;for(var i=ar.length;i>0;){cc=ar[--i];p=Calendar.getAbsolutePos(cc);var CX1=p.x;var CX2=cc.offsetWidth+CX1;var CY1=p.y;var CY2=cc.offsetHeight+CY1;if(self.hidden||(CX1>EX2)||(CX2<EX1)||(CY1>EY2)||(CY2<EY1)){if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility=cc.__msh_save_visibility;}else{if(!cc.__msh_save_visibility){cc.__msh_save_visibility=getVisib(cc);}cc.style.visibility="hidden";}}}};if(Calendar.is_khtml)setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()",10);else Calendar.continuation_for_the_fucking_khtml_browser();};Calendar.prototype._displayWeekdays=function(){var MON=this.mondayFirst?0:1;var SUN=this.mondayFirst?6:0;var SAT=this.mondayFirst?5:6;var cell=this.firstdayname;for(var i=0;i<7;++i){cell.className="day name";if(!i){cell.ttip=this.mondayFirst?Calendar._TT["SUN_FIRST"]:Calendar._TT["MON_FIRST"];cell.navtype=100;cell.calendar=this;Calendar._add_evs(cell);}if(i==SUN||i==SAT){Calendar.addClass(cell,"weekend");}cell.firstChild.data=Calendar._SDN[i+1-MON];cell=cell.nextSibling;}};Calendar.prototype._hideCombos=function(){this.monthsCombo.style.display="none";this.yearsCombo.style.display="none";};Calendar.prototype._dragStart=function(ev){if(this.dragging){return;}this.dragging=true;var posX;var posY;if(Calendar.is_ie){posY=window.event.clientY+document.body.scrollTop;posX=window.event.clientX+document.body.scrollLeft;}else{posY=ev.clientY+window.scrollY;posX=ev.clientX+window.scrollX;}var st=this.element.style;this.xOffs=posX-parseInt(st.left);this.yOffs=posY-parseInt(st.top);with(Calendar){addEvent(document,"mousemove",calDragIt);addEvent(document,"mouseover",stopEvent);addEvent(document,"mouseup",calDragEnd);}};Date._MD=new Array(31,28,31,30,31,30,31,31,30,31,30,31);Date.SECOND=1000;Date.MINUTE=60*Date.SECOND;Date.HOUR=60*Date.MINUTE;Date.DAY=24*Date.HOUR;Date.WEEK=7*Date.DAY;Date.prototype.getMonthDays=function(month){var year=this.getFullYear();if(typeof month=="undefined"){month=this.getMonth();}if(((0==(year%4))&&((0!=(year%100))||(0==(year%400))))&&month==1){return 29;}else{return Date._MD[month];}};Date.prototype.getDayOfYear=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,1,0,0,0);var time=now-then;return Math.floor(time/Date.DAY);};Date.prototype.getWeekNumber=function(){var now=new Date(this.getFullYear(),this.getMonth(),this.getDate(),0,0,0);var then=new Date(this.getFullYear(),0,1,0,0,0);var time=now-then;var day=then.getDay();if(day==0)day=7;(day>4)&&(day-=4)||(day+=3);return Math.round(((time/Date.DAY)+day)/7);};Date.prototype.equalsTo=function(date){return((this.getFullYear()==date.getFullYear())&&(this.getMonth()==date.getMonth())&&(this.getDate()==date.getDate())&&(this.getHours()==date.getHours())&&(this.getMinutes()==date.getMinutes()));};Date.prototype.print=function(str){var m=this.getMonth();var d=this.getDate();var y=this.getFullYear();var wn=this.getWeekNumber();var w=this.getDay();var s={};var hr=this.getHours();var pm=(hr>=12);var ir=(pm)?(hr-12):hr;var dy=this.getDayOfYear();if(ir==0)ir=12;var min=this.getMinutes();var sec=this.getSeconds();s["%a"]=Calendar._SDN[w];s["%A"]=Calendar._DN[w];s["%b"]=Calendar._SMN[m];s["%B"]=Calendar._MN[m];s["%C"]=1+Math.floor(y/100);s["%d"]=(d<10)?("0"+d):d;s["%e"]=d;s["%H"]=(hr<10)?("0"+hr):hr;s["%I"]=(ir<10)?("0"+ir):ir;s["%j"]=(dy<100)?((dy<10)?("00"+dy):("0"+dy)):dy;s["%k"]=hr;s["%l"]=ir;s["%m"]=(m<9)?("0"+(1+m)):(1+m);s["%M"]=(min<10)?("0"+min):min;s["%n"]="\n";s["%p"]=pm?"PM":"AM";s["%P"]=pm?"pm":"am";s["%s"]=Math.floor(this.getTime()/1000);s["%S"]=(sec<10)?("0"+sec):sec;s["%t"]="\t";s["%U"]=s["%W"]=s["%V"]=(wn<10)?("0"+wn):wn;s["%u"]=w+1;s["%w"]=w;s["%y"]=(''+y).substr(2,2);s["%Y"]=y;s["%%"]="%";var re=Date._msh_formatRegexp;if(typeof re=="undefined"){var tmp="";for(var i in s)tmp+=tmp?("|"+i):i;Date._msh_formatRegexp=re=new RegExp("("+tmp+")",'g');}return str.replace(re,function(match,par){return s[par];});};window.calendar=null;
\ No newline at end of file
--- /dev/null
+<%
+ my($title, $menubar) = @_;
+ my $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
+%>
+ <HTML>
+ <HEAD>
+ <TITLE>
+ <%= $title %>
+ </TITLE>
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8"<%= $etc %>>
+ <FONT SIZE=7>
+ <%= $title %>
+ </FONT>
+ <BR><BR>
+ <%= $menubar ? "$menubar<BR><BR>" : '' %>
--- /dev/null
+<%
+ my($item, $url, @html);
+ while (@_) {
+ ($item, $url) = splice(@_,0,2);
+ push @html, qq!<A HREF="$url">$item</A>!;
+ }
+%>
+<%= join(' | ', @html) %>
--- /dev/null
+<%
+
+ my %opt = @_;
+
+ my $pager = '';
+ if ( $opt{'total'} != $opt{'num_rows'} && $opt{'maxrecords'} ) {
+ unless ( $opt{'offset'} == 0 ) {
+ $cgi->param('offset', $opt{'offset'} - $opt{'maxrecords'});
+%>
+
+ <A HREF="<%= $cgi->self_url %>"><B><FONT SIZE="+1">Previous</FONT></B></A>
+
+<%
+ }
+ my $page = 0;
+ for ( my $poff = 0; $poff < $opt{'total'}; $poff += $opt{'maxrecords'} ) {
+ $page++;
+ if ( $opt{'offset'} == $poff ) {
+%>
+
+ <FONT SIZE="+2"><%= $page %></FONT>
+
+<%
+ } else {
+ $cgi->param('offset', $poff);
+%>
+
+ <A HREF="<%= $cgi->self_url %>">$page</A>
+
+<%
+ }
+ }
+ unless ( $opt{'offset'} + $opt{'maxrecords'} > $opt{'total'} ) {
+ $cgi->param('offset', $opt{'offset'} + $opt{'maxrecords'});
+%>
+
+ <A HREF="<%= $cgi->self_url %>"><B><FONT SIZE="+1">Next</FONT></B></A>
+
+<%
+ }
+ }
+%>
--- /dev/null
+<%
+ my $color = shift;
+ if ( $color ) {
+%>
+ <TABLE BGCOLOR="<%= $color %>" BORDER=1 WIDTH="100%" CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">
+<% } else { %>
+ <TABLE BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">
+<% } %>
my $refunded = $refunded_sth->fetchrow_arrayref->[0] || 0;
#horrible local kludge that doesn't even really work right
- my $expenses_sql = "SELECT SUM(cust_bill_pay.amount) FROM cust_bill_pay, cust_bill WHERE cust_bill_pay.invnum = cust_bill.invnum AND cust_bill_pay._date >= $speriod AND cust_bill_pay._date < $eperiod AND 0 < ( select count(*) from cust_bill_pkg, cust_pkg, part_pkg WHERE cust_bill.invnum = cust_bill_pkg.invnum AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%' )";
+ my $expenses_sql2 = "SELECT SUM(cust_bill_pay.amount) FROM cust_bill_pay, cust_bill WHERE cust_bill_pay.invnum = cust_bill.invnum AND cust_bill_pay._date >= $speriod AND cust_bill_pay._date < $eperiod AND 0 < ( select count(*) from cust_bill_pkg, cust_pkg, part_pkg WHERE cust_bill.invnum = cust_bill_pkg.invnum AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%' )";
-# my $expenses_sql = "SELECT SUM(cust_bill_pay.amount) FROM cust_bill_pay, cust_bill_pkg, cust_bill, cust_pkg, part_pkg WHERE cust_bill_pay.invnum = cust_bill.invnum AND cust_bill.invnum = cust_bill_pkg.invnum AND cust_bill_pay._date >= $speriod AND cust_bill_pay._date < $eperiod AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%'";
- my $expenses_sth = dbh->prepare($expenses_sql) or die dbh->errstr;
- $expenses_sth->execute or die $expenses_sth->errstr;
- my $expenses = $expenses_sth->fetchrow_arrayref->[0] || 0;
+# my $expenses_sql2 = "SELECT SUM(cust_bill_pay.amount) FROM cust_bill_pay, cust_bill_pkg, cust_bill, cust_pkg, part_pkg WHERE cust_bill_pay.invnum = cust_bill.invnum AND cust_bill.invnum = cust_bill_pkg.invnum AND cust_bill_pay._date >= $speriod AND cust_bill_pay._date < $eperiod AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%'";
+ my $expenses_sth2 = dbh->prepare($expenses_sql2) or die dbh->errstr;
+ $expenses_sth2->execute or die $expenses_sth2->errstr;
+ my $expenses2 = $expenses_sth2->fetchrow_arrayref->[0] || 0;
- push @{$data{cash}}, $paid-$refunded-$expenses;
+ push @{$data{cash}}, $paid-$refunded-$expenses2;
}
#$chart->cgi_png(\@data);
http_header('Content-Type' => 'image/png' );
-$Response->{ContentType} = 'image/png';
+#$Response->{ContentType} = 'image/png';
$chart->_set_colors();
-<!-- mason kludge %>
+<!-- mason kludge -->
<%
#my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
Cashflow (payments - refunds)<BR>
<BR>
From <SELECT NAME="smonth">
-<% my @m = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
- foreach my $m ( 1..12 ) { %>
-<OPTION VALUE="<%= $m %>"<%= $m == $smonth ? ' SELECTED' : '' %>><%= $m[$m-1] %>
+<% my @mon = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); %>
+<% foreach my $mon ( 1..12 ) { %>
+<OPTION VALUE="<%= $mon %>"<%= $mon == $smonth ? ' SELECTED' : '' %>><%= $mon[$mon-1] %>
<% } %>
</SELECT>
<SELECT NAME="syear">
<% } %>
</SELECT>
to <SELECT NAME="emonth">
-<% foreach my $m ( 1..12 ) { %>
-<OPTION VALUE="<%= $m %>"<%= $m == $emonth ? ' SELECTED' : '' %>><%= $m[$m-1] %>
+<% foreach my $mon ( 1..12 ) { %>
+<OPTION VALUE="<%= $mon %>"<%= $mon == $emonth ? ' SELECTED' : '' %>><%= $mon[$mon-1] %>
<% } %>
</SELECT>
<SELECT NAME="eyear">
</td><td>
<font color="#ff0000" size=7>freeside main menu</font>
</td><td align=right valign=bottom>
- version 1.5.0pre2
- <BR><A HREF="http://www.sisd.com/freeside">Freeside home page</A>
+ version %%%VERSION%%%
+ <BR><A HREF="http://www.sisd.com/freeside">Freeside home page</A>
<BR><A HREF="docs/">Documentation</A>
</td></tr>
</table>
<FORM ACTION="search/cust_main.cgi" METHOD="POST"><INPUT TYPE="hidden" NAME="phone_on" VALUE="1">Phone # <INPUT TYPE="text" NAME="phone_text"><INPUT TYPE="submit" VALUE="Search"></FORM>
<BR><FORM ACTION="search/svc_acct.cgi" METHOD="POST">Username <INPUT TYPE="text" NAME="username"><SELECT NAME="username_type"><OPTION VALUE="All">(all)</OPTION><OPTION>Fuzzy</OPTION><OPTION>Substring</OPTION><OPTION SELECTED>Exact</OPTION></SELECT><INPUT TYPE="submit" VALUE="Search"> or <A HREF="search/svc_acct.cgi?username">all accounts by username</A> or <A HREF="search/svc_acct.cgi?uid">uid</A></FORM>
<BR><FORM ACTION="search/svc_domain.cgi" METHOD="POST">Domain <INPUT TYPE="text" NAME="domain"><INPUT TYPE="submit" VALUE="Search"> or <A HREF="search/svc_domain.cgi?domain">all domains</A></FORM>
-<!-- <LI><A HREF="search/svc_forward.html">mail forwards (by ?)</A>-->
+ <BR><A HREF="search/svc_forward.cgi?svcnum">all mail forwards by svcnum</A><BR>
+
<BR>
</TD></TR>
</TABLE>
<BR><FORM ACTION="search/cust_main.cgi" METHOD="POST">Credit card # <INPUT TYPE="hidden" NAME="card_on" VALUE="1"><INPUT TYPE="text" NAME="card"><INPUT TYPE="submit" VALUE="Search"></FORM>
<FORM ACTION="search/cust_bill.cgi" METHOD="POST">Invoice # <INPUT TYPE="text" NAME="invnum" SIZE="8"><INPUT TYPE="submit" VALUE="Search"></FORM>
<FORM ACTION="search/cust_pay.cgi" METHOD="POST">Check # <INPUT TYPE="text" NAME="payinfo" SIZE="8"><INPUT TYPE="hidden" NAME="payby" VALUE="BILL"><INPUT TYPE="submit" VALUE="Search"></FORM>
- <BR><A HREF="browse/cust_pay_batch.cgi">View pending credit card batch</A> <BR><BR><A HREF="search/cust_pkg.html">Packages (by next bill date range)</A>
+ <BR><A HREF="browse/cust_pay_batch.cgi">View pending credit card batch</A> <BR><BR><A HREF="search/cust_pkg_report.cgi">Packages (by next bill date range)</A>
<BR><BR>Invoice reports
<UL>
<LI><a href="search/cust_bill_event.html">Invoice event errors (failed credit cards)</a>
<LI>all invoices (<A HREF="search/cust_bill.cgi?invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?date">by date</A>) (<A HREF="search/cust_bill.cgi?custnum">by customer number</A>)
</UL>
<A HREF="search/report_cust_pay.html">Payment report (by type and/or date range)</A>
- <BR><BR>Financial reports
+ <BR><BR><A HREF="search/report_receivables.cgi">Accounts Receivable Aging Summary</A>
+ <BR><BR><A HREF="search/report_prepaid_income.html">Prepaid Income (Unearned Revenue) Report</A>
+ <BR><BR>(old) Financial reports (being rewritten)
<UL>
- <LI> <A HREF="search/report_receivables.cgi">current receivables</A>
<LI> <A HREF="search/report_tax.html">tax reports</A>
<LI> <A HREF="search/report_cc.html">credit card receipts</A>
<LI> <A HREF="search/report_credit.html">credit memos</A>
Packages
<UL>
<LI><A HREF="search/cust_pkg.cgi?pkgnum">all packages (by package number)</A>
- <LI><A HREF="search/cust_pkg.cgi?SUSP_pkgnum">suspended packages (by package number)</A>
+ <LI><A HREF="search/cust_pkg.cgi?magic=suspended">suspended packages (by package number)</A>
<LI><A HREF="search/cust_pkg.cgi?APKG_pkgnum">packages with unconfigured services (by package number)</A>
- <LI><A HREF="search/cust_pkg.html">packages (by next bill date range)</A>
- </UL>
- <A HREF="browse/part_pkg.cgi?active=1">Package definitions (by number of active packages)</A>
- <BR><BR>Invoices
- <UL>
- <LI><a href="search/cust_bill_event.html">Invoice event errors (failed credit cards)</a>
- <LI>open invoices (<A HREF="search/cust_bill.cgi?OPEN_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN_custnum">by customer number</A>)
- <LI>30 day open invoices (<A HREF="search/cust_bill.cgi?OPEN30_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN30_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN30_custnum">by customer number</A>)
- <LI>60 day open invoices (<A HREF="search/cust_bill.cgi?OPEN60_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN60_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN60_custnum">by customer number</A>)
- <LI>90 day open invoices (<A HREF="search/cust_bill.cgi?OPEN90_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN90_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN90_custnum">by customer number</A>)
- <LI>120 day open invoices (<A HREF="search/cust_bill.cgi?OPEN120_invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?OPEN120_date">by date</A>) (<A HREF="search/cust_bill.cgi?OPEN120_custnum">by customer number</A>)
- <LI>all invoices (<A HREF="search/cust_bill.cgi?invnum">by invoice number</A>) (<A HREF="search/cust_bill.cgi?date">by date</A>) (<A HREF="search/cust_bill.cgi?custnum">by customer number</A>)
+ <LI><A HREF="search/cust_pkg_report.cgi">packages (by next bill date range)</A>
</UL>
- <A HREF="search/report_cust_pay.html">Payment Report (by type and/or date range)</A>
- <BR><BR>Financial reports
- <UL>
- <LI> <A HREF="search/report_receivables.cgi">current receivables</A>
- <LI> <A HREF="search/report_tax.html">tax reports</A>
- <LI> <A HREF="search/report_cc.html">credit card receipts</A>
- <LI> <A HREF="search/report_credit.html">credit memos</A>
- </UL>
+ <A HREF="browse/part_pkg.cgi?active=1">Package definitions (by number of active packages)</A><BR><BR>
+ <A HREF="browse/part_svc.cgi?active=1">Service definitions (by number of active services)</A><BR><BR>
Customers
<UL>
<LI><A HREF="search/cust_main-otaker.cgi">Search customers by order-taker</A>
</UL>
- <FORM ACTION="search/sql.cgi" METHOD="POST">SQL query: <TT>SELECT </TT><INPUT TYPE="text" NAME="sql" SIZE=32><INPUT TYPE="submit" VALUE="Query"></FORM>
+ <FORM ACTION="search/sql.html" METHOD="POST">SQL query: <TT>SELECT </TT><INPUT TYPE="text" NAME="sql" SIZE=32><INPUT TYPE="submit" VALUE="Query"></FORM>
<BR>
</TD></TR>
<BR><A HREF="browse/queue.cgi">View pending job queue</A>
<BR><A HREF="misc/cust_main-import.cgi">Batch import customers from CSV file</A>
<BR><A HREF="misc/cust_main-import_charges.cgi">Batch import charges from CSV file</A>
+ <BR><A HREF="misc/dump.cgi">Download database dump</A>
<BR><BR><CENTER><HR WIDTH="94%" NOSHADE></CENTER><BR>
<A NAME="config" HREF="config/config-view.cgi">Configuration</a><!-- - <font size="+2" color="#ff0000">start here</font> -->
<BR><BR><A NAME="admin">Administration</a>
<LI><A HREF="browse/cust_main_county.cgi">View/Edit locales and tax rates</A>
- Change tax rates, or break down a country into states, or a state
into counties and assign different tax rates to each.
- <LI><A HREF="browse/svc_acct_pop.cgi">View/Edit Access Numbers</A>
+ <LI><A HREF="browse/svc_acct_pop.cgi">View/Edit access numbers</A>
- Points of Presence
<LI><A HREF="browse/part_bill_event.cgi">View/Edit invoice events</A> - Actions for overdue invoices
<LI><A HREF="browse/msgcat.cgi">View/Edit message catalog</A> - Change error messages and other customizable labels.
my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
-my $error = $cust_main->cancel;
-eidiot($error) if $error;
+my @errors = $cust_main->cancel;
+eidiot(join(' / ', @errors)) if scalar(@errors);
#print $cgi->redirect($p. "view/cust_main.cgi?". $cust_main->custnum);
print $cgi->redirect($p);
--- /dev/null
+<%
+
+#untaint crednum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal crednum";
+my $crednum = $1;
+
+my $cust_credit = qsearchs('cust_credit',{'crednum'=>$crednum});
+my $custnum = $cust_credit->custnum;
+
+my $error = $cust_credit->delete;
+eidiot($error) if $error;
+
+print $cgi->redirect($p. "view/cust_main.cgi?". $custnum);
+
+%>
--- /dev/null
+<%
+
+#http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes
+http_header('Content-Type' => 'text/plain' );
+
+for my $cust_pay_batch ( sort { $a->paybatchnum <=> $b->paybatchnum }
+ qsearch('cust_pay_batch', {} )
+) {
+
+$cust_pay_batch->exp =~ /^\d{2}(\d{2})[\/\-](\d+)[\/\-]\d+$/;
+my( $mon, $y ) = ( $2, $1 );
+$mon = "0$mon" if $mon < 10;
+my $exp = "$mon$y";
+
+%>,,,,<%= $cust_pay_batch->cardnum %>,<%= $exp %>,<%= $cust_pay_batch->amount %>,<%= $cust_pay_batch->paybatchnum %>
+<% } %>
--- /dev/null
+<%
+ if ( driver_name =~ /^Pg$/ ) {
+ my $dbname = (split(':', datasrc))[2];
+ if ( $dbname =~ /[;=]/ ) {
+ my %elements = map { /^(\w+)=(.*)$/; $1=>$2 } split(';', $dbname);
+ $dbname = $elements{'dbname'};
+ }
+ open(DUMP,"pg_dump $dbname |");
+ } else {
+ eidiot "don't (yet) know how to dump ". driver_name. " databases\n";
+ }
+
+ http_header('Content-Type' => 'text/plain' );
+
+ while (<DUMP>) {
+ print $_;
+ }
+ close DUMP;
+%>
--- /dev/null
+<%
+
+my $conf = new FS::Conf;
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d*)$/;
+my $invnum = $1;
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Can't find invoice!\n" unless $cust_bill;
+
+my $error = send_email(
+ 'from' => $conf->config('invoice_from'),
+ 'to' => [ grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ],
+ 'subject' => 'Invoice',
+ 'body' => [ $cust_bill->print_text ],
+);
+eidiot($error) if $error;
+
+my $custnum = $cust_bill->getfield('custnum');
+print $cgi->redirect("${p}view/cust_main.cgi?$custnum");
+
+%>
die "Can't find invoice!\n" unless $cust_bill;
open(LPR,"|$lpr") or die "Can't open $lpr: $!";
- print LPR $cust_bill->print_text; #( date )
+
+ if ( $conf->exists('invoice_latex') ) {
+ print LPR $cust_bill->print_ps; #( date )
+ } else {
+ print LPR $cust_bill->print_text; #( date )
+ }
+
close LPR
or die $! ? "Error closing $lpr: $!"
: "Exit status $? from $lpr";
my $custnum = $cust_bill->getfield('custnum');
-print $cgi->redirect(popurl(2). "view/cust_main.cgi?$custnum#history");
+print $cgi->redirect("${p}view/cust_main.cgi?$custnum");
%>
#hashmaker widget
sub hashmaker {
my($name, $from, $to, $labelfrom, $labelto) = @_;
- $fromsize = scalar(@$from);
- $tosize = scalar(@$to);
+ my $fromsize = scalar(@$from);
+ my $tosize = scalar(@$to);
"<TABLE><TR><TH>$labelfrom</TH><TH>$labelto</TH></TR><TR><TD>".
qq!<SELECT NAME="${name}_from" SIZE=$fromsize>\n!.
join("\n", map { qq!<OPTION VALUE="$_">$_</OPTION>! } sort { $a cmp $b } @$from ).
--- /dev/null
+<%
+
+ my $fh = $cgi->upload('batch_results');
+ my $filename = $cgi->param('batch_results');
+ $filename =~ /^.*[\/\\]([^\/\\]+)$/ or die;
+ my $paybatch = $1;
+
+ my $error = defined($fh)
+ ? FS::cust_pay_batch::import_results( {
+ 'filehandle' => $fh,
+ 'format' => $cgi->param('format'),
+ 'paybatch' => $paybatch,
+ } )
+ : 'No file';
+
+ if ( $error ) {
+ %>
+ <!-- mason kludge -->
+ <%
+ eidiot($error);
+# $cgi->param('error', $error);
+# print $cgi->redirect( "${p}cust_main-import.cgi
+ } else {
+ %>
+ <!-- mason kludge -->
+ <%= header('Batch results upload sucessful') %> <%
+ }
+%>
+
my $beginning = str2time($1) || 0;
$cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/;
-my $ending = str2time($1) + 86400;
+my $ending = str2time($1) + 86399;
my @cust_bill_event =
sort { $a->_date <=> $b->_date }
Quick payment entry
</FONT>
<BR><BR>
+ <A HREF="../">Main Menu</A><BR><BR>
<FORM ACTION="cust_main.cgi" METHOD="post">
<INPUT TYPE="hidden" NAME="quickpay" VALUE="yes">
<INPUT TYPE="checkbox" NAME="last_on" CHECKED> Search for <B>last name</B>:
<INPUT TYPE="text" NAME="last_text">
using search method: <SELECT NAME="last_type">
- <OPTION SELECTED>All
+ <OPTION>All
<OPTION>Fuzzy
<OPTION>Substring
- <OPTION>Exact
+ <OPTION SELECTED>Exact
</SELECT>
<P><INPUT TYPE="checkbox" NAME="company_on" CHECKED> Search for <B>company</B>:
<INPUT TYPE="text" NAME="company_text">
using search methods: <SELECT NAME="company_type">
- <OPTION SELECTED>All
+ <OPTION>All
<OPTION>Fuzzy
<OPTION>Substring
- <OPTION>Exact
+ <OPTION SELECTED>Exact
</SELECT>
- <P><INPUT TYPE="submit" VALUE="Search"> Note: Fuzzy searching can take a while. Please be patient.
+ <P><INPUT TYPE="submit" VALUE="Search">
</FORM>
my(@cust_main, $sortby, $orderby);
if ( $cgi->param('browse')
|| $cgi->param('otaker_on')
+ || $cgi->param('agentnum_on')
) {
my %search = ();
if ( $cgi->param('otaker_on') ) {
$cgi->param('otaker') =~ /^(\w{1,32})$/ or eidiot "Illegal otaker\n";
$search{otaker} = $1;
+ } elsif ( $cgi->param('agentnum_on') ) {
+ $cgi->param('agentnum') =~ /^(\d+)$/ or eidiot "Illegal agentnum\n";
+ $search{agentnum} = $1;
} else {
die "unknown query...";
}
AND (temp1_$$.count > 0
OR temp2_$$.count = 0 )
";
+
} else {
$ncancelled = "
0 < ( SELECT COUNT(*) FROM cust_pkg
WHERE cust_pkg.custnum = cust_main.custnum
)
";
- }
-
+ }
+ }
+
+ my $cancelled = '';
+ if ( $cgi->param('cancelled') ) {
+ $cancelled = "
+ 0 = ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ AND ( cust_pkg.cancel IS NULL
+ OR cust_pkg.cancel = 0
+ )
+ )
+ AND 0 < ( SELECT COUNT(*) FROM cust_pkg
+ WHERE cust_pkg.custnum = cust_main.custnum
+ )
+ ";
}
#EWWWWWW
my $qual = join(' AND ',
map { "$_ = ". dbh->quote($search{$_}) } keys %search );
- if ( $ncancelled ) {
+ if ( $cancelled ) {
+ $qual .= ' AND ' if $qual;
+ $qual .= $cancelled;
+ } elsif ( $ncancelled ) {
$qual .= ' AND ' if $qual;
$qual .= $ncancelled;
}
$total = $sth->fetchrow_arrayref->[0];
- if ( $ncancelled ) {
+ my $rqual = $cancelled || $ncancelled;
+ if ( $rqual ) {
if ( %search ) {
- $ncancelled = " AND $ncancelled";
+ $rqual = " AND $rqual";
} else {
- $ncancelled = " WHERE $ncancelled";
+ $rqual = " WHERE $rqual";
}
}
my @just_cust_main;
if ( driver_name eq 'mysql' ) {
@just_cust_main = qsearch('cust_main', \%search, 'cust_main.*',
- ",temp1_$$,temp2_$$ $ncancelled $orderby $limit");
+ ",temp1_$$,temp2_$$ $rqual $orderby $limit");
} else {
@just_cust_main = qsearch('cust_main', \%search, '',
- "$ncancelled $orderby $limit" );
+ "$rqual $orderby $limit" );
}
if ( driver_name eq 'mysql' ) {
my $sql = "DROP TABLE temp1_$$,temp2_$$;";
}
@cust_main = grep { $_->ncancelled_pkgs || ! $_->all_pkgs } @cust_main
- if $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
- || ( $conf->exists('hidecancelledcustomers')
- && ! $cgi->param('showcancelledcustomers') );
+ if ! $cgi->param('cancelled')
+ && (
+ $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
+ || ( $conf->exists('hidecancelledcustomers')
+ && ! $cgi->param('showcancelledcustomers') )
+ );
my %saw = ();
@cust_main = grep { !$saw{$_->custnum}++ } @cust_main;
}
}
#end pager
-
- if ( $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
- || ( $conf->exists('hidecancelledcustomers')
- && ! $cgi->param('showcancelledcustomers')
- )
- ) {
- $cgi->param('showcancelledcustomers', 1);
- $cgi->param('offset', 0);
- print qq!( <a href="!. $cgi->self_url. qq!">show cancelled customers</a> )!;
- } else {
- $cgi->param('showcancelledcustomers', 0);
- $cgi->param('offset', 0);
- print qq!( <a href="!. $cgi->self_url. qq!">hide cancelled customers</a> )!;
+
+ unless ( $cgi->param('cancelled') ) {
+ if ( $cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
+ || ( $conf->exists('hidecancelledcustomers')
+ && ! $cgi->param('showcancelledcustomers')
+ )
+ ) {
+ $cgi->param('showcancelledcustomers', 1);
+ $cgi->param('offset', 0);
+ print qq!( <a href="!. $cgi->self_url. qq!">show!;
+ } else {
+ $cgi->param('showcancelledcustomers', 0);
+ $cgi->param('offset', 0);
+ print qq!( <a href="!. $cgi->self_url. qq!">hide!;
+ }
+ print ' cancelled customers</a> )';
}
if ( $cgi->param('referral_custnum') ) {
$cgi->param('referral_custnum') =~ /^(\d+)$/
if ( $cgi->param('magic') && $cgi->param('magic') eq '_date' ) {
my %search;
+ my @search;
+
if ( $cgi->param('payby') ) {
- $cgi->param('payby') =~ /^(CARD|CHEK|BILL)$/
+ $cgi->param('payby') =~ /^(CARD|CHEK|BILL)(-(VisaMC|Amex|Discover))?$/
or die "illegal payby ". $cgi->param('payby');
$search{'payby'} = $1;
+ if ( $3 ) {
+ if ( $3 eq 'VisaMC' ) {
+ #avoid posix regexes for portability
+ push @search, " ( substring(payinfo from 1 for 1) = '4' ".
+ " OR substring(payinfo from 1 for 2) = '51' ".
+ " OR substring(payinfo from 1 for 2) = '52' ".
+ " OR substring(payinfo from 1 for 2) = '53' ".
+ " OR substring(payinfo from 1 for 2) = '54' ".
+ " OR substring(payinfo from 1 for 2) = '54' ".
+ " OR substring(payinfo from 1 for 2) = '55' ".
+ " ) ";
+ } elsif ( $3 eq 'Amex' ) {
+ push @search, " ( substring(payinfo from 1 for 2 ) = '34' ".
+ " OR substring(payinfo from 1 for 2 ) = '37' ".
+ " ) ";
+ } elsif ( $3 eq 'Discover' ) {
+ push @search, " substring(payinfo from 1 for 4 ) = '6011' ";
+ } else {
+ die "unknown card type $3";
+ }
+ }
}
#false laziness with cust_pkg.cgi
- my $range = '';
if ( $cgi->param('beginning')
&& $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/ ) {
my $beginning = str2time($1);
- $range = " WHERE _date >= $beginning ";
+ push @search, "_date >= $beginning ";
}
if ( $cgi->param('ending')
&& $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/ ) {
- my $ending = str2time($1) + 86400;
- $range .= ( $range ? ' AND ' : ' WHERE ' ). " _date <= $ending ";
+ my $ending = str2time($1) + 86399;
+ push @search, " _date <= $ending ";
+ }
+ my $search;
+ if ( @search ) {
+ $search = ( scalar(keys %search) ? ' AND ' : ' WHERE ' ).
+ join(' AND ', @search);
}
- $range =~ s/^\s*WHERE/ AND/ if scalar(keys %search) ;
- @cust_pay = qsearch('cust_pay', \%search, '', $range );
+ @cust_pay = qsearch('cust_pay', \%search, '', $search );
$sortby = \*date_sort;
}
if ( $cgi->param('ending')
&& $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/ ) {
- my $ending = str2time($1) + 86400;
+ my $ending = str2time($1) + 86399;
$range .= ( $range ? ' AND ' : ' WHERE ' ). " bill <= $ending ";
}
+ $range .= ( $range ? 'AND ' : ' WHERE ' ). '( cancel IS NULL OR cancel = 0 )';
+
+ if ( $cgi->param('agentnum') =~ /^(\d+)$/ and $1 ) {
+ $range .= ( $range ? 'AND ' : ' WHERE ' ).
+ "$1 = ( SELECT agentnum FROM cust_main".
+ " WHERE cust_main.custnum = cust_pkg.custnum )";
+ }
+
#false laziness with below
my $statement = "SELECT COUNT(*) FROM cust_pkg $range";
warn $statement;
} else {
my $qual = '';
- if ( $cgi->param('magic') && $cgi->param('magic') eq 'active' ) {
+ if ( $cgi->param('magic') &&
+ $cgi->param('magic') =~ /^(active|suspended|canceled)$/
+ ) {
- $qual = 'WHERE ( susp IS NULL OR susp = 0 )'.
- ' AND ( cancel IS NULL OR cancel = 0)';
+ if ( $cgi->param('magic') eq 'active' ) {
+ $qual = 'WHERE ( susp IS NULL OR susp = 0 )'.
+ ' AND ( cancel IS NULL OR cancel = 0)';
+ } elsif ( $cgi->param('magic') eq 'suspended' ) {
+ $qual = 'WHERE susp IS NOT NULL AND susp != 0'.
+ ' AND ( cancel IS NULL OR cancel = 0)';
+ } elsif ( $cgi->param('magic') eq 'canceled' ) {
+ $qual = 'WHERE cancel IS NOT NULL AND cancel != 0';
+ } else {
+ die "guru meditation #420";
+ }
$sortby = \*pkgnum_sort;
$sortby=\*pkgnum_sort;
- } elsif ( $query eq 'SUSP_pkgnum' ) {
-
- $sortby=\*pkgnum_sort;
-
- $qual = 'WHERE susp IS NOT NULL AND susp != 0';
-
} elsif ( $query eq 'APKG_pkgnum' ) {
$sortby=\*pkgnum_sort;
+++ /dev/null
-<HTML>
- <HEAD>
- <TITLE>Packages</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Packages</H1>
- </CENTER>
- <HR>
- <FORM ACTION="cust_pkg.cgi" METHOD="post">
- <INPUT TYPE="hidden" NAME="magic" VALUE="bill">
- Return <B>packages</B> with next bill date:
- from <INPUT TYPE="text" NAME="beginning"> <i>m/d/y</i>
- to <INPUT TYPE="text" NAME="ending"> <i>m/d/y</i>
-
- <P><INPUT TYPE="submit" VALUE="Get Report">
-
- </FORM>
-
- <HR>
-
- </BODY>
-</HTML>
-
--- /dev/null
+<HTML>
+ <HEAD>
+ <TITLE>Packages</TITLE>
+ <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ <H1>Packages</H1>
+ <FORM ACTION="cust_pkg.cgi" METHOD="post">
+ <INPUT TYPE="hidden" NAME="magic" VALUE="bill">
+ Return packages with next bill date:<BR><BR>
+ <TABLE>
+ <TR>
+ <TD ALIGN="right">From: </TD>
+ <TD><INPUT TYPE="text" NAME="beginning" ID="beginning_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="beginning_button" STYLE="cursor: pointer" TITLE="Select date"><BR><I>m/d/y</I></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "beginning_text",
+ ifFormat: "%m/%d/%Y",
+ button: "beginning_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ <TR>
+ <TD ALIGN="right">To: </TD>
+ <TD><INPUT TYPE="text" NAME="ending" ID="ending_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="ending_button" STYLE="cursor: pointer" TITLE="Select date"><BR><I>m/d/y</I></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "ending_text",
+ ifFormat: "%m/%d/%Y",
+ button: "ending_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+<% my %agent_search = dbdef->table('agent')->column('disabled')
+ ? ( 'disabled' => '' ) : ();
+ my @agents = qsearch( 'agent', \%agent_search );
+ if ( scalar(@agents) == 1 ) {
+%>
+ <INPUT TYPE="hidden" NAME="agentnum" VALUE="<%= $agents[0]->agentnum %>">
+<% } else { %>
+
+ <TR>
+ <TD ALIGN="right">Agent: </TD>
+ <TD><SELECT NAME="agentnum"><OPTION VALUE="">(all)
+ <% foreach my $agent ( sort { $a->agent cmp $b->agent; } @agents) { %>
+ <OPTION VALUE="<%= $agent->agentnum %>"><%= $agent->agent %>
+ <% } %>
+ </TD>
+ </TR>
+<% } %>
+ </TABLE>
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
+
+ </FORM>
+
+ </BODY>
+</HTML>
+
--- /dev/null
+<%
+
+ my %opt = @_;
+ unless (exists($opt{'count_query'}) && length($opt{'count_query'})) {
+ ( $opt{'count_query'} = $opt{'query'} ) =~
+ s/^\s*SELECT\s*(.*)\s+FROM\s/SELECT COUNT(*) FROM /i;
+ }
+
+ my $conf = new FS::Conf;
+ my $maxrecords = $conf->config('maxsearchrecordsperpage');
+
+ my $limit = $maxrecords ? "LIMIT $maxrecords" : '';
+
+ my $offset = $cgi->param('offset') || 0;
+ $limit .= " OFFSET $offset" if $offset;
+
+ my $count_sth = dbh->prepare($opt{'count_query'})
+ or die "Error preparing $opt{'count_query'}: ". dbh->errstr;
+ $count_sth->execute
+ or die "Error executing $opt{'count_query'}: ". $count_sth->errstr;
+ my $total = $count_sth->fetchrow_arrayref->[0];
+
+ my $sth = dbh->prepare("$opt{'query'} $limit")
+ or die "Error preparing $opt{'query'}: ". dbh->errstr;
+ $sth->execute
+ or die "Error executing $opt{'query'}: ". $sth->errstr;
+
+ #can get # of rows without fetching them all?
+ my $rows = $sth->fetchall_arrayref;
+
+%>
+<!-- mason kludge -->
+<% my $pager = include ( '/elements/pager.html',
+ 'offset' => $offset,
+ 'num_rows' => scalar(@$rows),
+ 'total' => $total,
+ 'maxrecords' => $maxrecords,
+ );
+%>
+
+<%= $total %> total <%= $opt{'name'} %><BR><BR><%= $pager %>
+<%= include( '/elements/table.html' ) %>
+ <TR>
+ <% foreach ( @{$sth->{NAME}} ) { %>
+ <TH><%= $_ %></TH>
+ <% } %>
+ </TR>
+ <% foreach my $row ( @$rows ) { %>
+ <TR>
+ <% foreach ( @$row ) { %>
+ <TD><%= $_ %></TD>
+ <% } %>
+ </TR>
+ <% } %>
+
+</TABLE>
+<%= $pager %>
+</BODY>
+</HTML>
<HTML>
<HEAD>
<TITLE>Credit Card Receipt Report Criteria</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Credit Card Receipt Report Criteria</H1>
- </CENTER>
- <HR>
+ <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ <H1>Credit Card Receipt Report Criteria</H1>
<FORM ACTION="report_cc.cgi" METHOD="post">
Return <B>credit card receipt report</B> for period:
- from <INPUT TYPE="text" NAME="beginning"> <i>m/d/y</i>
- to <INPUT TYPE="text" NAME="ending"> <i>m/d/y</i>
+ <TABLE>
+ <TR>
+ <TD ALIGN="right">From: </TD>
+ <TD><INPUT TYPE="text" NAME="beginning" ID="beginning_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="beginning_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "beginning_text",
+ ifFormat: "%m/%d/%Y",
+ button: "beginning_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ <TD ALIGN="right">To: </TD>
+ <TD><INPUT TYPE="text" NAME="ending" ID="ending_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="ending_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "ending_text",
+ ifFormat: "%m/%d/%Y",
+ button: "ending_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ </TABLE>
- <P><INPUT TYPE="submit" VALUE="Get Report">
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
</FORM>
-
- <HR>
-
</BODY>
</HTML>
<HTML>
<HEAD>
<TITLE>In House Credit Report Criteria</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>In House Credit Report Criteria</H1>
- </CENTER>
- <HR>
+ <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ <H1>In House Credit Report Criteria</H1>
<FORM ACTION="report_credit.cgi" METHOD="post">
Return <B>in house credit report</B> for period:
- from <INPUT TYPE="text" NAME="beginning"> <i>m/d/y</i>
- to <INPUT TYPE="text" NAME="ending"> <i>m/d/y</i>
+ <TABLE>
+ <TR>
+ <TD ALIGN="right">From: </TD>
+ <TD><INPUT TYPE="text" NAME="beginning" ID="beginning_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="beginning_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "beginning_text",
+ ifFormat: "%m/%d/%Y",
+ button: "beginning_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ <TD ALIGN="right">To: </TD>
+ <TD><INPUT TYPE="text" NAME="ending" ID="ending_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="ending_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "ending_text",
+ ifFormat: "%m/%d/%Y",
+ button: "ending_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ </TABLE>
- <P><INPUT TYPE="submit" VALUE="Get Report">
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
</FORM>
-
- <HR>
-
</BODY>
</HTML>
<HTML>
<HEAD>
<TITLE>Payment report criteria</TITLE>
+ <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
</HEAD>
- <BODY>
- <CENTER>
- <H1>Payment report criteria</H1>
- </CENTER>
- <HR>
+ <BODY BGCOLOR="#e8e8e8">
+ <H1>Payment report criteria</H1>
<FORM ACTION="cust_pay.cgi" METHOD="post">
<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
- Return <SELECT NAME="payby">
- <OPTION VALUE="">all</OPTION>
- <OPTION VALUE="CARD">credit card</OPTION>
- <OPTION VALUE="CHEK">electronic check (ACH)</OPTION>
- <OPTION VALUE="BILL">check/cash</OPTION>
- </SELECT> payments for period<BR>
- from <INPUT TYPE="text" NAME="beginning"> <i>m/d/y</i>
- to <INPUT TYPE="text" NAME="ending"> <i>m/d/y</i>
- <P><INPUT TYPE="submit" VALUE="Get Report">
+ <TABLE>
+ <TR>
+ <TD ALIGN="right">Payments of type: </TD>
+ <TD><SELECT NAME="payby">
+ <OPTION VALUE="">all</OPTION>
+ <OPTION VALUE="CARD">credit card (all)</OPTION>
+ <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION>
+ <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION>
+ <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION>
+ <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
+ <OPTION VALUE="BILL">check / cash</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">From: </TD>
+ <TD><INPUT TYPE="text" NAME="beginning" ID="beginning_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="beginning_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "beginning_text",
+ ifFormat: "%m/%d/%Y",
+ button: "beginning_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ <TD ALIGN="right">To: </TD>
+ <TD><INPUT TYPE="text" NAME="ending" ID="ending_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="ending_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "ending_text",
+ ifFormat: "%m/%d/%Y",
+ button: "ending_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ </TABLE>
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
</FORM>
- <HR>
</BODY>
</HTML>
--- /dev/null
+<!-- mason kludge -->
+<%
+
+ #doesn't yet deal with daily/weekly packages
+
+ #needs to be re-written in sql for efficiency
+
+ my $now = $cgi->param('date') && str2time($cgi->param('date')) || time;
+ $now =~ /^(\d+)$/ or die "unparsable date?";
+ $now = $1;
+
+ my %prepaid;
+
+ my @cust_bill_pkg =
+ grep { $_->cust_pkg && $_->cust_pkg->part_pkg->freq !~ /^([01]|\d+[dw])$/ }
+ qsearch( 'cust_bill_pkg', {
+ 'recur' => { op=>'!=', value=>0 },
+ 'edate' => { op=>'>', value=>$now },
+ }, );
+
+ foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
+
+ #conceptual false laziness w/texas tax exempt_amount stuff in
+ #FS::cust_main::bill
+
+ my $freq = $cust_bill_pkg->cust_pkg->part_pkg->freq;
+ my $per_month = sprintf("%.2f", $cust_bill_pkg->recur / $freq);
+
+ my($mon, $year) = (localtime($cust_bill_pkg->sdate) )[4,5];
+ $mon+=2; $year+=1900;
+
+ foreach my $which_month ( 2 .. $freq ) {
+ until ( $mon < 13 ) { $mon -= 12; $year++; }
+ $prepaid{"$year-$mon"} += $per_month;
+ $mon++;
+ }
+
+ }
+
+ my @mon = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+
+%>
+
+<%= header( 'Prepaid Income (Unearned Revenue) Report',
+ menubar( 'Main Menu'=>$p, ) ) %>
+<%= table() %>
+<%
+
+ my $total = 0;
+
+ my ($now_mon, $now_year) = (localtime($now))[4,5];
+ $now_mon+=2; $now_year+=1900;
+ until ( $now_mon < 13 ) { $now_mon -= 12; $now_year++; }
+
+ my $subseq = 0;
+ for my $year ( $now_year .. 2037 ) {
+ for my $mon ( ( $subseq++ ? 1 : $now_mon ) .. 12 ) {
+ if ( $prepaid{"$year-$mon"} ) {
+ $total += $prepaid{"$year-$mon"};
+ %> <TR><TD ALIGN="right"><%= $mon[$mon-1]. ' '. $year %></TD>
+ <TD ALIGN="right">
+ <%= sprintf("%.2f", $prepaid{"$year-$mon"} ) %>
+ </TD>
+ </TR>
+ <%
+ }
+ }
+
+ }
+
+%>
+<TR><TH>Total</TH><TD ALIGN="right"><%= sprintf("%.2f", $total) %></TD></TR>
+</TABLE>
+</BODY>
+</HTML>
--- /dev/null
+<HTML>
+ <HEAD>
+ <TITLE>Prepaid Income (Unearned Revenue) Report</TITLE>
+ <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT>
+ </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ <H1>Prepaid Income (Unearned Revenue) Report</H1>
+ <FORM ACTION="report_prepaid_income.cgi" METHOD="post">
+ Prepaid income (unearned revenue) as of <INPUT TYPE="text" NAME="date" VALUE="now">
+ <INPUT TYPE="submit" VALUE="Generate report">
+ </BODY>
+</HTML>
+ <TABLE>
+
<!-- mason kludge -->
<%
-my $user = getotaker;
+ my $charged = <<END;
+ sum( charged
+ - coalesce(
+ ( select sum(amount) from cust_bill_pay
+ where cust_bill.invnum = cust_bill_pay.invnum )
+ ,0
+ )
+ - coalesce(
+ ( select sum(amount) from cust_credit_bill
+ where cust_bill.invnum = cust_credit_bill.invnum )
+ ,0
+ )
-print header('Current Receivables Report Results');
+ )
+END
-open (REPORT, "freeside-receivables-report -v $user |");
+ my $owed_cols = <<END;
+ coalesce(
+ ( select $charged from cust_bill
+ where cust_bill._date > extract(epoch from now())-2592000
+ and cust_main.custnum = cust_bill.custnum
+ )
+ ,0
+ ) as owed_0_30,
-print '<PRE>';
-while(<REPORT>) {
- print $_;
-}
-print '</PRE>';
+ coalesce(
+ ( select $charged from cust_bill
+ where cust_bill._date > extract(epoch from now())-5184000
+ and cust_bill._date <= extract(epoch from now())-2592000
+ and cust_main.custnum = cust_bill.custnum
+ )
+ ,0
+ ) as owed_30_60,
-print '</BODY></HTML>';
+ coalesce(
+ ( select $charged from cust_bill
+ where cust_bill._date > extract(epoch from now())-7776000
+ and cust_bill._date <= extract(epoch from now())-5184000
+ and cust_main.custnum = cust_bill.custnum
+ )
+ ,0
+ ) as owed_60_90,
-%>
+ coalesce(
+ ( select $charged from cust_bill
+ where cust_bill._date <= extract(epoch from now())-7776000
+ and cust_main.custnum = cust_bill.custnum
+ )
+ ,0
+ ) as owed_90_plus,
+
+ coalesce(
+ ( select $charged from cust_bill
+ where cust_main.custnum = cust_bill.custnum
+ )
+ ,0
+ ) as owed_total
+END
+
+ my $recurring = <<END;
+ 0 < ( select freq from part_pkg
+ where cust_pkg.pkgpart = part_pkg.pkgpart )
+END
+
+ my $packages_cols = <<END;
+
+ ( select count(*) from cust_pkg
+ where cust_main.custnum = cust_pkg.custnum
+ and $recurring
+ and ( cancel = 0 or cancel is null )
+ ) as uncancelled_pkgs,
+
+ ( select count(*) from cust_pkg
+ where cust_main.custnum = cust_pkg.custnum
+ and $recurring
+ and ( cancel = 0 or cancel is null )
+ and ( susp = 0 or susp is null )
+ ) as active_pkgs
+
+END
+
+ my $sql = <<END;
+select *, $owed_cols, $packages_cols from cust_main
+where 0 <
+ coalesce(
+ ( select $charged from cust_bill
+ where cust_main.custnum = cust_bill.custnum
+ )
+ ,0
+ )
+
+order by lower(company), lower(last)
+
+END
+
+ my $total_sql = "select $owed_cols";
+
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute or die $sth->errstr;
+
+ my $total_sth = dbh->prepare($total_sql) or die dbh->errstr;
+ $total_sth->execute or die $total_sth->errstr;
+
+%>
+<%= header('Accounts Receivable Aging Summary', menubar( 'Main Menu'=>$p, ) ) %>
+<%= table() %>
+ <TR>
+ <TH>Customer</TH>
+ <TH>Status</TH>
+ <TH>0-30</TH>
+ <TH>30-60</TH>
+ <TH>60-90</TH>
+ <TH>90+</TH>
+ <TH>Total</TH>
+ </TR>
+<% while ( my $row = $sth->fetchrow_hashref() ) {
+ my $status = 'Cancelled';
+ my $statuscol = 'FF0000';
+ if ( $row->{uncancelled_pkgs} ) {
+ $status = 'Suspended';
+ $statuscol = 'FF9900';
+ if ( $row->{active_pkgs} ) {
+ $status = 'Active';
+ $statuscol = '00CC00';
+ }
+ }
+%>
+ <TR>
+ <TD><A HREF="<%= $p %>view/cust_main.cgi?<%= $row->{'custnum'} %>"><%= $row->{'custnum'} %>:
+ <%= $row->{'company'} ? $row->{'company'}. ' (' : '' %><%= $row->{'last'}. ', '. $row->{'first'} %><%= $row->{'company'} ? ')' : '' %></A>
+ </TD>
+ <TD><B><FONT SIZE=-1 COLOR="#<%= $statuscol %>"><%= $status %></FONT></B></TD>
+ <TD ALIGN="right">$<%= sprintf("%.2f", $row->{'owed_0_30'} ) %></TD>
+ <TD ALIGN="right">$<%= sprintf("%.2f", $row->{'owed_30_60'} ) %></TD>
+ <TD ALIGN="right">$<%= sprintf("%.2f", $row->{'owed_60_90'} ) %></TD>
+ <TD ALIGN="right">$<%= sprintf("%.2f", $row->{'owed_90_plus'} ) %></TD>
+ <TD ALIGN="right"><B>$<%= sprintf("%.2f", $row->{'owed_total'} ) %></B></TD>
+ </TR>
+<% } %>
+<% my $row = $total_sth->fetchrow_hashref(); %>
+ <TR>
+ <TD COLSPAN=6> </TD>
+ </TR>
+ <TR>
+ <TD COLSPAN=2><I>Total</I></TD>
+ <TD ALIGN="right"><I>$<%= sprintf("%.2f", $row->{'owed_0_30'} ) %></TD>
+ <TD ALIGN="right"><I>$<%= sprintf("%.2f", $row->{'owed_30_60'} ) %></TD>
+ <TD ALIGN="right"><I>$<%= sprintf("%.2f", $row->{'owed_60_90'} ) %></TD>
+ <TD ALIGN="right"><I>$<%= sprintf("%.2f", $row->{'owed_90_plus'} ) %></TD>
+ <TD ALIGN="right"><I><B>$<%= sprintf("%.2f", $row->{'owed_total'} ) %></B></I></TD>
+ </TR>
+</TABLE>
+</BODY>
+</HTML>
<HTML>
<HEAD>
<TITLE>Tax Report Criteria</TITLE>
- </HEAD>
- <BODY>
- <CENTER>
- <H1>Tax Report Criteria</H1>
- </CENTER>
- <HR>
+ <LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2">
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT>
+ <SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> </HEAD>
+ <BODY BGCOLOR="#e8e8e8">
+ <H1>Tax Report Criteria</H1>
<FORM ACTION="report_tax.cgi" METHOD="post">
- Return <B>tax report</B> for period:
- from <INPUT TYPE="text" NAME="beginning"> <i>m/d/y</i>
- to <INPUT TYPE="text" NAME="ending"> <i>m/d/y</i>
+ Return <B>tax report</B> for period:
+ <TABLE>
+ <TR>
+ <TD ALIGN="right">From: </TD>
+ <TD><INPUT TYPE="text" NAME="beginning" ID="beginning_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="beginning_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "beginning_text",
+ ifFormat: "%m/%d/%Y",
+ button: "beginning_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ <TD ALIGN="right">To: </TD>
+ <TD><INPUT TYPE="text" NAME="ending" ID="ending_text" VALUE="" SIZE=11 MAXLENGTH=10> <IMG SRC="../images/calendar.png" ID="ending_button" STYLE="cursor: pointer" TITLE="Select date"><BR><i>m/d/y</i></TD>
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "ending_text",
+ ifFormat: "%m/%d/%Y",
+ button: "ending_button",
+ align: "BR"
+ });
+</SCRIPT>
+ </TR>
+ </TABLE>
- <P><INPUT TYPE="submit" VALUE="Get Report">
+ <BR><INPUT TYPE="submit" VALUE="Get Report">
</FORM>
-
- <HR>
-
</BODY>
</HTML>
+++ /dev/null
-<%
-
-my $conf = new FS::Conf;
-my $maxrecords = $conf->config('maxsearchrecordsperpage');
-
-my $limit = '';
-$limit .= "LIMIT $maxrecords" if $maxrecords;
-
-my $offset = $cgi->param('offset') || 0;
-$limit .= " OFFSET $offset" if $offset;
-
-my $total;
-
-my $sql = $cgi->param('sql');
-$sql =~ s/^\s*SELECT//i;
-
-my $count_sql = $sql;
-$count_sql =~ s/^(.*)\s+FROM\s/COUNT(*) FROM /i;
-
-my $sth = dbh->prepare("SELECT $count_sql")
- or eidiot dbh->errstr. " doing $count_sql\n";
-$sth->execute or eidiot "Error executing \"$count_sql\": ". $sth->errstr;
-
-$total = $sth->fetchrow_arrayref->[0];
-
-my $sth = dbh->prepare("SELECT $sql $limit")
- or eidiot dbh->errstr. " doing $sql\n";
-$sth->execute or eidiot "Error executing \"$sql\": ". $sth->errstr;
-my $rows = $sth->fetchall_arrayref;
-
-%>
-<!-- mason kludge -->
-<%
-
- #begin pager
- my $pager = '';
- if ( $total != scalar(@$rows) && $maxrecords ) {
- unless ( $offset == 0 ) {
- $cgi->param('offset', $offset - $maxrecords);
- $pager .= '<A HREF="'. $cgi->self_url.
- '"><B><FONT SIZE="+1">Previous</FONT></B></A> ';
- }
- my $poff;
- my $page;
- for ( $poff = 0; $poff < $total; $poff += $maxrecords ) {
- $page++;
- if ( $offset == $poff ) {
- $pager .= qq!<FONT SIZE="+2">$page</FONT> !;
- } else {
- $cgi->param('offset', $poff);
- $pager .= qq!<A HREF="!. $cgi->self_url. qq!">$page</A> !;
- }
- }
- unless ( $offset + $maxrecords > $total ) {
- $cgi->param('offset', $offset + $maxrecords);
- $pager .= '<A HREF="'. $cgi->self_url.
- '"><B><FONT SIZE="+1">Next</FONT></B></A> ';
- }
- }
- #end pager
-
- print header('Query Results', menubar('Main Menu'=>$p) ).
- "$total total rows<BR><BR>$pager". table().
- "<TR>";
- print "<TH>$_</TH>" foreach @{$sth->{NAME}};
- print "</TR>";
-
- foreach $row ( @$rows ) {
- print "<TR>";
- print "<TD>$_</TD>" foreach @$row;
- print "</TR>";
- }
-
- print "</TABLE>$pager</BODY></HTML>";
-
-%>
--- /dev/null
+<%= include( '/elements/header.html', 'Query Results',
+ include( '/elements/menubar.html', 'Main Menu' => $p )
+ )
+%>
+
+<%= include( 'elements/search.html',
+ 'name' => 'rows',
+ 'query' => 'SELECT '. ( $cgi->param('sql')
+ || eidiot('Empty query') ),
+ )
+%>
+
$orderby = "ORDER BY ${tblname}username";
} elsif ( $query eq 'uid' ) {
$sortby=\*uid_sort;
- $orderby = ( $unlinked ? 'AND' : 'WHERE' ).
+ $orderby = ( $unlinked ? ' AND' : ' WHERE' ).
" ${tblname}uid IS NOT NULL ORDER BY ${tblname}uid";
+} elsif ( $cgi->param('popnum') =~ /^(\d+)$/ ) {
+ $unlinked .= ( $unlinked ? 'AND' : 'WHERE' ).
+ " popnum = $1";
+ $sortby=\*username_sort;
+ $orderby = "ORDER BY ${tblname}username";
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ $unlinked .= ( $unlinked ? ' AND' : ' WHERE' ).
+ " $1 = ( SELECT svcpart FROM cust_svc ".
+ " WHERE cust_svc.svcnum = svc_acct.svcnum ) ";
+ $sortby=\*uid_sort;
+ #$sortby=\*svcnum_sort;
} else {
$sortby=\*uid_sort;
@svc_acct = @{&usernamesearch};
}
-if ( $query eq 'svcnum' || $query eq 'username' || $query eq 'uid' ) {
+
+if ( $query eq 'svcnum'
+ || $query eq 'username'
+ || $query eq 'uid'
+ || $cgi->param('popnum') =~ /^(\d+)$/
+ || $cgi->param('svcpart') =~ /^(\d+)$/
+ ) {
my $statement = "SELECT COUNT(*) FROM svc_acct $unlinked";
my $sth = dbh->prepare($statement)
'svcnum' => $_->svcnum,
'pkgnum' => '',
}), qsearch('svc_domain',{});
+} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ @svc_domain =
+ qsearch( 'svc_domain', {}, '',
+ " WHERE $1 = ( SELECT svcpart FROM cust_svc ".
+ " WHERE cust_svc.svcnum = svc_domain.svcnum ) "
+ );
+ $sortby=\*svcnum_sort;
} else {
$cgi->param('domain') =~ /^([\w\-\.]+)$/;
my($domain)=$1;
--- /dev/null
+<%
+
+my $conf = new FS::Conf;
+
+my($query)=$cgi->keywords;
+$query ||= ''; #to avoid use of unitialized value errors
+my(@svc_forward,$sortby);
+if ( $query eq 'svcnum' ) {
+ $sortby=\*svcnum_sort;
+ @svc_forward=qsearch('svc_forward',{});
+} else {
+ eidiot('unimplemented');
+}
+
+if ( scalar(@svc_forward) == 1 ) {
+ print $cgi->redirect(popurl(2). "view/svc_forward.cgi?". $svc_forward[0]->svcnum);
+ #exit;
+} elsif ( scalar(@svc_forward) == 0 ) {
+%>
+<!-- mason kludge -->
+<%
+ eidiot "No matching forwards found!\n";
+} else {
+%>
+<!-- mason kludge -->
+<%
+ my $total = scalar(@svc_forward);
+ print header("Mail forward Search Results",''), <<END;
+
+ $total matching mail forwards found
+ <TABLE BORDER=4 CELLSPACING=0 CELLPADDING=0>
+ <TR>
+ <TH>Service #<BR><FONT SIZE=-1>(click to view forward)</FONT></TH>
+ <TH>Mail to<BR><FONT SIZE=-1>(click to view account)</FONT></TH>
+ <TH>Forwards to<BR><FONT SIZE=-1>(click to view account)</FONT></TH>
+ </TR>
+END
+
+ foreach my $svc_forward (
+ sort $sortby (@svc_forward)
+ ) {
+ my $svcnum = $svc_forward->svcnum;
+
+ my $src = $svc_forward->src;
+ $src = "<I>(anything)</I>$src" if $src =~ /^@/;
+ if ( $svc_forward->srcsvc_acct ) {
+ $src = qq!<A HREF="${p}view/svc_acct.cgi?!. $svc_forward->srcsvc. '">'.
+ $svc_forward->srcsvc_acct->email. '</A>';
+ }
+
+ my $dst = $svc_forward->dst;
+ if ( $svc_forward->dstsvc_acct ) {
+ $dst = qq!<A HREF="${p}view/svc_acct.cgi?!. $svc_forward->dstsvc. '">'.
+ $svc_forward->dstsvc_acct->email. '</A>';
+ }
+
+ print <<END;
+ <TR>
+ <TD><A HREF="${p}view/svc_forward.cgi?$svcnum">$svcnum</A></TD>
+ <TD>$src</TD>
+ <TD>$dst</TD>
+ </TR>
+END
+
+ }
+
+ print <<END;
+ </TABLE>
+ </BODY>
+</HTML>
+END
+
+}
+
+sub svcnum_sort {
+ $a->getfield('svcnum') <=> $b->getfield('svcnum');
+}
+
+%>
--- /dev/null
+<%
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $invnum = $1;
+
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Invoice #$invnum not found!" unless $cust_bill;
+
+http_header('Content-Type' => 'application/pdf' );
+%>
+<%= $cust_bill->print_pdf %>
--- /dev/null
+<%
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $invnum = $1;
+
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+die "Invoice #$invnum not found!" unless $cust_bill;
+
+http_header('Content-Type' => 'application/postscript' );
+%>
+<%= $cust_bill->print_ps %>
print qq!<A HREF="${p}edit/cust_pay.cgi?$invnum">Enter payments (check/cash) against this invoice</A> | !
if $cust_bill->owed > 0;
-print qq!<A HREF="${p}misc/print-invoice.cgi?$invnum">Reprint this invoice</A>!. '<BR><BR>';
+print qq!<A HREF="${p}misc/print-invoice.cgi?$invnum">Reprint this invoice</A>!;
+if ( grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ) {
+ print qq! | <A HREF="${p}misc/email-invoice.cgi?$invnum">!.
+ qq!Re-email this invoice</A>!;
+}
+
+print '<BR><BR>';
+
+my $conf = new FS::Conf;
+if ( $conf->exists('invoice_latex') ) {
+ print menubar(
+ 'View typeset invoice' => "${p}view/cust_bill-pdf.cgi?$invnum",
+ ), '<BR><BR>';
+}
#false laziness with search/cust_bill_event.cgi
print "Billing address", &ntable("#cccccc"), "<TR><TD>",
&ntable("#cccccc",2),
- '<TR><TD ALIGN="right">Contact name</TD>',
+ '<TR><TD ALIGN="right">Contact name</TD>',
'<TD COLSPAN=3 BGCOLOR="#ffffff">',
$cust_main->last, ', ', $cust_main->first,
'</TD>';
$cust_main->country,
'</TD></TR>',
;
- my $daytime_label = FS::Msgcat::_gettext('daytime') || 'Day Phone';
- my $night_label = FS::Msgcat::_gettext('night') || 'Night Phone';
+ my $daytime_label = FS::Msgcat::_gettext('daytime') || 'Day Phone';
+ my $night_label = FS::Msgcat::_gettext('night') || 'Night Phone';
print '<TR><TD ALIGN="right">'. $daytime_label.
'</TD><TD COLSPAN=5 BGCOLOR="#ffffff">',
$cust_main->daytime || ' ', '</TD></TR>',
print '<TD VALIGN="top">';
print &ntable("#cccccc"), "<TR><TD>", &ntable("#cccccc",2),
- '<TR><TD ALIGN="right">Customer number</TD><TD BGCOLOR="#ffffff">',
+ '<TR><TD ALIGN="right">Customer number</TD><TD BGCOLOR="#ffffff">',
$custnum, '</TD></TR>',
;
my $referral = qsearchs('part_referral', {
'refnum' => $cust_main->refnum
} );
- print '<TR><TD ALIGN="right">Advertising source</TD><TD BGCOLOR="#ffffff">',
+ print '<TR><TD ALIGN="right">Advertising source</TD><TD BGCOLOR="#ffffff">',
$referral->refnum, ": ", $referral->referral, '</TD></TR>';
}
print '<TR><TD ALIGN="right">Order taker</TD><TD BGCOLOR="#ffffff">',
$cust_main->otaker, '</TD></TR>';
- print '<TR><TD ALIGN="right">Referring Customer</TD><TD BGCOLOR="#ffffff">';
+ print '<TR><TD ALIGN="right">Referring Customer</TD><TD BGCOLOR="#ffffff">';
my $referring_cust_main = '';
if ( $cust_main->referral_custnum
&& ( $referring_cust_main =
print "Billing information (",
qq!<A HREF="!, popurl(2), qq!misc/bill.cgi?$custnum">!, "Bill now</A>)",
&ntable("#cccccc"), "<TR><TD>", &ntable("#cccccc",2),
- '<TR><TD ALIGN="right">Tax exempt</TD><TD BGCOLOR="#ffffff">',
+ '<TR><TD ALIGN="right">Tax exempt</TD><TD BGCOLOR="#ffffff">',
$cust_main->tax ? 'yes' : 'no',
'</TD></TR>',
- '<TR><TD ALIGN="right">Postal invoices</TD><TD BGCOLOR="#ffffff">',
+ '<TR><TD ALIGN="right">Postal invoices</TD><TD BGCOLOR="#ffffff">',
( grep { $_ eq 'POST' } @invoicing_list ) ? 'yes' : 'no',
'</TD></TR>',
- '<TR><TD ALIGN="right">Email invoices</TD><TD BGCOLOR="#ffffff">',
+ '<TR><TD ALIGN="right">Email invoices</TD><TD BGCOLOR="#ffffff">',
join(', ', grep { $_ ne 'POST' } @invoicing_list ) || 'no',
'</TD></TR>',
- '<TR><TD ALIGN="right">Billing type</TD><TD BGCOLOR="#ffffff">',
+ '<TR><TD ALIGN="right">Billing type</TD><TD BGCOLOR="#ffffff">',
;
if ( $cust_main->payby eq 'CARD' || $cust_main->payby eq 'DCRD' ) {
my $payinfo = $cust_main->payinfo;
$payinfo = 'x'x(length($payinfo)-4). substr($payinfo,(length($payinfo)-4));
- print 'Credit card ',
+ print 'Credit card ',
( $cust_main->payby eq 'CARD' ? '(automatic)' : '(on-demand)' ),
'</TD></TR>',
'<TR><TD ALIGN="right">Card number</TD><TD BGCOLOR="#ffffff">',
;
} elsif ( $cust_main->payby eq 'CHEK' || $cust_main->payby eq 'DCHK') {
my( $account, $aba ) = split('@', $cust_main->payinfo );
- print 'Electronic check',
+ print 'Electronic check ',
( $cust_main->payby eq 'CHEK' ? '(automatic)' : '(on-demand)' ),
'</TD></TR>',
'<TR><TD ALIGN="right">Account number</TD><TD BGCOLOR="#ffffff">',
} elsif ( $cust_main->payby eq 'LECB' ) {
$cust_main->payinfo =~ /^(\d{3})(\d{3})(\d{4})$/;
my $payinfo = "$1-$2-$3";
- print 'Phone bill billing</TD></TR>',
+ print 'Phone bill billing</TD></TR>',
'<TR><TD ALIGN="right">Phone number</TD><TD BGCOLOR="#ffffff">',
$payinfo, '</TD></TR>',
;
;
} elsif ( $cust_main->payby eq 'COMP' ) {
print 'Complimentary</TD></TR>',
- '<TR><TD ALIGN="right">Authorized by</TD><TD BGCOLOR="#ffffff">',
+ '<TR><TD ALIGN="right">Authorized by</TD><TD BGCOLOR="#ffffff">',
$cust_main->payinfo, '</TD></TR>',
'<TR><TD ALIGN="right">Expiration</TD><TD BGCOLOR="#ffffff">',
$cust_main->paydate, '</TD></TR>',
#get package info
-my $packages = get_packages($cust_main);
+my $packages = get_packages($cust_main, $conf);
if ( @$packages ) {
%>
<%=$pkg->{pkg}%> - <%=$pkg->{comment}%> ( <%=pkg_details_link($pkg)%> )<BR>
<% unless ($pkg->{cancel}) { %>
( <%=pkg_change_link($pkg)%> )
- ( <%=pkg_dates_link($pkg)%> | <%=pkg_customize_link($pkg)%> )
+ ( <%=pkg_dates_link($pkg)%> | <%=pkg_customize_link($pkg,$custnum)%> )
<% } %>
</TD>
<%
#foreach (qw(setup last_bill next_bill susp expire cancel)) {
- # print qq! <TD ROWSPAN=$rowspan>! . pkg_datestr($pkg,$_) . qq!</TD>\n!;
+ # print qq! <TD ROWSPAN=$rowspan>! . pkg_datestr($pkg,$_,$conf) . qq!</TD>\n!;
#}
print "<TD ROWSPAN=$rowspan>". &itable('');
- #move
- my %freq = (
- 1 => 'monthly',
- 2 => 'bi-monthly',
- 3 => 'quarterly',
- 6 => 'semi-annually',
- 12 => 'annually',
- 24 => 'bi-annually',
- 36 => 'tri-annually',
- );
-
sub freq {
+
+ #false laziness w/edit/part_pkg.cgi
+ my %freq = ( #move this
+ '1d' => 'daily',
+ '1w' => 'weekly',
+ '2w' => 'biweekly (every 2 weeks)',
+ '1' => 'monthly',
+ '2' => 'bimonthly (every 2 months)',
+ '3' => 'quarterly (every 3 months)',
+ '6' => 'semiannually (every 6 months)',
+ '12' => 'annually',
+ '24' => 'biannually (every 2 years)',
+ );
+
my $freq = shift;
exists $freq{$freq} ? $freq{$freq} : "every $freq months";
}
if ( $pkg->{cancel} ) { #status: cancelled
print '<TR><TD><FONT COLOR="#ff0000"><B>Cancelled </B></FONT></TD>'.
- '<TD>'. pkg_datestr($pkg,'cancel'). '</TD></TR>';
+ '<TD>'. pkg_datestr($pkg,'cancel',$conf). '</TD></TR>';
unless ( $pkg->{setup} ) {
print '<TR><TD COLSPAN=2>Never billed</TD></TR>';
} else {
print "<TR><TD>Setup </TD><TD>".
- pkg_datestr($pkg, 'setup'). '</TD></TR>';
+ pkg_datestr($pkg, 'setup',$conf). '</TD></TR>';
print "<TR><TD>Last bill </TD><TD>".
- pkg_datestr($pkg, 'last_bill'). '</TD></TR>'
+ pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>'
if $pkg->{'last_bill'};
print "<TR><TD>Suspended </TD><TD>".
- pkg_datestr($pkg, 'susp'). '</TD></TR>'
+ pkg_datestr($pkg, 'susp',$conf). '</TD></TR>'
if $pkg->{'susp'};
}
if ( $pkg->{susp} ) { #status: suspended
print '<TR><TD><FONT COLOR="#FF9900"><B>Suspended</B> </FONT></TD>'.
- '<TD>'. pkg_datestr($pkg,'susp'). '</TD></TR>';
+ '<TD>'. pkg_datestr($pkg,'susp',$conf). '</TD></TR>';
unless ( $pkg->{setup} ) {
print '<TR><TD COLSPAN=2>Never billed</TD></TR>';
} else {
print "<TR><TD>Setup </TD><TD>".
- pkg_datestr($pkg, 'setup'). '</TD></TR>';
+ pkg_datestr($pkg, 'setup',$conf). '</TD></TR>';
}
print "<TR><TD>Last bill </TD><TD>".
- pkg_datestr($pkg, 'last_bill'). '</TD></TR>'
+ pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>'
if $pkg->{'last_bill'};
# next bill ??
print "<TR><TD>Expires </TD><TD>".
- pkg_datestr($pkg, 'expire'). '</TD></TR>'
+ pkg_datestr($pkg, 'expire',$conf). '</TD></TR>'
if $pkg->{'expire'};
print '<TR><TD COLSPAN=2>( '. pkg_unsuspend_link($pkg).
' | '. pkg_cancel_link($pkg). ' )</TD></TR>';
unless ( $pkg->{freq} ) {
print "<TR><TD COLSPAN=2>One-time charge</TD></TR>".
'<TR><TD>Billed </TD><TD>'.
- pkg_datestr($pkg,'setup'). '</TD></TR>';
+ pkg_datestr($pkg,'setup',$conf). '</TD></TR>';
} else {
print '<TR><TD COLSPAN=2><FONT COLOR="#00CC00"><B>Active</B></FONT>'.
', billed '. freq($pkg->{freq}). '</TD></TR>'.
'<TR><TD>Setup </TD><TD>'.
- pkg_datestr($pkg, 'setup'). '</TD></TR>';
+ pkg_datestr($pkg, 'setup',$conf). '</TD></TR>';
}
}
print "<TR><TD>Last bill </TD><TD>".
- pkg_datestr($pkg, 'last_bill'). '</TD></TR>'
+ pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>'
if $pkg->{'last_bill'};
print "<TR><TD>Next bill </TD><TD>".
- pkg_datestr($pkg, 'next_bill'). '</TD></TR>'
+ pkg_datestr($pkg, 'next_bill',$conf). '</TD></TR>'
if $pkg->{'next_bill'};
print "<TR><TD>Expires </TD><TD>".
- pkg_datestr($pkg, 'expire'). '</TD></TR>'
+ pkg_datestr($pkg, 'expire',$conf). '</TD></TR>'
if $pkg->{'expire'};
if ( $pkg->{freq} ) {
print '<TR><TD COLSPAN=2>( '. pkg_suspend_link($pkg).
== true)
window.location.href = href;
}
+function cust_credit_areyousure(href) {
+ if (confirm("Are you sure you want to delete this credit?")
+ == true)
+ window.location.href = href;
+}
</SCRIPT>
END
$cust_credit->reason,
time2str("%D", $cust_credit_bill->_date),
);
+ my $delete =
+ $cust_credit->closed !~ /^Y/i && $conf->exists('deletecredits')
+ ? qq! (<A HREF="javascript:cust_credit_areyousure('${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum. qq!')">delete</A>)!
+ : '';
push @history,
"$date\tCredit #$crednum: $reason<BR>".
- "(applied to invoice #$invnum on $app_date)\t\t\t$amount\t";
+ "(applied to invoice #$invnum on $app_date)$delete\t\t\t$amount\t";
}
}
qsearch('cust_credit',{'custnum'=>$custnum});
foreach my $credit (@credits) {
my($cref)=$credit->hashref;
+ my $delete =
+ $credit->closed !~ /^Y/i && $conf->exists('deletecredits')
+ ? qq! (<A HREF="javascript:cust_credit_areyousure('${p}misc/delete-cust_credit.cgi?!. $credit->crednum. qq!')">delete</A>)!
+ : '';
push @history,
$cref->{_date} . "\t" .
qq!<A HREF="! . popurl(2). qq!edit/cust_credit_bill.cgi?!. $cref->{crednum} . qq!">!.
'<b><font size="+1" color="#ff0000">Unapplied credit #' .
$cref->{crednum} . "</font></b></A>: ".
- $cref->{reason} . "\t\t\t" . $credit->credited . "\t";
+ $cref->{reason} . "$delete\t\t\t" . $credit->credited . "\t";
}
my(@refunds)=qsearch('cust_refund',{'custnum'=> $custnum } );
sub get_packages {
+ my $cust_main = shift or return undef;
+ my $conf = shift;
+
+ my @packages = ();
+
+ foreach my $cust_pkg (
+ $conf->exists('hidecancelledpackages')
+ ? $cust_main->ncancelled_pkgs
+ : $cust_main->all_pkgs
+ ) {
+
+ my $part_pkg = $cust_pkg->part_pkg;
+
+ my %pkg = ();
+ $pkg{pkgnum} = $cust_pkg->pkgnum;
+ $pkg{pkg} = $part_pkg->pkg;
+ $pkg{pkgpart} = $part_pkg->pkgpart;
+ $pkg{comment} = $part_pkg->getfield('comment');
+ $pkg{freq} = $part_pkg->freq;
+ $pkg{setup} = $cust_pkg->getfield('setup');
+ $pkg{last_bill} = $cust_pkg->getfield('last_bill');
+ $pkg{next_bill} = $cust_pkg->getfield('bill');
+ $pkg{susp} = $cust_pkg->getfield('susp');
+ $pkg{expire} = $cust_pkg->getfield('expire');
+ $pkg{cancel} = $cust_pkg->getfield('cancel');
+
+ my %svcparts = ();
-my $cust_main = shift or return undef;
-
-my @packages = ();
-
-foreach my $cust_pkg (($conf->exists('hidecancelledpackages') ? ($cust_main->ncancelled_pkgs)
- : ($cust_main->all_pkgs))) {
-
- my $part_pkg = $cust_pkg->part_pkg;
-
- my %pkg = ();
- $pkg{pkgnum} = $cust_pkg->pkgnum;
- $pkg{pkg} = $part_pkg->pkg;
- $pkg{pkgpart} = $part_pkg->pkgpart;
- $pkg{comment} = $part_pkg->getfield('comment');
- $pkg{freq} = $part_pkg->freq;
- $pkg{setup} = $cust_pkg->getfield('setup');
- $pkg{last_bill} = $cust_pkg->getfield('last_bill');
- $pkg{next_bill} = $cust_pkg->getfield('bill');
- $pkg{susp} = $cust_pkg->getfield('susp');
- $pkg{expire} = $cust_pkg->getfield('expire');
- $pkg{cancel} = $cust_pkg->getfield('cancel');
-
- $pkg{svcparts} = [];
-
- foreach my $pkg_svc (qsearch('pkg_svc', { 'pkgpart' => $part_pkg->pkgpart })) {
-
- next if ($pkg_svc->quantity == 0);
-
- my $part_svc = qsearchs('part_svc', { 'svcpart' => $pkg_svc->svcpart });
-
- my $svcpart = {};
- $svcpart->{svcpart} = $part_svc->svcpart;
- $svcpart->{svc} = $part_svc->svc;
- $svcpart->{svcdb} = $part_svc->svcdb;
- $svcpart->{quantity} = $pkg_svc->quantity;
- $svcpart->{count} = 0;
+ foreach my $pkg_svc (
+ qsearch('pkg_svc', { 'pkgpart' => $part_pkg->pkgpart })
+ ) {
+
+ next if ($pkg_svc->quantity == 0);
+
+ my $part_svc = qsearchs('part_svc', { 'svcpart' => $pkg_svc->svcpart });
+
+ my $svcpart = {};
+ $svcpart->{svcpart} = $part_svc->svcpart;
+ $svcpart->{svc} = $part_svc->svc;
+ $svcpart->{svcdb} = $part_svc->svcdb;
+ $svcpart->{quantity} = $pkg_svc->quantity;
+ $svcpart->{count} = 0;
+
+ $svcpart->{services} = [];
- $svcpart->{services} = [];
+ $svcparts{$svcpart->{svcpart}} = $svcpart;
- foreach my $cust_svc (qsearch('cust_svc', { 'pkgnum' => $cust_pkg->pkgnum,
- 'svcpart' => $part_svc->svcpart } )) {
+ }
- my $svc = {};
- $svc->{svcnum} = $cust_svc->svcnum;
- $svc->{label} = ($cust_svc->label)[1];
+ foreach my $cust_svc (
+ qsearch( 'cust_svc', {
+ 'pkgnum' => $cust_pkg->pkgnum,
+ #'svcpart' => $part_svc->svcpart,
+ }
+ )
+ ) {
+
+ warn "svcnum ". $cust_svc->svcnum. " / svcpart ". $cust_svc->svcpart. "\n";
+ my $svc = {
+ 'svcnum' => $cust_svc->svcnum,
+ 'label' => ($cust_svc->label)[1],
+ };
+
+ #false laziness with above, to catch extraneous services. whole
+ #damn thing should be OO...
+ my $svcpart = ( $svcparts{$cust_svc->svcpart} ||= {
+ 'svcpart' => $cust_svc->svcpart,
+ 'svc' => $cust_svc->part_svc->svc,
+ 'svcdb' => $cust_svc->part_svc->svcdb,
+ 'quantity' => 0,
+ 'count' => 0,
+ 'services' => [],
+ } );
push @{$svcpart->{services}}, $svc;
}
- push @{$pkg{svcparts}}, $svcpart;
+ $pkg{svcparts} = [ values %svcparts ];
+ push @packages, \%pkg;
+
}
-
- push @packages, \%pkg;
-
-}
-
-return \@packages;
+
+ return \@packages;
}
sub svc_link {
- my ($svcpart, $svc) = (shift,shift) or return '';
- return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svcpart->{svc}</A>!;
+ my ($svcpart, $svc) = (shift,shift) or return '';
+ return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svcpart->{svc}</A>!;
}
sub svc_label_link {
- my ($svcpart, $svc) = (shift,shift) or return '';
- return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svc->{label}</A>!;
+ my ($svcpart, $svc) = (shift,shift) or return '';
+ return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svc->{label}</A>!;
}
}
sub pkg_datestr {
- my($pkg, $field) = @_ or return '';
+ my($pkg, $field, $conf) = @_ or return '';
return ' ' unless $pkg->{$field};
my $format = $conf->exists('pkg_showtimes')
? '<B>%D</B> <FONT SIZE=-3>%l:%M:%S%P %z</FONT>'
sub pkg_customize_link {
my $pkg = shift or return '';
+ my $custnum = shift;
return qq!<A HREF="${p}edit/part_pkg.cgi?keywords=$custnum;clone=$pkg->{pkgpart};pkgnum=$pkg->{pkgnum}">Customize</A>!;
}
<%
#if ( $cust_pkg && $cust_pkg->part_pkg->plan eq 'sqlradacct_hour' ) {
-if ( $part_svc->part_export('sqlradius') ) {
+if ( $part_svc->part_export('sqlradius')
+ || $part_svc->part_export('sqlradius_withdomain')
+) {
my $last_bill;
my %plandata;
}
my $seconds = $svc_acct->seconds_since_sqlradacct( $last_bill, time );
- my $h = int($seconds/3600);
- my $m = int( ($seconds%3600) / 60 );
- my $s = $seconds%60;
+ my $hour = int($seconds/3600);
+ my $min = int( ($seconds%3600) / 60 );
+ my $sec = $seconds%60;
my $input = $svc_acct->attribute_since_sqlradacct(
$last_bill, time, 'AcctInputOctets'
) / 1048576;
if ( $seconds ) {
- print "Online <B>$h</B>h <B>$m</B>m <B>$s</B>s";
+ print "Online <B>$hour</B>h <B>$min</B>m <B>$sec</B>s";
} else {
print 'Has not logged on';
}
$svc_broadband->getfield('speed_up'),
$svc_broadband->getfield('ip_addr')
);
+%>
-
-
-print header('Broadband Service View', menubar(
+<%=header('Broadband Service View', menubar(
( ( $custnum )
? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum",
"View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum",
"${p}misc/cancel-unaudited.cgi?$svcnum" )
),
"Main menu" => $p,
-)).
- qq!<A HREF="${p}edit/svc_broadband.cgi?$svcnum">Edit this information</A><BR>!.
- ntable("#cccccc"). '<TR><TD>'. ntable("#cccccc",2).
- qq!<TR><TD ALIGN="right">Service number</TD>!.
- qq!<TD BGCOLOR="#ffffff">$svcnum</TD></TR>!.
- qq!<TR><TD ALIGN="right">Router</TD>!.
- qq!<TD BGCOLOR="#ffffff">$routernum: $routername</TD></TR>!.
- qq!<TR><TD ALIGN="right">Download Speed</TD>!.
- qq!<TD BGCOLOR="#ffffff">$speed_down</TD></TR>!.
- qq!<TR><TD ALIGN="right">Upload Speed</TD>!.
- qq!<TD BGCOLOR="#ffffff">$speed_up</TD></TR>!.
- qq!<TR><TD ALIGN="right">IP Address</TD>!.
- qq!<TD BGCOLOR="#ffffff">$ip_addr</TD></TR>!.
- '</TD></TR><TR ROWSPAN="1"><TD></TD></TR>';
+))
+%>
+<A HREF="<%=${p}%>edit/svc_broadband.cgi?<%=$svcnum%>">Edit this information</A>
+<BR>
+<%=ntable("#cccccc")%>
+ <TR>
+ <TD>
+ <%=ntable("#cccccc",2)%>
+ <TR>
+ <TD ALIGN="right">Service number</TD>
+ <TD BGCOLOR="#ffffff"><%=$svcnum%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Router</TD>
+ <TD BGCOLOR="#ffffff"><%=$routernum%>: <%=$routername%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Download Speed</TD>
+ <TD BGCOLOR="#ffffff"><%=$speed_down%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">Upload Speed</TD>
+ <TD BGCOLOR="#ffffff"><%=$speed_up%></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right">IP Address</TD>
+ <TD BGCOLOR="#ffffff"><%=$ip_addr%></TD>
+ </TR>
+ <TR COLSPAN="2"><TD></TD></TR>
+
+<%
foreach (sort { $a cmp $b } $svc_broadband->virtual_fields) {
- print $svc_broadband->pvf($_)->widget('HTML', 'view',
- $svc_broadband->getfield($_)), "\n";
+ print $svc_broadband->pvf($_)->widget('HTML', 'view',
+ $svc_broadband->getfield($_)), "\n";
}
-print '</TABLE>';
+%>
+ </TABLE>
+ </TD>
+ </TR>
+</TABLE>
-print '<BR>'. joblisting({'svcnum'=>$svcnum}, 1).
- '</BODY></HTML>'
-;
+<BR>
+<%=ntable("#cccccc", 2)%>
+<%
+ my $sb_router = qsearchs('router', { svcnum => $svcnum });
+ if ($sb_router) {
+ %>
+ <B>Router associated: <%=$sb_router->routername%> </B>
+ <A HREF="<%=popurl(2)%>edit/router.cgi?<%=$sb_router->routernum%>">
+ (details)
+ </A>
+ <BR>
+ <% my @addr_block;
+ if (@addr_block = $sb_router->addr_block) {
+ %>
+ <B>Address space </B>
+ <A HREF="<%=popurl(2)%>browse/addr_block.cgi">
+ (edit)
+ </A>
+ <BR>
+ <% print ntable("#cccccc", 1);
+ foreach (@addr_block) { %>
+ <TR>
+ <TD><%=$_->ip_gateway%>/<%=$_->ip_netmask%></TD>
+ </TR>
+ <% } %>
+ </TABLE>
+ <% } else { %>
+ <B>No address space allocated.</B>
+ <% } %>
+ <BR>
+ <%
+ } else {
%>
+
+<FORM METHOD="GET" ACTION="<%=popurl(2)%>edit/router.cgi">
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%=$svcnum%>">
+Add router named
+ <INPUT TYPE="text" NAME="routername" SIZE="32" VALUE="Broadband router (<%=$svcnum%>)">
+ <INPUT TYPE="submit" VALUE="Add router">
+</FORM>
+
+<%
+}
+%>
+
+<BR>
+<%=joblisting({'svcnum'=>$svcnum}, 1)%>
+ </BODY>
+</HTML>
+
--- /dev/null
+<!-- mason kludge -->
+<%
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/;
+my $svcnum = $1;
+my $svc_external = qsearchs( 'svc_external', { 'svcnum' => $svcnum } )
+ or die "svc_external: Unknown svcnum $svcnum";
+
+#false laziness w/all svc_*.cgi
+my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $svcnum } );
+my $pkgnum = $cust_svc->getfield('pkgnum');
+my($cust_pkg, $custnum);
+if ($pkgnum) {
+ $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $pkgnum } );
+ $custnum = $cust_pkg->custnum;
+} else {
+ $cust_pkg = '';
+ $custnum = '';
+}
+#eofalse
+
+%>
+
+<%= header('External Service View', menubar(
+ ( ( $custnum )
+ ? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum",
+ "View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum",
+ )
+ : ( "Cancel this (unaudited) external service" =>
+ "${p}misc/cancel-unaudited.cgi?$svcnum" )
+ ),
+ "Main menu" => $p,
+)) %>
+
+<A HREF="<%=$p%>edit/svc_external.cgi?<%=$svcnum%>">Edit this information</A><BR>
+<%= ntable("#cccccc") %><TR><TD><%= ntable("#cccccc",2) %>
+
+<TR><TD ALIGN="right">Service number</TD>
+ <TD BGCOLOR="#ffffff"><%= $svcnum %></TD></TR>
+<TR><TD ALIGN="right">External ID</TD>
+ <TD BGCOLOR="#ffffff"><%= $svc_external->id %></TD></TR>
+<TR><TD ALIGN="right">Title</TD>
+ <TD BGCOLOR="#ffffff"><%= $svc_external->title %></TD></TR>
+
+<% foreach (sort { $a cmp $b } $svc_external->virtual_fields) { %>
+ <%= $svc_external->pvf($_)->widget('HTML', 'view', $svc_external->getfield($_)) %>
+<% } %>
+
+</TABLE></TD></TR></TABLE>
+<BR><%= joblisting({'svcnum'=>$svcnum}, 1) %>
+</BODY></HTML>
? ( "View this package (#$pkgnum)" => "${p}view/cust_pkg.cgi?$pkgnum",
"View this customer (#$custnum)" => "${p}view/cust_main.cgi?$custnum",
)
- : ( "Cancel this (unaudited) account" =>
+ : ( "Cancel this (unaudited) mail forward" =>
"${p}misc/cancel-unaudited.cgi?$svcnum" )
),
"Main menu" => $p,
$svc_forward->dstsvc,
$svc_forward->dst,
);
+my $src = $svc_forward->dbdef_table->column('src') ? $svc_forward->src : '';
+
my $svc = $part_svc->svc;
-my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$srcsvc})
- or die "Corrupted database: no svc_acct.svcnum matching srcsvc $srcsvc";
-my $source = $svc_acct->email;
+
+my $source;
+if ($srcsvc) {
+ my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$srcsvc})
+ or die "Corrupted database: no svc_acct.svcnum matching srcsvc $srcsvc";
+ $source = $svc_acct->email;
+} else {
+ $source = $src;
+}
+
my $destination;
if ($dstsvc) {
my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$dstsvc})
or die "Corrupted database: no svc_acct.svcnum matching dstsvc $dstsvc";
$destination = $svc_acct->email;
-}else{
+} else {
$destination = $dst;
}
FREESIDE_PATH="%%%FREESIDE_PATH%%%"
-PASSWD_USER=%%%PASSWD_USER%%%
-PASSWD_MACHINE=%%%PASSWD_MACHINE%%%
-
-SIGNUP_USER=%%%SIGNUP_USER%%%
-SIGNUP_MACHINE=%%%SIGNUP_MACHINE%%%
-SIGNUP_AGENTNUM=%%%SIGNUP_AGENTNUM%%%
-SIGNUP_REFNUM=%%%SIGNUP_REFNUM%%%
-
SELFSERVICE_USER=%%%SELFSERVICE_USER%%%
-SELFSERVICE_MACHINE=%%%SELFSERVICE_MACHINE%%%
+SELFSERVICE_MACHINES="%%%SELFSERVICE_MACHINES%%%"
case "$1" in
start)
freeside-queued $QUEUED_USER
echo "done."
- echo -n "Starting fs_passwd_server: "
- su freeside -c "$FREESIDE_PATH/fs_passwd/fs_passwd_server $PASSWD_USER $PASSWD_MACHINE" &
- echo "done."
-
- echo -n "Starting fs_signup_server: "
- su freeside -c "$FREESIDE_PATH/fs_signup/fs_signup_server $SIGNUP_USER $SIGNUP_MACHINE $SIGNUP_AGENTNUM $SIGNUP_REFNUM" &
- echo "done."
-
- echo -n "Starting freeside-selfservice-server: "
- freeside-selfservice-server $SELFSERVICE_USER $SELFSERVICE_MACHINE
- echo "done."
+ for MACHINE in $SELFSERVICE_MACHINES; do
+ echo -n "Starting freeside-selfservice-server to $MACHINE: "
+ freeside-selfservice-server $SELFSERVICE_USER $MACHINE
+ echo "done."
+ done
;;
stop)
kill `cat /var/run/freeside-queued.pid`
echo "done."
- echo -n "Stopping fs_passwd_server: "
- killall fs_passwd_server
- echo "done."
+ if [ -e /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid ]
+ then
+ echo -n "Stopping (old) freeside-selfservice-server: "
+ kill `cat /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid`
+ rm /var/run/freeside-selfservice-server.$SELFSERVICE_USER.pid
+ fi
- echo -n "Stopping fs_signup_server: "
- killall fs_signup_server
- echo "done."
+ for MACHINE in $SELFSERVICE_MACHINES; do
+ echo -n "Stopping freeside-selfservice-server to $MACHINE: "
+ kill `cat /var/run/freeside-selfservice-server.$SELFSERVICE_USER.$MACHINE.pid`
+ echo "done."
+ done
- echo -n "Stopping freeside-selfservice-server: "
- kill `cat /var/run/freeside-selfservice-server.pid`
- echo "done."
;;
restart)
--- /dev/null
+#!/bin/sh
+
+echo "deb http://pouncequick.420.am/~ivan/freeside-woody/ ./" >>/etc/apt/sources.list
+
+apt-get update
+apt-get install screen zsh libapache-mod-ssl libapache-mod-perl rsync \
+ postgresql cvs fsh \
+ liburi-perl libhtml-tagset-perl libnet-perl liblocale-codes-perl \
+ libnet-whois-perl libwww-perl libbusiness-creditcard-perl \
+ libmailtools-perl libtimedate-perl libdate-manip-perl \
+ libfile-counterfile-perl libfreezethaw-perl libstring-approx-perl \
+ libtext-template-perl libdbi-perl libdbd-pg-perl \
+ libdbix-datasource-perl libdbix-dbschema-perl libnet-ssh-perl \
+ libstring-shellquote-perl libnet-scp-perl libapache-asp-perl \
+ libtie-ixhash-perl libtime-duration-perl \
+ libhtml-widgets-selectlayers-perl libstorable-perl \
+ libapache-dbi-perl libcache-cache-perl libdbd-mysql-perl
+
+useradd freeside
+su postgres -c "createuser -P freeside"
+
+su freeside -c "createdb freeside"
+
+#?
+cd ../../..
+make install-perl-modules
+make create-config
+freeside-adduser -c -h /usr/local/etc/freeside/htpasswd ivan
+su freeside -c 'freeside-setup ivan'
+su freeside -c '/home/ivan/freeside/bin/populate-msgcat ivan'
+make deploy
#www/p5-HTML-Widgets-SelectLayers
devel/p5-Storable
www/p5-Apache-DBI
+devel/p5-Cache-Cache
--- /dev/null
+#!/bin/sh
+
+DIR=`pwd`
+
+#cd /usr/ports
+#cvs -q -d anoncvs@anoncvs6.usa.openbsd.org:/cvs up -r OPENBSD_`uname -r | perl -pe 's/\./_/g;'` -Pd
+
+for a in `grep -v '^ *#' $DIR/ports`
+do cd /usr/ports/$a
+ make install
+done
+
+for a in `grep -v '^ *#' $DIR/cpan`
+do perl -MCPAN -e "install $a"
+done
+
+#from /usr/local/share/doc/postgresql/README.OpenBSD
+useradd -c "PostgreSQL Admin User" -g =uid -m -d /var/postgresql -s /bin/sh postgresql
+
+su -l postgresql -c 'mkdir /var/postgresql/data'
+su -l postgresql -c 'initdb -D /var/postgresql/data'
+
+cat <<END >>/etc/rc.local
+if [ -x /usr/local/bin/pg_ctl ]; then
+ su -l postgresql -c "/usr/local/bin/pg_ctl start \
+ -D /var/postgresql/data -l /var/postgresql/logfile \
+ -o '-D /var/postgresql/data'"
+ echo -n ' postgresql'
+fi
+END
+
+cat <<END >>/etc/rc.shutdown
+if [ -f /var/postgresql/data/postmaster.pid ]; then
+ su -l postgresql -c "/usr/local/bin/pg_ctl stop -m fast \
+ -D /var/postgresql/data"
+ rm -f /var/postgresql/data/postmaster.pid
+fi
+
+su -l postgresql -c "/usr/local/bin/pg_ctl start \
+ -D /var/postgresql/data -l /var/postgresql/logfile \
+ -o '-D /var/postgresql/data'"
+
+useradd -c "Freeside" -g =uid -m freeside
+su -l postgresql -c 'createuser -P freeside'
+su -l freeside -c 'createdb freeside'
+
+#?
+cd ../..
+make install-perl-modules
+make create-config
+make deploy
+
+#edit apache config, etc.
+
--- /dev/null
+DBIx::DBSchema
+Time::Duration
+Business::CreditCard
+String::ShellQuote
+Net::SSH
+HTML::Mason
+HTML::Widgets::SelectLayers
+DBIx::DataSource
+Date::Manip
+String::Approx
+Tie::IxHash
+Date::Parse
+File::CounterFile
+Net::SCP
+Mail::Internet
--- /dev/null
+shells/zsh
+misc/screen
+#www/apache13-modssl
+www/mod_perl
+net/rsync
+databases/postgresql
+converters/p5-MIME-Base64
+security/p5-Digest-MD5
+security/p5-MD5
+www/p5-HTML-Tagset
+www/p5-HTML-Parser
+net/p5-libnet
+misc/p5-Locale-Codes
+net/p5-Net-Whois
+www/p5-libwww
+#mail/p5-Mail-Tools
+devel/p5-FreezeThaw
+textproc/p5-Text-Template
+databases/p5-DBI
+databases/p5-DBD-Pg
+#databases/p5-DBD-Msql-Mysql
+www/p5-Apache-ASP
+devel/p5-Storable
+www/p5-Apache-DBI
#!/bin/sh
-wget ftp://apt-rpm.tuxfamily.org/apt/redhat/7.3/en/i386/RPMS.extra/apt-*i386.rpm
+wget --passive-ftp ftp://apt-rpm.tuxfamily.org/apt/redhat/7.3/en/i386/RPMS.extra/apt-*i386.rpm
rpm -i apt*i386.rpm
cp sources.list /etc/apt/
apt-get update; apt-get update
String::Approx, Text::Template, DBIx::DataSource, \
DBIx::DBSchema, Net::SSH, String::ShellQuote, \
Net::SCP, Apache::ASP, Tie::IxHash, Time::Duration, \
- HTML::Widgets::SelectLayers, Apache::DBI"
+ HTML::Widgets::SelectLayers, Apache::DBI, Cache::Cache"
+
+useradd freeside
+
+chkconfig postgresql on
+/etc/init.d/postgresql start
+
+su postgres -c "createuser -P freeside"
+
+su freeside -c "createdb freeside"
+
+#?
+cd ../..
+make install-perl-modules
+make create-config
+freeside-adduser -c -h /usr/local/etc/freeside/htpasswd ivan
+su freeside -c 'freeside-setup ivan'
+su freeside -c '/home/ivan/freeside/bin/populate-msgcat ivan'
+make deploy
+
--- /dev/null
+#!/bin/sh
+
+
+wget --passive-ftp --continue ftp://apt-rpm.tuxfamily.org/apt/redhat/9/en/i386/RPMS.extra/apt-*i386.rpm
+rpm -i apt*i386.rpm
+cp sources.list /etc/apt/
+apt-get update; apt-get update
+#apt-get install apache mod_ssl mod_perl perl-CGI perl-CPAN perl-DBD-MySQL perl-DBD-Pg perl-DBI perl-DateManip perl-Digest-MD5 perl-HTML-Parser perl-HTML-Tagset perl-MIME-Base64 perl-Storable perl-TimeDate perl-URI perl-libnet perl-libwww-perl perl-suidperl rsync postgresql postgresql-docs postgresql-libs postgresql-server screen zsh lftp cvs #openssh
+
+apt-get install perl-Devel-Symdump perl-BSD-Resource rpm-build gdbm-devel expat-devel openssl-devel krb5-devel db4-devel
+
+wget --passive-ftp --continue http://reb00t.com/linux/RPMS/redhat-9/apache/apache-1.3.28-0.n0i.src.rpm http://reb00t.com/linux/RPMS/redhat-9/mm/mm-1.2.1-0.n0i.i686.rpm http://reb00t.com/linux/RPMS/redhat-9/mm/mm-devel-1.2.1-0.n0i.i686.rpm
+rpm -i mm-1.2.1-0.n0i.i686.rpm mm-devel-1.2.1-0.n0i.i686.rpm apache-1.3.28-0.n0i.src.rpm
+
+install -d /usr/src/redhat
+for a in BUILD RPMS SOURCES SPECS SRPMS; do install -d /usr/src/redhat/$a; done
+for a in athlon i386 i486 i586 i686 noarch; do install -d /usr/src/redhat/RPMS/$a; done
+
+cd /usr/src/redhat/SPECS
+rpmbuild -ba apache.spec
+
+cd /usr/src/redhat/RPMS/i386
+rpm -i apache-1.3.28-0.n0i.i386.rpm
+
+apt-get install perl-CGI perl-CPAN perl-DBD-MySQL perl-DBD-Pg perl-DBI perl-DateManip perl-HTML-Parser perl-HTML-Tagset perl-TimeDate perl-URI perl-libwww-perl perl-suidperl rsync postgresql postgresql-docs postgresql-libs postgresql-server screen zsh lftp cvs gcc gd #openssh
+
+wget --passive-ftp --continue http://atrpms.physik.fu-berlin.de/dist/rh9/perl-GD/perl-GD-2.11-7.rh9.at.i386.rpm http://atrpms.physik.fu-berlin.de/dist/rh9/atrpms/atrpms-45-1.rh9.at.noarch.rpm http://atrpms.physik.fu-berlin.de/dist/rh9/yum/yum-2.0.4-28.rh9.at.noarch.rpm http://atrpms.physik.fu-berlin.de/dist/rh9/gd/gd-2.0.15-1_6.rh9.at.i386.rpm http://atrpms.physik.fu-berlin.de/dist/rh9/atrpms/atrpms-package-config-45-1.rh9.at.noarch.rpm
+
+cp /etc/apt/apt.conf /etc/apt/apt.conf.real
+
+rpm -i --replacefiles atrpms-package-config-45-1.rh9.at.noarch.rpm yum-2.0.4-28.rh9.at.noarch.rpm atrpms-45-1.rh9.at.noarch.rpm gd-2.0.15-1_6.rh9.at.i386.rpm perl-GD-2.11-7.rh9.at.i386.rpm
+
+mv /etc/apt/apt.conf.real /etc/apt/apt.conf
+
+perl -MCPAN -e"install Locale::Country, Net::Whois, Business::CreditCard, \
+ Mail::Internet, File::CounterFile, FreezeThaw, \
+ String::Approx, Text::Template, DBIx::DataSource, \
+ DBIx::DBSchema, Net::SSH, String::ShellQuote, \
+ Net::SCP, Apache::ASP, Tie::IxHash, Time::Duration, \
+ HTML::Widgets::SelectLayers, Apache::DBI, Cache::Cache \
+ Test::Pod NetAddr::IP IPC::ShareLite Chart::LinesPoints"
+
+echo 'OPTIONS="-DHAVE_PERL -DHAVE_SSL"' >>/etc/sysconfig/apache
+
+#remove perl & ssl LoadModule lines from /etc/httpd/conf/httpd.conf
+#as they're statically linked
+
+/usr/sbin/useradd freeside
+
+/sbin/chkconfig postgresql on
+/etc/init.d/postgresql start
+
+su postgres -c "createuser -P freeside"
+
+su freeside -c "createdb freeside"
+
+#?
+cd ../../..
+make install-perl-modules
+make create-config
+freeside-adduser -c -h /usr/local/etc/freeside/htpasswd ivan
+su freeside -c 'freeside-setup ivan'
+su freeside -c '/home/ivan/freeside/bin/populate-msgcat ivan'
+make deploy
+
--- /dev/null
+rpm ftp://apt-rpm.tuxfamily.org/apt redhat/9/en/i386 os updates extra
+rpm-src ftp://apt-rpm.tuxfamily.org/apt redhat/9/en/i386 os updates extra
+++ /dev/null
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
-#
-# (Except where explictly superceded by other copyright notices)
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# Unless otherwise specified, all modifications, corrections or
-# extensions to this work which alter its source code become the
-# property of Best Practical Solutions, LLC when submitted for
-# inclusion in the work.
-#
-#
-# END LICENSE BLOCK
-RT is an enterprise-grade issue tracking system. It allows
-organizations to keep track of their to-do lists, who is working
-on which tasks, what's already been done, and when tasks were
-completed. It is available under the terms of version 2 of the GNU
-General Public License (GPL), so it doesn't cost anything to set
-up and use.
-
-
- Jesse Vincent
- Best Practical Solutions, LLC
- March 2003
-
-REQUIRED PACKAGES:
-------------------
-
-o Perl 5.8.0 or later (http://www.perl.com).
-
- (If you intend to use the FastCGI or SpeedyCGI support, you
- need to make sure that perl has been built with support for
- setgid perl scripts.)`
-
- Perl 5.6.1 is currently deprecated and will be officially desupported
- in a future release
-
-o A DB backend; MySQL is recommended ( http://www.mysql.com )
- Currently supported: Mysql 4.0.13 or later.
- Postgres 7.2 or later.
-
- Mysql 3.23.46 or newer with support for InnoDB
- is currently deprecated and will be officially
- desupported in a future release.
-
-o Apache version 1.3.x or 2.x (http://httpd.apache.org)
- with mod_perl -- (http://perl.apache.org )
- or a webserver with FastCGI support (www.fastcgi.com)
-
- mod_perl 2.0 isn't quite ready for prime_time just yet;
- Best Practical Solutions strongly recommends that sites use
- Apache 1.3 or FastCGI.
-
- Compiling mod_perl on Apache 1.3.x as a DSO has been known
- to have massive stability problems and is not recommended.
-
- mod_perl 1.x must be build with EVERYTHING=1
-
- RT's FastCGI handler runs setgid to the 'rt' group to
- protect RT's database password. You may need to install
- a special "suidperl" package or reconfigure your perl
- setup to support "setuid scripts" if you intend to use RT
- with FastCGI.
-
- Debian GNU/* 3.0+: the package which installs suidperl is
- called perl-suid, and should work without any tweaking.
-
- FreeBSD 4.2+: the package is called sperl, and should
- install a suidperl that just works
-
- Conectiva Linux 6.0+: suidperl is installed by default when
- perl is installed, but the program /bin/suidperl is not setuid.
- You must use chmod to make it setuid.
-
-
-
-o Various and sundry perl modules
- A tool included with RT takes care of the installation of
- most of these automatically during the install process.
-
- The tool supplied with RT uses Perl's CPAN system
- (http://www.cpan.org) to install modules. Some operating
- systems package all or some of the modules required and
- you may be better off installing the modules that way.
-
-
-GENERAL INSTALLATION
---------------------
-
-This is a rough guide to installing RT. For more detail, you'll want
-to read 'Chapter 2: Installing' in RT's manual, available at
-http://www.bestpractical.com/rt
-
-1 Unpack this distribution SOMWHERE OTHER THAN where you want to install RT
-
- Granted, you've already got it open. To do this cleanly:
-
- tar xzvf rt.tar.gz -C /tmp
-
-2 Run the "configure" script.
-
- ./configure --help to see the list of options
- ./configure (with the flags you want)
-
-3 Satisfy RT's myriad dependencies.
-
-3.1 Check for compliance:
-
- perl sbin/rt-test-dependencies \
- --with-<databasename> --with-<web-environment>
-
- databasename is one of: mysql, postgres
- web-environment is one of: fastcgi, modperl1, modperl2
-
-3.2 If there are unsatisfied dependencies, install them by hand or run:
-
- perl sbin/rt-test-dependencies \
- --with-<databasename> --with-<web-environment> --install
-
-
-3.3 Check to make sure everything was installed properly:
-
- perl sbin/rt-test-dependencies \
- --with-<databasename> --with-<web-environment>
-
-4 Create a group called 'rt'
-
-5a FOR A NEW INSTALLATION:
-
- As root, type:
- make install (replace "make" with the local name for
- Make, if you need to)
-
-
- make initialize-database
-
-
- If the make fails, type:
- make dropdb
- and start over from step 5a
-
-5b FOR UPGRADING: (Within the RT 3.0.x series)
-
- As root, type:
- make upgrade (replace "make" with the local name for
- Make, if you need to)
-
- This will build new binaries, config files and libraries without
- overwriting your RT database.
-
- It may then instruct you to update your RT system database objects
-
-6 Edit etc/RT_SiteConfig.pm in your RT installation directory, by specifying
- any values you need to change from the defaults in etc/RT_Config.pm
-
-7 Configure the email and web gateways, as described below.
-
-8 Stop and start your webserver, so it picks up your configuration changes.
-
- NOTE: root's password for the web interface is "password"
- (without the quotes.) Not changing this is a SECURITY risk
-
-9 Configure RT per the instructions in RT's manual.
-
- Until you do this, RT will not be able to send or receive email,
- nor will it be more than marginally functional. This is not an
- optional step.
-
-
-THE WEB INTERFACE
------------------
-
-RT's web interface is based around HTML::Mason, which works best with the mod_perl
-perl interpreter within Apache httpd. Alternatively, support for the FastCGI
-(and plain CGI) interface is also provided as 'bin/mason_handler.fcgi'.
-
-Apache
- You'll need to add a few lines to your httpd.conf telling it about RT:
-
-<VirtualHost your.ip.address>
- ServerName your.rt.server.hostname
- DocumentRoot /opt/rt3/share/html
- AddDefaultCharset UTF-8
-
- # this line applies to Apache2+mod_perl2 only
- PerlModule Apache2 Apache::compat
-
- PerlModule Apache::DBI
- PerlRequire /opt/rt3/bin/webmux.pl
-
- # this section applies to Apache 1 only
- <Location />
- SetHandler perl-script
- PerlHandler RT::Mason
- </Location>
-
- # this section applies to Apache2+mod_perl2 only
- <FilesMatch "\.html$">
- SetHandler perl-script
- PerlHandler RT::Mason
- </FilesMatch>
- <LocationMatch "/Attachment/">
- SetHandler perl-script
- PerlHandler RT::Mason
- </LocationMatch>
- <LocationMatch "/REST/">
- SetHandler perl-script
- PerlHandler RT::Mason
- </LocationMatch>
-</VirtualHost>
-
-
-
-SETTING UP THE MAIL GATEWAY
----------------------------
-
-An alias for the initial queue will need to be made in either your
-global mail aliases file (if you are using NIS) or locally on your
-machine.
-
-Add the following lines to /etc/aliases (or your local equivalent) :
-
-rt: "|/opt/rt3/bin/rt-mailgate --queue general --action correspond --url http://localhost/"
-rt-comment: "|/opt/rt3/bin/rt-mailgate --queue general --action comment --url http://localhost/"
- | | |
- <queue-name>----/ | |
- | |
- <correspond or comment depending on whether | |
- the mail should be resent to the requestor>---/ |
- |
- <URL for RT's web interface>---/
-
-
-BUGS
-----
-
-To report a bug, send email to rt-3.0-bugs@fsck.com.
-
-GETTING HELP
-------------
-
-If RT is mission-critical for you or if you use it heavily, we recommend that
-you purchase a commercial support contract. Details on support contracts
-are available at http://www.bestpractical.com.
-
-If you're interested in having RT extended or customized or would like more
-information about commercial support options, please send email to
-<sales@bestpractical.com> to discuss rates and availability.
-
-
-RT-USERS MAILINGLIST
---------------------
-
-To keep up to date on the latest RT tips, techniques and extensions,
-you probably want to join the rt-users mailing list. Send a message to:
-
- rt-users-request@lists.fsck.com
-
-With the body of the message consisting of only the word:
-
- subscribe
-
-If you're interested in hacking on RT, you'll want to subscribe to
-rt-devel@lists.fsck.com. Subscribe to it with instructions similar to
-those above.
-
-Address questions about the stable release to the rt-users list, and
-questions about the development version to the rt-devel list. If you feel
-your questions are best not asked publicly, send them personally to
-<jesse@bestpractical.com>.
-
-
-RT WEBSITE
-----------
-
-For current information about RT, check out the RT website at
- http://www.bestpractical.com/
-
-You'll find screenshots, a pointer to the current version of RT, contributed
-patches, and lots of other great stuff.
-
-
-TROUBLESHOOTING
----------------
-
-If the solution to the problem you're running into isn't obvious and you've
-checked the FAQ, feel free to send mail to rt-users@fsck.com (for released
-versions of RT) or rt-devel@fsck.com (for development versions).
-
-Thanks!