From: cvs2git Date: Tue, 15 Jul 2003 11:23:22 +0000 (+0000) Subject: This commit was manufactured by cvs2svn to create tag 'freeside_1_5_0pre3'. X-Git-Tag: freeside_1_5_0pre3 X-Git-Url: http://git.freeside.biz/gitweb/?a=commitdiff_plain;h=0be8a0f00a4efca21979481cca1010ae2078b09d;hp=745aca307ef43c0c9bd5d8ee78464f624acb7b3f;p=freeside.git This commit was manufactured by cvs2svn to create tag 'freeside_1_5_0pre3'. --- diff --git a/Artistic b/Artistic deleted file mode 100644 index 4ffc78e97..000000000 --- a/Artistic +++ /dev/null @@ -1,125 +0,0 @@ - The "Artistic License" - - Preamble - -The intent of this document is to state the conditions under which a -Package may be copied, such that the Copyright Holder maintains some -semblance of artistic control over the development of the Package, -while giving the users of the package the right to use and distribute -the Package in a more-or-less customary fashion, plus the right to make -reasonable modifications. - -It also grants you the rights to reuse parts of a Package in your own -programs without transferring this License to those programs, provided -that you meet some reasonable requirements. - -Definitions: - - "Package" refers to the collection of files distributed by the - Copyright Holder, and derivatives of that collection of files - created through textual modification. - - "Standard Version" refers to such a Package if it has not been - modified, or has been modified in accordance with the wishes - of the Copyright Holder as specified below. - - "Copyright Holder" is whoever is named in the copyright or - copyrights for the package. - - "You" is you, if you're thinking about copying or distributing - this Package. - - "Reasonable copying fee" is whatever you can justify on the - basis of media cost, duplication charges, time of people involved, - and so on. (You will not be required to justify it to the - Copyright Holder, but only to the computing community at large - as a market that must bear the fee.) - - "Freely Available" means that no fee is charged for the item - itself, though there may be fees involved in handling the item. - It also means that recipients of the item may redistribute it - under the same conditions they received it. - -1. You may make and give away verbatim copies of the source form of the -Standard Version of this Package without restriction, provided that you -duplicate all of the original copyright notices and associated disclaimers. - -2. You may apply bug fixes, portability fixes and other modifications -derived from the Public Domain or from the Copyright Holder. A Package -modified in such a way shall still be considered the Standard Version. - -3. You may otherwise modify your copy of this Package in any way, provided -that you insert a prominent notice in each changed file stating how and -when you changed that file, and provided that you do at least ONE of the -following: - - a) place your modifications in the Public Domain or otherwise make them - Freely Available, such as by posting said modifications to Usenet or - an equivalent medium, or placing the modifications on a major archive - site such as uunet.uu.net, or by allowing the Copyright Holder to include - your modifications in the Standard Version of the Package. - - b) use the modified Package only within your corporation or organization. - - c) rename any non-standard executables so the names do not conflict - with standard executables, which must also be provided, and provide - a separate manual page for each non-standard executable that clearly - documents how it differs from the Standard Version. - - d) make other distribution arrangements with the Copyright Holder. - -4. You may distribute the programs of this Package in object code or -executable form, provided that you do at least ONE of the following: - - a) distribute a Standard Version of the executables and library files, - together with instructions (in the manual page or equivalent) on where - to get the Standard Version. - - b) accompany the distribution with the machine-readable source of - the Package with your modifications. - - c) give non-standard executables non-standard names, and clearly - document the differences in manual pages (or equivalent), together - with instructions on where to get the Standard Version. - - d) make other distribution arrangements with the Copyright Holder. - -5. You may charge a reasonable copying fee for any distribution of this -Package. You may charge any fee you choose for support of this -Package. You may not charge a fee for this Package itself. However, -you may distribute this Package in aggregate with other (possibly -commercial) programs as part of a larger (possibly commercial) software -distribution provided that you do not advertise this Package as a -product of your own. - -6. The scripts and library files supplied as input to or produced as -output from the programs of this Package do not automatically fall -under the copyright of this Package, but belong to whomever generated -them, and may be sold commercially, and may be aggregated with this -Package. If such scripts or library files are aggregated with this -Package via the so-called "undump" or "unexec" methods of producing a -binary executable image, then distribution of such an image shall -neither be construed as a distribution of this Package nor shall it -fall under the restrictions of Paragraphs 3 and 4, provided that you do -not represent such an executable image as a Standard Version of this -Package. - -7. You may reuse parts of this Package in your own programs, provided that -you explicitly state where you got them from, in the source code (and, left -to your courtesy, in the documentation), duplicating all the associated -copyright notices and disclaimers. Besides your changes, if any, must be -clearly marked as such. Parts reused that way will no longer fall under this -license if, and only if, the name of your program(s) have no immediate -connection with the name of the Package itself or its associated programs. -You may then apply whatever restrictions you wish on the reused parts or -choose to place them in the Public Domain--this will apply only within the -context of your package. - -8. The name of the Copyright Holder may not be used to endorse or promote -products derived from this software without specific prior written permission. - -9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - - The End diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 84b3c26ed..706ebe720 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -1032,6 +1032,14 @@ httemplate/docs/config.html }, { + 'key' => 'radius-ip', + 'section' => '', + 'description' => 'RADIUS attribute for IP addresses.', + 'type' => 'select', + 'select_enum' => [ 'Framed-IP-Address', 'Framed-Address' ], + }, + + { 'key' => 'svc_acct-alldomains', 'section' => '', 'description' => 'Allow accounts to select any domain in the database. Normally accounts can only select from the domain set in the service definition and those purchased by the customer.', diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 9a724feac..02fd4e390 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -426,11 +426,11 @@ sub AUTOLOAD { $field =~ s/.*://; if ( defined($value) ) { confess "errant AUTOLOAD $field for $self (arg $value)" - unless $self->can('setfield'); + unless ref($self) && $self->can('setfield'); $self->setfield($field,$value); } else { confess "errant AUTOLOAD $field for $self (no args)" - unless $self->can('getfield'); + unless ref($self) && $self->can('getfield'); $self->getfield($field); } } diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index 7e2ff388e..4302c504a 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -4,7 +4,12 @@ use strict; use vars qw( @ISA $conf $Debug $import ); use Safe; use Carp; -use Time::Local qw(timelocal_nocheck); +BEGIN { + eval "use Time::Local;"; + die "Time::Local 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 Date::Format; #use Date::Manip; use Business::CreditCard; @@ -301,23 +306,11 @@ sub insert { } } - #false laziness with sub replace - my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' }; - $error = $queue->insert($self->getfield('last'), $self->company); + $error = $self->queue_fuzzyfiles_update; if ( $error ) { $dbh->rollback if $oldAutoCommit; - return "queueing job (transaction rolled back): $error"; - } - - if ( defined $self->dbdef_table->column('ship_last') && $self->ship_last ) { - $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' }; - $error = $queue->insert($self->getfield('last'), $self->company); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "queueing job (transaction rolled back): $error"; - } + return "updating fuzzy search cache: $error"; } - #eslaf $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -520,34 +513,47 @@ sub replace { if ( $self->payby =~ /^(CARD|CHEK|LECB)$/ && grep { $self->get($_) ne $old->get($_) } qw(payinfo paydate payname) ) { - # card/check info has changed, want to retry realtime_card invoice events - #false laziness w/collect - foreach my $cust_bill_event ( - grep { - #$_->part_bill_event->plan eq 'realtime-card' - $_->part_bill_event->eventcode =~ - /^\$cust_bill\->realtime_(card|ach|lec)\(\);$/ - && $_->status eq 'done' - && $_->statustext - } - map { $_->cust_bill_event } - grep { $_->cust_bill_event } - $self->open_cust_bill - - ) { - my $error = $cust_bill_event->retry; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "error scheduling invoice events for retry: $error"; - } + # card/check/lec info has changed, want to retry realtime_ invoice events + my $error = $self->retry_realtime; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; } - #eslaf + } + $error = $self->queue_fuzzyfiles_update; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "updating fuzzy search cache: $error"; } - #false laziness with sub insert + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; + +} + +=item queue_fuzzyfiles_update + +Used by insert & replace to update the fuzzy search cache + +=cut + +sub queue_fuzzyfiles_update { + my $self = shift; + + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + my $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' }; - $error = $queue->insert($self->getfield('last'), $self->company); + my $error = $queue->insert($self->getfield('last'), $self->company); if ( $error ) { $dbh->rollback if $oldAutoCommit; return "queueing job (transaction rolled back): $error"; @@ -555,13 +561,12 @@ sub replace { if ( defined $self->dbdef_table->column('ship_last') && $self->ship_last ) { $queue = new FS::queue { 'job' => 'FS::cust_main::append_fuzzyfiles' }; - $error = $queue->insert($self->getfield('last'), $self->company); + $error = $queue->insert($self->getfield('ship_last'), $self->ship_company); if ( $error ) { $dbh->rollback if $oldAutoCommit; return "queueing job (transaction rolled back): $error"; } } - #eslaf $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -1271,7 +1276,10 @@ invoice_time - Use this time when deciding when to print invoices and late notices on those invoices. The default is now. It is specified as a UNIX timestamp; see L). Also see L and L for conversion functions. -retry_card - Retry cards even when not scheduled by invoice events. +retry - Retry card/echeck/LEC transactions even when not scheduled by invoice +events. + +retry_card - Deprecated alias for 'retry' batch_card - This option is deprecated. See the invoice events web interface to control whether cards are batched or run against a realtime gateway. @@ -1305,26 +1313,16 @@ sub collect { return ''; } - if ( exists($options{'retry_card'}) && $options{'retry_card'} ) { - #false laziness w/replace - foreach my $cust_bill_event ( - grep { - #$_->part_bill_event->plan eq 'realtime-card' - $_->part_bill_event->eventcode eq '$cust_bill->realtime_card();' - && $_->status eq 'done' - && $_->statustext - } - map { $_->cust_bill_event } - grep { $_->cust_bill_event } - $self->open_cust_bill - ) { - my $error = $cust_bill_event->retry; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "error scheduling invoice events for retry: $error"; - } + if ( exists($options{'retry_card'}) ) { + carp 'retry_card option passed to collect is deprecated; use retry'; + $options{'retry'} ||= $options{'retry_card'}; + } + if ( exists($options{'retry'}) && $options{'retry'} ) { + my $error = $self->retry_realtime; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; } - #eslaf } foreach my $cust_bill ( $self->cust_bill ) { @@ -1414,6 +1412,60 @@ sub collect { } +=item retry_realtime + +Schedules realtime credit card / electronic check / LEC billing events for +for retry. Useful if card information has changed or manual retry is desired. +The 'collect' method must be called to actually retry the transaction. + +Implementation details: For each of this customer's open invoices, changes +the status of the first "done" (with statustext error) realtime processing +event to "failed". + +=cut + +sub retry_realtime { + 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_bill ( + grep { $_->cust_bill_event } + $self->open_cust_bill + ) { + my @cust_bill_event = + sort { $a->part_bill_event->seconds <=> $b->part_bill_event->seconds } + grep { + #$_->part_bill_event->plan eq 'realtime-card' + $_->part_bill_event->eventcode =~ + /\$cust_bill\->realtime_(card|ach|lec)$/ + && $_->status eq 'done' + && $_->statustext + } + $cust_bill->cust_bill_event; + next unless @cust_bill_event; + my $error = $cust_bill_event[0]->retry; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "error scheduling invoice event for retry: $error"; + } + + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + ''; + +} + =item realtime_bop METHOD AMOUNT [ OPTION => VALUE ... ] Runs a realtime credit card, ACH (electronic check) or phone bill transaction @@ -1504,6 +1556,10 @@ sub realtime_bop { ( $content{account_number}, $content{routing_code} ) = split('@', $self->payinfo); $content{bank_name} = $self->payname; + $content{account_type} = 'CHECKING'; + $content{account_name} = $payname; + $content{customer_org} = $self->company ? 'B' : 'I'; + $content{customer_ssn} = $self->ss; } elsif ( $method eq 'LEC' ) { $content{phone} = $self->payinfo; } @@ -2501,4 +2557,3 @@ L, L, schema.html from the base documentation. 1; - diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm index 8ac806519..c0cb6f4e9 100644 --- a/FS/FS/cust_svc.pm +++ b/FS/FS/cust_svc.pm @@ -1,7 +1,7 @@ package FS::cust_svc; use strict; -use vars qw( @ISA ); +use vars qw( @ISA $ignore_quantity ); use Carp qw( cluck ); use FS::Record qw( qsearch qsearchs dbh ); use FS::cust_pkg; @@ -17,6 +17,8 @@ use FS::part_export; @ISA = qw( FS::Record ); +$ignore_quantity = 0; + sub _cache { my $self = shift; my ( $hashref, $cache ) = @_; @@ -229,7 +231,7 @@ sub check { }); return "Already ". scalar(@cust_svc). " ". $part_svc->svc. " services for pkgnum ". $self->pkgnum - if scalar(@cust_svc) >= $quantity; + if scalar(@cust_svc) >= $quantity && (!$ignore_quantity || !$quantity); } ''; #no error diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm index 9a1b9d864..ff519969d 100644 --- a/FS/FS/part_export.pm +++ b/FS/FS/part_export.pm @@ -548,7 +548,7 @@ tie my %shellcommands_options, 'Tie::IxHash', #'machine' => { label=>'Remote machine' }, 'user' => { label=>'Remote username', default=>'root' }, 'useradd' => { label=>'Insert command', - default=>'useradd -d $dir -m -s $shell -u $uid -p $crypt_password $username' + default=>'useradd -c $finger -d $dir -m -s $shell -u $uid -p $crypt_password $username' #default=>'cp -pr /etc/skel $dir; chown -R $uid.$gid $dir' }, 'useradd_stdin' => { label=>'Insert command STDIN', @@ -564,7 +564,7 @@ tie my %shellcommands_options, 'Tie::IxHash', default=>'', }, 'usermod' => { label=>'Modify command', - default=>'usermod -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username', + default=>'usermod -c $new_finger -d $new_dir -m -l $new_username -s $new_shell -u $new_uid -p $new_crypt_password $old_username', #default=>'[ -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; '. @@ -576,6 +576,9 @@ tie my %shellcommands_options, 'Tie::IxHash', type =>'textarea', default=>'', }, + 'usermod_pwonly' => { label=>'Disallow username changes', + type =>'checkbox', + }, 'suspend' => { label=>'Suspension command', default=>'', }, @@ -613,6 +616,9 @@ tie my %shellcommands_withdomain_options, 'Tie::IxHash', type =>'textarea', #default=>"$_password\n$_password\n", }, + 'usermod_pwonly' => { label=>'Disallow username changes', + type =>'checkbox', + }, 'suspend' => { label=>'Suspension command', default=>'', }, @@ -685,6 +691,12 @@ tie my %sqlradius_options, 'Tie::IxHash', 'password' => { label=>'Database password' }, ; +tie my %sqlradius_withdomain_options, 'Tie::IxHash', + 'datasrc' => { label=>'DBI data source ' }, + 'username' => { label=>'Database username' }, + 'password' => { label=>'Database password' }, +; + tie my %cyrus_options, 'Tie::IxHash', 'server' => { label=>'IMAP server' }, 'username' => { label=>'Admin username' }, @@ -692,7 +704,6 @@ tie my %cyrus_options, 'Tie::IxHash', ; tie my %cp_options, 'Tie::IxHash', - 'host' => { label=>'Hostname' }, 'port' => { label=>'Port number' }, 'username' => { label=>'Username' }, 'password' => { label=>'Password' }, @@ -876,9 +887,9 @@ tie my %forward_shellcommands_options, 'Tie::IxHash', }, 'shellcommands_withdomain' => { - 'desc' => 'Real-time export via remote SSH.', + 'desc' => 'Real-time export via remote SSH (vpopmail, etc.).', 'options' => \%shellcommands_withdomain_options, - 'notes' => 'Run remote commands via SSH. username@domain (rather than just usernames) are considered unique (also see shellcommands). You probably want this if the commands you are running will accept a domain as a parameter, and will allow the same username with different domains. You will need to setup SSH for unattended operation.

The following variables are available for interpolation (prefixed with new_ or old_ for replace operations):
  • $username
  • $domain
  • $_password
  • $quoted_password - unencrypted password quoted for the shell
  • $crypt_password - encrypted password
  • $uid
  • $gid
  • $finger - GECOS, already quoted for the shell (do not add additional quotes)
  • $dir - home directory
  • $shell
  • $quota
  • All other fields in svc_acct are also available.
', + 'notes' => 'Run remote commands via SSH. username@domain (rather than just usernames) are considered unique (also see shellcommands). You probably want this if the commands you are running will accept a domain as a parameter, and will allow the same username with different domains. You will need to setup SSH for unattended operation.

Use these buttons for some useful presets:
The following variables are available for interpolation (prefixed with new_ or old_ for replace operations):
  • $username
  • $domain
  • $_password
  • $quoted_password - unencrypted password quoted for the shell
  • $crypt_password - encrypted password
  • $uid
  • $gid
  • $finger - GECOS, already quoted for the shell (do not add additional quotes)
  • $dir - home directory
  • $shell
  • $quota
  • All other fields in svc_acct are also available.
', }, 'ldap' => { @@ -891,7 +902,14 @@ tie my %forward_shellcommands_options, 'Tie::IxHash', 'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS)', 'options' => \%sqlradius_options, 'nodomain' => 'Y', - 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for FreeRADIUS or ICRADIUS. An existing RADIUS database will be updated in realtime, but you can use freeside-sqlradius-reset to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the DBI documentation and the documentation for your DBD for the exact syntax of a DBI data source. If using FreeRADIUS 0.5 or above, make sure your op fields are set to allow NULL values.', + 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for FreeRADIUS or ICRADIUS. This export does not export RADIUS realms (see also sqlradius_withdomain). An existing RADIUS database will be updated in realtime, but you can use freeside-sqlradius-reset to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the DBI documentation and the documentation for your DBD for the exact syntax of a DBI data source.', + }, + + 'sqlradius_withdomain' => { + 'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS) with realms', + 'options' => \%sqlradius_withdomain_options, + 'nodomain' => '', + 'notes' => 'Real-time export of radcheck, radreply and usergroup tables to any SQL database for FreeRADIUS or ICRADIUS. This export exports domains to RADIUS realms (see also sqlradius). An existing RADIUS database will be updated in realtime, but you can use freeside-sqlradius-reset to delete the entire RADIUS database and repopulate the tables from the Freeside database. See the DBI documentation and the documentation for your DBD for the exact syntax of a DBI data source.', }, 'sqlmail' => { diff --git a/FS/FS/part_export/cp.pm b/FS/FS/part_export/cp.pm index d998c1d95..c4750dd5d 100644 --- a/FS/FS/part_export/cp.pm +++ b/FS/FS/part_export/cp.pm @@ -10,10 +10,10 @@ sub rebless { shift; } sub _export_insert { my( $self, $svc_acct ) = (shift, shift); $self->cp_queue( $svc_acct->svcnum, 'create_mailbox', - Mailbox => $svc_acct->username, - Password => $svc_acct->_password, - Workgroup => $self->option('workgroup'), - Domain => $svc_acct->domain, + 'Mailbox' => $svc_acct->username, + 'Password' => $svc_acct->_password, + 'Workgroup' => $self->option('workgroup'), + 'Domain' => $svc_acct->domain, ); } @@ -30,8 +30,30 @@ sub _export_replace { sub _export_delete { my( $self, $svc_acct ) = (shift, shift); $self->cp_queue( $svc_acct->svcnum, 'delete_mailbox', - Mailbox => $svc_acct->username, - Domain => $svc_acct->domain, + 'Mailbox' => $svc_acct->username, + 'Domain' => $svc_acct->domain, + ); +} + +sub _export_suspend { + my( $self, $svc_acct ) = (shift, shift); + $self->cp_queue( $svc_acct->svcnum, 'set_mailbox_status', + 'Mailbox' => $svc_acct->username, + 'Domain' => $svc_acct->domain, + 'OTHER' => 'T', + 'OTHER_SUSPEND' => 'T', + ); +} + +sub _export_unsuspend { + my( $self, $svc_acct ) = (shift, shift); + $self->cp_queue( $svc_acct->svcnum, 'set_mailbox_status', + 'Mailbox' => $svc_acct->username, + 'Domain' => $svc_acct->domain, + 'PAYMENT' => 'F', + 'OTHER' => 'F', + 'OTHER_SUSPEND' => 'F', + 'OTHER_BOUNCE' => 'F', ); } @@ -42,7 +64,7 @@ sub cp_queue { 'job' => 'FS::part_export::cp::cp_command', }; $queue->insert( - $self->option('host'), + $self->machine, $self->option('port'), $self->option('username'), $self->option('password'), @@ -69,20 +91,22 @@ sub cp_command { #subroutine, not method ); } - my $other = 'F'; + #my $other = 'F'; if ( $new_password =~ /^\*SUSPENDED\* (.*)$/ ) { $new_password = $1; - $other = 'T'; + # $other = 'T'; } - cp_command($host, $port, $username, $password, 'set_mailbox_status', - Domain => $domain, - Mailbox => $new_username, - Other => $other, - Other_Bounce => $other, - ); + #cp_command($host, $port, $username, $password, $login_domain, + # 'set_mailbox_status', + # Domain => $domain, + # Mailbox => $new_username, + # Other => $other, + # Other_Bounce => $other, + #); if ( $old_password ne $new_password ) { - cp_command($host, $port, $username, $password, 'change_mailbox', + cp_command($host, $port, $username, $password, $login_domain, + 'change_mailbox', Domain => $domain, Mailbox => $new_username, Password => $new_password, diff --git a/FS/FS/part_export/forward_shellcommands.pm b/FS/FS/part_export/forward_shellcommands.pm index 43d558a69..f6fcb6062 100644 --- a/FS/FS/part_export/forward_shellcommands.pm +++ b/FS/FS/part_export/forward_shellcommands.pm @@ -29,13 +29,13 @@ sub _export_command { ${$_} = $svc_forward->getfield($_) foreach $svc_forward->fields; } - my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } ); + my $svc_acct = $svc_forward->srcsvc_acct; $username = $svc_acct->username; $domain = $svc_acct->domain; - if ($self->dstsvc) { - $destination = $self->dstsvc_acct->email; + if ($svc_forward->dstsvc_acct) { + $destination = $svc_forward->dstsvc_acct->email; } else { - $destination = $self->dst; + $destination = $svc_forward->dst; } #done setting variables for the command @@ -59,22 +59,22 @@ sub _export_replace { ${"new_$_"} = $new->getfield($_) foreach $new->fields; } - my $old_svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } ); + my $old_svc_acct = $old->srcsvc_acct; $old_username = $old_svc_acct->username; $old_domain = $old_svc_acct->domain; - if ($self->dstsvc) { - $old_destination = $self->dstsvc_acct->email; + if ($old->dstsvc_acct) { + $old_destination = $old->dstsvc_acct->email; } else { - $old_destination = $self->dst; + $old_destination = $old->dst; } - my $new_svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $self->srcsvc } ); + my $new_svc_acct = $new->srcsvc_acct; $new_username = $new_svc_acct->username; $new_domain = $new_svc_acct->domain; - if ($self->dstsvc) { - $new_destination = $self->dstsvc_acct->email; + if ($new->dstsvc) { + $new_destination = $new->dstsvc_acct->email; } else { - $new_destination = $self->dst; + $new_destination = $new->dst; } #done setting variables for the command diff --git a/FS/FS/part_export/shellcommands.pm b/FS/FS/part_export/shellcommands.pm index f592a838a..edc944009 100644 --- a/FS/FS/part_export/shellcommands.pm +++ b/FS/FS/part_export/shellcommands.pm @@ -75,12 +75,24 @@ sub _export_replace { ${"new_$_"} = $new->getfield($_) foreach $new->fields; } $new_finger = shell_quote $new_finger; - $quoted_new__password = shell_quote $new__password; + $quoted_new__password = shell_quote $new__password; #old, wrong? + $new_quoted_password = shell_quote $new__password; #new, better? $old_domain = $old->domain; $new_domain = $new->domain; $new_crypt_password = ''; #surpress "used only once" warnings $new_crypt_password = crypt( $new->_password, $saltset[int(rand(64))].$saltset[int(rand(64))]); + if ( $self->option('usermod_pwonly') ) { + my $error = ''; + if ( $old_username ne $new_username ) { + $error ||= "can't change username"; + } + if ( $old_domain ne $new_domain ) { + $error ||= "can't change domain"; + } + return $error. ' ('. $self->exporttype. ' to '. $self->machine. ')' + if $error; + } $self->shellcommands_queue( $new->svcnum, user => $self->option('user')||'root', host => $self->machine, diff --git a/FS/FS/part_export/sqlmail.pm b/FS/FS/part_export/sqlmail.pm index 64f72df07..8ccad3c7e 100644 --- a/FS/FS/part_export/sqlmail.pm +++ b/FS/FS/part_export/sqlmail.pm @@ -1,9 +1,10 @@ package FS::part_export::sqlmail; use vars qw(@ISA); +use Digest::MD5 qw(md5_hex); use FS::Record qw(qsearchs); use FS::part_export; -use Digest::MD5 qw(md5_hex); +use FS::svc_domain; @ISA = qw(FS::part_export); diff --git a/FS/FS/part_export/sqlradius.pm b/FS/FS/part_export/sqlradius.pm index 60450ee63..8a8f9beba 100644 --- a/FS/FS/part_export/sqlradius.pm +++ b/FS/FS/part_export/sqlradius.pm @@ -8,6 +8,11 @@ use FS::part_export; sub rebless { shift; } +sub export_username { + my($self, $svc_acct) = (shift, shift); + $svc_acct->username; +} + sub _export_insert { my($self, $svc_acct) = (shift, shift); @@ -16,14 +21,14 @@ sub _export_insert { my %attrib = $svc_acct->$method(); next unless keys %attrib; my $err_or_queue = $self->sqlradius_queue( $svc_acct->svcnum, 'insert', - $table, $svc_acct->username, %attrib ); + $table, $self->export_username($svc_acct), %attrib ); return $err_or_queue unless ref($err_or_queue); } my @groups = $svc_acct->radius_groups; if ( @groups ) { my $err_or_queue = $self->sqlradius_queue( $svc_acct->svcnum, 'usergroup_insert', - $svc_acct->username, @groups ); + $self->export_username($svc_acct), @groups ); return $err_or_queue unless ref($err_or_queue); } ''; @@ -44,9 +49,9 @@ sub _export_replace { my $dbh = dbh; my $jobnum = ''; - if ( $old->username ne $new->username ) { + if ( $self->export_username($old) ne $self->export_username($new) ) { my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'rename', - $new->username, $old->username ); + $self->export_username($new), $self->export_username($old) ); unless ( ref($err_or_queue) ) { $dbh->rollback if $oldAutoCommit; return $err_or_queue; @@ -63,7 +68,7 @@ sub _export_replace { } keys %new ) { my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'insert', - $table, $new->username, %new ); + $table, $self->export_username($new), %new ); unless ( ref($err_or_queue) ) { $dbh->rollback if $oldAutoCommit; return $err_or_queue; @@ -80,7 +85,7 @@ sub _export_replace { my @del = grep { !exists $new{$_} } keys %old; if ( @del ) { my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'attrib_delete', - $table, $new->username, @del ); + $table, $self->export_username($new), @del ); unless ( ref($err_or_queue) ) { $dbh->rollback if $oldAutoCommit; return $err_or_queue; @@ -109,7 +114,7 @@ sub _export_replace { if ( @delgroups ) { my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'usergroup_delete', - $new->username, @delgroups ); + $self->export_username($new), @delgroups ); unless ( ref($err_or_queue) ) { $dbh->rollback if $oldAutoCommit; return $err_or_queue; @@ -125,7 +130,7 @@ sub _export_replace { if ( @newgroups ) { my $err_or_queue = $self->sqlradius_queue( $new->svcnum, 'usergroup_insert', - $new->username, @newgroups ); + $self->export_username($new), @newgroups ); unless ( ref($err_or_queue) ) { $dbh->rollback if $oldAutoCommit; return $err_or_queue; @@ -147,7 +152,7 @@ sub _export_replace { sub _export_delete { my( $self, $svc_acct ) = (shift, shift); my $err_or_queue = $self->sqlradius_queue( $svc_acct->svcnum, 'delete', - $svc_acct->username ); + $self->export_username($svc_acct) ); ref($err_or_queue) ? '' : $err_or_queue; } diff --git a/FS/FS/part_export/sqlradius_withdomain.pm b/FS/FS/part_export/sqlradius_withdomain.pm new file mode 100644 index 000000000..1c8f38c9d --- /dev/null +++ b/FS/FS/part_export/sqlradius_withdomain.pm @@ -0,0 +1,12 @@ +package FS::part_export::sqlradius_withdomain; + +use vars qw(@ISA); +use FS::part_export::sqlradius; + +@ISA = qw(FS::part_export::sqlradius); + +sub export_username { + my($self, $svc_acct) = (shift, shift); + $svc_acct->email; +} + diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm index 60b0e01f9..6525864c4 100644 --- a/FS/FS/part_pkg.pm +++ b/FS/FS/part_pkg.pm @@ -267,11 +267,12 @@ SVCDB is specified and does not match the svcdb of the service definition, sub svcpart { my $self = shift; - my $svcdb = shift; - my @pkg_svc = $self->pkg_svc; - return '' if scalar(@pkg_svc) != 1 - || $pkg_svc[0]->quantity != 1 - || ( $svcdb && $pkg_svc[0]->part_svc->svcdb ne $svcdb ); + my $svcdb = scalar(@_) ? shift : ''; + my @pkg_svc = grep { + $_->quantity == 1 + && ( $svcdb eq $_->part_svc->svcdb || !$svcdb ) + } $self->pkg_svc; + return '' if scalar(@pkg_svc) != 1; $pkg_svc[0]->svcpart; } diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index 497e59c94..5b8107fc8 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -9,7 +9,7 @@ use vars qw( @ISA $DEBUG $me $conf $username_uppercase $welcome_template $welcome_from $welcome_subject $welcome_mimetype $smtpmachine - $radius_password + $radius_password $radius_ip $dirhash @saltset @pw_set ); use Carp; @@ -68,6 +68,7 @@ $FS::UID::callback{'FS::svc_acct'} = sub { } $smtpmachine = $conf->config('smtpmachine'); $radius_password = $conf->config('radius-password') || 'Password'; + $radius_ip = $conf->config('radius-ip') || 'Framed-IP-Address'; }; @saltset = ( 'a'..'z' , 'A'..'Z' , '0'..'9' , '.' , '/' ); @@ -603,6 +604,8 @@ error, returns the error, otherwise returns false. Called by the suspend method of FS::cust_pkg (see L). +Calls any export-specific suspend hooks. + =cut sub suspend { @@ -627,6 +630,8 @@ an error, returns the error, otherwise returns false. Called by the unsuspend method of FS::cust_pkg (see L). +Calls any export-specific unsuspend hooks. + =cut sub unsuspend { @@ -783,12 +788,14 @@ sub check { $recref->{quota} = $1; unless ( $part_svc->part_svc_column('slipip')->columnflag eq 'F' ) { - unless ( $recref->{slipip} eq '0e0' ) { + if ( $recref->{slipip} eq '' ) { + $recref->{slipip} = ''; + } elsif ( $recref->{slipip} eq '0e0' ) { + $recref->{slipip} = '0e0'; + } else { $recref->{slipip} =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ or return "Illegal slipip: ". $self->slipip; $recref->{slipip} = $1; - } else { - $recref->{slipip} = '0e0'; } } @@ -860,7 +867,7 @@ sub radius_reply { ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) ); } grep { /^radius_/ && $self->getfield($_) } fields( $self->table ); if ( $self->slipip && $self->slipip ne '0e0' ) { - $reply{'Framed-IP-Address'} = $self->slipip; + $reply{$radius_ip} = $self->slipip; } %reply; } diff --git a/FS/FS/svc_acct_pop.pm b/FS/FS/svc_acct_pop.pm index 3c9ea0130..196ab7ebb 100644 --- a/FS/FS/svc_acct_pop.pm +++ b/FS/FS/svc_acct_pop.pm @@ -142,8 +142,7 @@ sub popselector { function popstate_changed(what) { state = what.options[what.selectedIndex].text; - for (var i = what.form.popnum.length;i > 0;i--) - what.form.popnum.options[i] = null; + what.form.popnum.options.length = 0 what.form.popnum.options[0] = new Option("", "", false, true); END @@ -167,7 +166,13 @@ END $text .= ''; #callback? return 3 html pieces? #''; $text .= qq!' unless @$pops; return $pops->[0]{city}. ', '. $pops->[0]{state}. - ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. + ' ('. $pops->[0]{ac}. ')/'. $pops->[0]{exch}. '-'. $pops->[0]{loc}. '' if scalar(@$pops) == 1; - my %pop = (); - foreach (@$pops) { - push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_; - } + #my %pop = (); + #my %popnum2pop = (); + #foreach (@$pops) { + # push @{ $pop{ $_->{state} }->{ $_->{ac} } }, $_; + # $popnum2pop{$_->{popnum}} = $_; + #} my $text = < @@ -398,16 +402,23 @@ sub popselector { var length = what.length; what.options[length] = optionName; } +END - function acstate_changed(what) { - state = what.options[what.selectedIndex].text; - for (var i = what.form.popac.length;i > 0;i--) - what.form.popac.options[i] = null; - what.form.popac.options[0] = new Option("Area code", "-1", false, true); + if ( $init_popstate ) { + $text .= ''; + } else { + $text .= < 0;i--) - what.form.popnum.options[i] = null; + what.form.popnum.options.length = 0; what.form.popnum.options[0] = new Option("City", "-1", false, true); END - foreach my $state ( keys %pop ) { + foreach my $state ( @states ) { foreach my $popac ( keys %{ $pop{$state} } ) { $text .= "\nif ( ac == \"$popac\" ) {\n"; foreach my $pop ( @{$pop{$state}->{$popac}}) { my $o_popnum = $pop->{popnum}; my $poptext = $pop->{city}. ', '. $pop->{state}. - ' ('. $pop->{ac}. ')/'. $pop->{exch}; + ' ('. $pop->{ac}. ')/'. $pop->{exch}. '-'. $pop->{loc}; $text .= "opt(what.form.popnum, \"$o_popnum\", \"$poptext\");\n"; if ($popnum == $o_popnum) { @@ -453,7 +463,7 @@ END qq!'; $text .= @@ -462,12 +472,19 @@ END $text .= qq!
'; #callback? return 3 html pieces? #'
!; diff --git a/fs_signup/FS-SignupClient/cgi/stateselect.html b/fs_signup/FS-SignupClient/cgi/stateselect.html new file mode 100644 index 000000000..39823be83 --- /dev/null +++ b/fs_signup/FS-SignupClient/cgi/stateselect.html @@ -0,0 +1,80 @@ +ISP Signup +ISP Signup - state selection

+ +
+Select your state: + +
+ + diff --git a/fs_signup/fs_signup_server b/fs_signup/fs_signup_server index 36af40a57..d6eb4a8d5 100755 --- a/fs_signup/fs_signup_server +++ b/fs_signup/fs_signup_server @@ -162,7 +162,7 @@ while (1) { my $part_pkg = qsearchs( 'part_pkg', { 'pkgpart' => $pkgpart } ) or $error ||= "WARNING: unknown pkgpart: $pkgpart"; - my $svcpart = $part_pkg->svcpart unless $error; + my $svcpart = $part_pkg->svcpart('svc_acct') unless $error; my $cust_pkg = new FS::cust_pkg ( { #later#'custnum' => $custnum, diff --git a/htetc/global.asa b/htetc/global.asa index 5fd89957f..d87f1eac6 100644 --- a/htetc/global.asa +++ b/htetc/global.asa @@ -110,8 +110,12 @@ sub Script_OnFlush { my $ref = $Response->{BinaryRef}; #$$ref = $cgi->header( @FS::CGI::header ) . $$ref; #$$ref = $cgi->header() . $$ref; - if ( dbh->can('sprintProfile') ) { - if ( lc($Response->{ContentType}) eq 'text/html' ) { + #warn "Script_OnFlush called with dbh ". dbh. "\n"; + #if ( dbh->can('sprintProfile') ) { + if ( UNIVERSAL::can(dbh,'sprintProfile') ) { + #warn "dbh can sprintProfile\n"; + if ( lc($Response->{ContentType}) eq 'text/html' ) { #con + #warn "contenttype is sprintProfile\n"; $$ref =~ s/<\/BODY>[\s\n]*<\/HTML>[\s\n]*$//i or warn "can't remove"; @@ -126,7 +130,9 @@ sub Script_OnFlush { } } -if ( defined(@DBIx::Profile::ISA) && DBIx::Profile::db->can('sprintProfile') ) { +#if ( defined(@DBIx::Profile::ISA) && DBIx::Profile::db->can('sprintProfile') ) { +#if ( defined(@DBIx::Profile::ISA) && UNIVERSAL::can('DBIx::Profile::db', 'sprintProfile') ) { +if ( defined(@DBIx::Profile::ISA) ) { #warn "enabling profiling redirects"; *CGI::redirect = sub { diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi index 58422c67d..7b9436cee 100755 --- a/httemplate/browse/part_pkg.cgi +++ b/httemplate/browse/part_pkg.cgi @@ -11,15 +11,35 @@ if ( $cgi->param('showdisabled') ) { my @part_pkg = qsearch('part_pkg', \%search ); my $total = scalar(@part_pkg); +my $sortby; +my %num_active_cust_pkg; +if ( $cgi->param('active') ) { + my $active_sth = dbh->prepare( + 'SELECT COUNT(*) FROM cust_pkg WHERE pkgpart = ?'. + ' AND ( cancel IS NULL OR cancel = 0 )'. + ' AND ( susp IS NULL OR susp = 0 )' + ) or die dbh->errstr; + foreach my $part_pkg ( @part_pkg ) { + $active_sth->execute($part_pkg->pkgpart) or die $active_sth->errstr; + $num_active_cust_pkg{$part_pkg->pkgpart} = + $active_sth->fetchrow_arrayref->[0]; + } + $sortby = \*active_cust_pkg_sort; +} else { + $sortby = \*pkgpart_sort; +} + %> <%= header("Package Definition Listing",menubar( 'Main Menu' => $p )) %> -One or more services are grouped together into a package and given pricing -information. Customers purchase packages rather than purchase services -directly.

-Add a new package definition -

+<% unless ( $cgi->param('active') ) { %> + One or more service definitions are grouped together into a package + definition and given pricing information. Customers purchase packages + rather than purchase services directly.

+ Add a new package definition +

+<% } %> -<%= $total %> packages +<%= $total %> package definitions <% if ( $cgi->param('showdisabled') ) { $cgi->param('showdisabled', 0); @@ -34,6 +54,10 @@ print &table(), < Package Comment +END +print ' Customer
packages
' + if $cgi->param('active'); +print <Freq. Plan Data @@ -42,9 +66,7 @@ print &table(), < END -foreach my $part_pkg ( sort { - $a->getfield('pkgpart') <=> $b->getfield('pkgpart') -} @part_pkg ) { +foreach my $part_pkg ( sort $sortby @part_pkg ) { my($hashref)=$part_pkg->hashref; my(@pkg_svc)=grep $_->getfield('quantity'), qsearch('pkg_svc',{'pkgpart'=> $hashref->{pkgpart} }); @@ -73,6 +95,16 @@ END print <$hashref->{pkg} $hashref->{comment} +END + if ( $cgi->param('active') ) { + print " "; + print ''. + $num_active_cust_pkg{$hashref->{'pkgpart'}}. + qq! active!; + # suspended/cancelled + print ''; + } + print <$hashref->{freq} $hashref->{plan} $plandata @@ -99,4 +131,14 @@ print < 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}; +} + %> diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi index ee7a2622a..7c83924a2 100755 --- a/httemplate/browse/part_svc.cgi +++ b/httemplate/browse/part_svc.cgi @@ -23,7 +23,7 @@ function part_export_areyousure(href) { } - Services are items you offer to your customers.

+ Service definitions are the templates for items you offer to your customers.

Add a new service definition<% if ( @part_svc ) { %> or '; } elsif ( $type eq 'textarea' ) { - $html .= qq!!; } elsif ( $type eq 'text' ) { $html .= qq!!; } elsif ( $type eq 'checkbox' ) { - $html .= qq!!; + $html .= qq!Transfer!; print <Domain +

Domain
Purpose/Description:

diff --git a/httemplate/index.html b/httemplate/index.html index e5bd11806..017ffcd88 100644 --- a/httemplate/index.html +++ b/httemplate/index.html @@ -67,7 +67,8 @@
  • 120 day open invoices (by invoice number) (by date) (by customer number)
  • all invoices (by invoice number) (by date) (by customer number) - Financial reports + Payment report (by type and/or date range) +

    Financial reports - Invoices + Package definitions (by number of active packages) +

    Invoices - Financial reports + Payment Report (by type and/or date range) +

    Financial reports
    • current receivables
    • tax reports diff --git a/httemplate/misc/bill.cgi b/httemplate/misc/bill.cgi index f048e5559..44d85b880 100755 --- a/httemplate/misc/bill.cgi +++ b/httemplate/misc/bill.cgi @@ -21,7 +21,8 @@ unless ( $error ) { #'batch_card'=> 'yes', #'batch_card'=> 'no', #'report_badcard'=> 'yes', - 'retry_card' => 'yes', + #'retry_card' => 'yes', + 'retry' => 'yes', ); } #&eidiot($error) if $error; diff --git a/httemplate/search/cust_bill_event.cgi b/httemplate/search/cust_bill_event.cgi index 9cb36d28e..b76f66b76 100644 --- a/httemplate/search/cust_bill_event.cgi +++ b/httemplate/search/cust_bill_event.cgi @@ -4,7 +4,7 @@ #false laziness with view/cust_bill.cgi $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/; -my $beginning = str2time($1); +my $beginning = str2time($1) || 0; $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/; my $ending = str2time($1) + 86400; diff --git a/httemplate/search/cust_pay.cgi b/httemplate/search/cust_pay.cgi index b5bdf8296..7a983703f 100755 --- a/httemplate/search/cust_pay.cgi +++ b/httemplate/search/cust_pay.cgi @@ -1,12 +1,47 @@ <% -$cgi->param('payinfo') =~ /^\s*(\d+)\s*$/ or die "illegal payinfo"; -my $payinfo = $1; -$cgi->param('payby') =~ /^(\w+)$/ or die "illegal payby"; -my $payby = $1; -my @cust_pay = qsearch('cust_pay', { 'payinfo' => $payinfo, +my $sortby; +my @cust_pay; +if ( $cgi->param('magic') && $cgi->param('magic') eq '_date' ) { + + my %search; + if ( $cgi->param('payby') ) { + $cgi->param('payby') =~ /^(CARD|CHEK|BILL)$/ + or die "illegal payby ". $cgi->param('payby'); + $search{'payby'} = $1; + } + + #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 "; + } + if ( $cgi->param('ending') + && $cgi->param('ending') =~ /^([ 0-9\-\/]{0,10})$/ ) { + my $ending = str2time($1) + 86400; + $range .= ( $range ? ' AND ' : ' WHERE ' ). " _date <= $ending "; + } + $range =~ s/^\s*WHERE/ AND/ if scalar(keys %search) ; + + @cust_pay = qsearch('cust_pay', \%search, '', $range ); + + $sortby = \*date_sort; + +} else { + + $cgi->param('payinfo') =~ /^\s*(\d+)\s*$/ or die "illegal payinfo"; + my $payinfo = $1; + + $cgi->param('payby') =~ /^(\w+)$/ or die "illegal payby"; + my $payby = $1; + + @cust_pay = qsearch('cust_pay', { 'payinfo' => $payinfo, 'payby' => $payby } ); -my $sortby = \*date_sort; + $sortby = \*date_sort; + +} if (0) { #if ( scalar(@cust_pay) == 1 ) { @@ -16,7 +51,7 @@ if (0) { %> <% - idiot("Check # not found."); + idiot("Payment not found."); #exit; } else { my $total = scalar(@cust_pay); @@ -24,9 +59,9 @@ if (0) { %> <% - print header("Check # Search Results", menubar( + print header("Payment Search Results", menubar( 'Main Menu', popurl(2) - )), "$total matching check$s found
      ", &table(), <", &table(), < Amount @@ -40,23 +75,36 @@ END foreach my $cust_pay ( sort $sortby grep(!$saw{$_->paynum}++, @cust_pay) ) { - my($paynum, $custnum, $payinfo, $amount, $date ) = ( + my($paynum, $custnum, $payby, $payinfo, $amount, $date ) = ( $cust_pay->paynum, $cust_pay->custnum, + $cust_pay->payby, $cust_pay->payinfo, sprintf("%.2f", $cust_pay->paid), $cust_pay->_date, ); - my $pdate = time2str("%b %d %Y", $date); + my $pdate = time2str("%b %d %Y", $date); my $rowspan = 1; my $view = popurl(2). "view/cust_main.cgi?". $custnum. "#". $payby. $payinfo; + my $payment_info; + if ( $payby eq 'CARD' ) { + $payment_info = 'Card #'. 'x'x(length($payinfo)-4). + substr($payinfo,(length($payinfo)-4)); + } elsif ( $payby eq 'CHEK' ) { + $payment_info = "E-check acct#$payinfo"; + } elsif ( $payby eq 'BILL' ) { + $payment_info = "Check #$payinfo"; + } else { + $payment_info = "$payby $payinfo"; + } + print < - $payinfo + $payment_info \$$amount $pdate END diff --git a/httemplate/search/cust_pkg.cgi b/httemplate/search/cust_pkg.cgi index 538edf3f1..8b2fd0ca0 100755 --- a/httemplate/search/cust_pkg.cgi +++ b/httemplate/search/cust_pkg.cgi @@ -19,6 +19,8 @@ my @cust_pkg; if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) { $sortby=\*bill_sort; + + #false laziness with cust_pay.cgi my $range = ''; if ( $cgi->param('beginning') && $cgi->param('beginning') =~ /^([ 0-9\-\/]{0,10})$/ ) { @@ -44,7 +46,19 @@ if ( $cgi->param('magic') && $cgi->param('magic') eq 'bill' ) { } else { my $qual = ''; - if ( $query eq 'pkgnum' ) { + if ( $cgi->param('magic') && $cgi->param('magic') eq 'active' ) { + + $qual = 'WHERE ( susp IS NULL OR susp = 0 )'. + ' AND ( cancel IS NULL OR cancel = 0)'; + + $sortby = \*pkgnum_sort; + + if ( $cgi->param('pkgpart') =~ /^(\d+)$/ ) { + $qual .= " AND pkgpart = $1"; + } + + } elsif ( $query eq 'pkgnum' ) { + $sortby=\*pkgnum_sort; } elsif ( $query eq 'SUSP_pkgnum' ) { @@ -196,7 +210,7 @@ if ( scalar(@cust_pkg) == 1 ) { Setup END - print 'Next
      bill
      ' + print 'Last
      bill
      ' if defined dbdef->table('cust_pkg')->column('last_bill'); print < + + Payment report criteria + + +
      +

      Payment report criteria

      +
      +
      +
      + + Return payments for period
      + from m/d/y + to m/d/y +

      +

      +
      + + diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi index 3934a3dcb..c36c9e265 100755 --- a/httemplate/view/cust_main.cgi +++ b/httemplate/view/cust_main.cgi @@ -20,7 +20,7 @@ print < END @@ -290,7 +290,7 @@ if ( $conf->config('payby-default') ne 'HIDE' ) { print ''; if ( defined $cust_main->dbdef_table->column('comments') - && $cust_main->comments ) + && $cust_main->comments =~ /[^\s\n\r]/ ) { print "
      Comments". &ntable("#cccccc"). "". &ntable("#cccccc",2). @@ -430,7 +430,7 @@ foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) { if ( $pkg->{cancel} ) { #status: cancelled - print 'Cancelled '. + print 'Cancelled '. ''. pkg_datestr($pkg,'cancel'). ''; unless ( $pkg->{setup} ) { print 'Never billed'; @@ -848,9 +848,12 @@ sub svc_label_link { sub svc_provision_link { my ($pkg, $svcpart) = (shift,shift) or return ''; - return qq!! . - qq!Provision $svcpart->{svc} (! . ($svcpart->{quantity} - $svcpart->{count}) . qq!)!; + "Provision $svc_nbsp (". + ($svcpart->{quantity} - $svcpart->{count}). + ')'; } sub svc_unprovision_link {