diff options
Diffstat (limited to 'bin')
-rwxr-xr-x | bin/23diff | 2 | ||||
-rwxr-xr-x | bin/cdr-netsapiens.import | 1 | ||||
-rwxr-xr-x | bin/cust_main-bulk_change | 54 | ||||
-rwxr-xr-x | bin/fs-migrate-supplemental | 151 | ||||
-rwxr-xr-x | bin/megapop.pl | 114 |
5 files changed, 310 insertions, 12 deletions
diff --git a/bin/23diff b/bin/23diff index d38c84834..1dc1659d2 100755 --- a/bin/23diff +++ b/bin/23diff @@ -7,7 +7,7 @@ $dir =~ s/freeside(\/?)/freeside2.3$1/; warn $dir; #$cmd = "diff -u $file $dir/$file"; -$cmd = "diff -u $dir/$file $file"; +$cmd = "diff -ubBw $dir/$file $file"; print "$cmd\n"; system($cmd); diff --git a/bin/cdr-netsapiens.import b/bin/cdr-netsapiens.import index 8aa4ac0b7..1cce461e2 100755 --- a/bin/cdr-netsapiens.import +++ b/bin/cdr-netsapiens.import @@ -37,6 +37,7 @@ do { my $ns = $part_export->ns_command( 'GET', '/cdr/', 'time_release' => "$time_release,", '_sort' => '+time_release', + '_limit' => '500', ); #loop over them, double check duplicates, insert the rest diff --git a/bin/cust_main-bulk_change b/bin/cust_main-bulk_change index fdf53d999..32a6d7bd6 100755 --- a/bin/cust_main-bulk_change +++ b/bin/cust_main-bulk_change @@ -1,13 +1,15 @@ #!/usr/bin/perl use strict; -use vars qw( $opt_p ); +use vars qw( $opt_a $opt_p $opt_t $opt_k ); use Getopt::Std; use FS::UID qw(adminsuidsetup); -use FS::Record qw(qsearchs); +use FS::Record qw(qsearch qsearchs); use FS::cust_main; +use FS::cust_tag; +use FS::cust_pkg; -getopts('p:'); +getopts('a:p:t:k:'); my $user = shift or &usage; adminsuidsetup $user; @@ -31,17 +33,41 @@ while (<STDIN>) { next; } - if ( $opt_p ) { - $cust_main->payby($opt_p); + my %cust_tag = ( custnum=>$custnum, tagnum=>$opt_t ); + if ( $opt_t && ! qsearchs('cust_tag', \%cust_tag) ) { + my $cust_tag = new FS::cust_tag \%cust_tag; + my $error = $cust_tag->insert; + die "$error\n" if $error; } - my $error = $cust_main->replace; - die "$error\n" if $error; + if ( $opt_p || $opt_a ) { + $cust_main->agentnum($opt_a) if $opt_a; + $cust_main->payby($opt_p) if $opt_p; + + my $error = $cust_main->replace; + die "$error\n" if $error; + } + + if ( $opt_k ) { + foreach my $k (split(/\s*,\s*/, $opt_k)) { + my($old, $new) = split(/\s*:\s*/, $k); + foreach my $cust_pkg ( qsearch('cust_pkg', { + 'custnum' => $cust_main->custnum, + 'pkgpart' => $old, + }) + ) + { + $cust_pkg->pkgpart($new); + my $error = $cust_pkg->replace; + die "$error\n" if $error; + } + } + } } sub usage { - die "usage: cust_main-bulk_change -p NEW_PAYBY employee_username <custnums.txt\n"; + die "usage: cust_main-bulk_change [ -a agentnum ] [ -p NEW_PAYBY ] [ -t tagnum ] [ -k old_pkgpart:new_pkgpart,... ] employee_username <custnums.txt\n"; } =head1 NAME @@ -50,13 +76,19 @@ cust_main-bulk_change =head1 SYNOPSIS - cust_main-bulk_change -p NEW_PAYBY username <custnums.txt + cust_main-bulk_change [ -a agentnum ] [ -p NEW_PAYBY ] [ -t tagnum ] [ -k old_pkgpart:new_pkgpart,... ] username <custnums.txt =head1 DESCRIPTION -Command-line tool to change the payby field for a group of customers. +Command-line tool to make bulk changes to a group of customers. + +-a: new agentnum + +-p: new payby, for example, I<CARD> or I<DCRD> + +-t: tagnum to add if not present --p: new payby, for example, I<CARD> or I<DCRD>. +-k: old_pkgpart:new_pkgpart, for example, I<5:4>. Multiple entries can be comma-separated. user: Employee username diff --git a/bin/fs-migrate-supplemental b/bin/fs-migrate-supplemental new file mode 100755 index 000000000..dbef95fc1 --- /dev/null +++ b/bin/fs-migrate-supplemental @@ -0,0 +1,151 @@ +#!/usr/bin/perl + +use strict; +use FS::UID qw(adminsuidsetup); +use FS::Record qw(qsearch qsearchs); +use FS::cust_pkg; +use FS::part_pkg; + +my $user = shift or die &usage; +my @pkgparts = @ARGV or die &usage; +my $dbh = adminsuidsetup $user; + +$FS::UID::AutoCommit = 0; + +my %stats = ( + mainpkgs => 0, + created => 0, + linked => 0, + errors => 0, +); + +my %pkg_freq; # cache +foreach my $pkgpart (@pkgparts) { + my $part_pkg = FS::part_pkg->by_key($pkgpart) + or die "pkgpart $pkgpart not found.\n"; + $pkg_freq{$pkgpart} = $part_pkg->freq; + my @links = $part_pkg->supp_part_pkg_link + or die "pkgpart $pkgpart has no supplemental packages.\n"; + CUST_PKG: foreach my $cust_pkg ( + qsearch('cust_pkg', { + 'pkgpart' => $pkgpart, + 'cancel' => '', + }) + ) { + my $cust_main = $cust_pkg->cust_main; + my @existing = $cust_pkg->supplemental_pkgs; + my @active = grep { !$_->main_pkgnum } $cust_main->ncancelled_pkgs; + LINK: foreach my $link (@links) { + # yeah, it's expensive + # see if there's an existing package with this link identity + foreach (@existing) { + if ($_->pkglinknum == $link->pkglinknum) { + next LINK; + } + } + # no? then is there one with this pkgpart? + my $i = 0; + foreach (@active) { + if ( $_->pkgpart == $link->dst_pkgpart ) { + set_link($cust_pkg, $link, $_); + splice(@active, $i, 1); # delete it so we don't reuse it + next LINK; + } + } + # no? then create one + create_linked($cust_pkg, $link); + } #foreach $link + $stats{mainpkgs}++; + } #foreach $cust_pkg +} #foreach $pkgpart + +print " +Main packages: $stats{mainpkgs} +Supplemental packages linked: $stats{linked} +Supplemental packages ordered: $stats{created} +Errors: $stats{errors} +"; + +$dbh->commit or die $dbh->errstr; + +sub set_link { + my ($main_pkg, $part_pkg_link, $supp_pkg) = @_; + my $task = "linking package ".$supp_pkg->pkgnum. + " to package ".$main_pkg->pkgnum; + $supp_pkg->set('main_pkgnum', $main_pkg->pkgnum); + $supp_pkg->set('pkglinknum', $part_pkg_link->pkglinknum); + # Set the next bill date of the supplemental package to the nearest one in + # the future that lines up with the main package. If the main package + # hasn't started billing yet, use its future start date. + my $new_bill = $main_pkg->get('bill') || $main_pkg->get('start_date'); + if ( $new_bill ) { + my $old_bill = $supp_pkg->get('bill'); + my $diff = $new_bill - $old_bill; + my $main_freq = $pkg_freq{$main_pkg->pkgpart}; + my $prev_bill = 0; + while ($diff < 0) { + # this will exit once $new_bill has overtaken the existing bill date. + # if there is no existing bill date, then this will exit right away + # and set bill to the bill date of the main package, which is correct. + $prev_bill = $new_bill; + $new_bill = FS::part_pkg->add_freq($new_bill, $main_freq); + $diff = $new_bill - $old_bill; + } + # then, of $new_bill and $prev_bill, pick the one that's closer to $old_bill + if ( $prev_bill > 0 and + $new_bill - $old_bill > $old_bill - $prev_bill ) { + $supp_pkg->set('bill', $prev_bill); + } else { + $supp_pkg->set('bill', $new_bill); + } + } else { + # otherwise the main package hasn't been billed yet and has no + # start date, so we can't sync the supplemental to it yet. + # but we can still link them. + warn "$task: main package has no next bill date.\n"; + } + my $error = $supp_pkg->replace; + if ( $error ) { + warn "$task:\n $error\n"; + $stats{errors}++; + } else { + $stats{linked}++; + } + return; +} + +sub create_linked { + my ($main_pkg, $part_pkg_link) = @_; + my $task = "creating pkgpart ".$part_pkg_link->dst_pkgpart. + " supplemental to package ".$main_pkg->pkgnum; + my $supp_pkg = FS::cust_pkg->new({ + 'pkgpart' => $part_pkg_link->dst_pkgpart, + 'pkglinknum' => $part_pkg_link->pkglinknum, + 'custnum' => $main_pkg->custnum, + 'main_pkgnum' => $main_pkg->pkgnum, + 'locationnum' => $main_pkg->locationnum, + 'start_date' => $main_pkg->start_date, + 'order_date' => $main_pkg->order_date, + 'expire' => $main_pkg->expire, + 'adjourn' => $main_pkg->adjourn, + 'contract_end' => $main_pkg->contract_end, + 'susp' => $main_pkg->susp, + 'bill' => $main_pkg->bill, + 'refnum' => $main_pkg->refnum, + 'discountnum' => $main_pkg->discountnum, + 'waive_setup' => $main_pkg->waive_setup, + }); + my $error = $supp_pkg->insert; + if ( $error ) { + warn "$task:\n $error\n"; + $stats{errors}++; + } else { + $stats{created}++; + } + return; +} + +sub usage { + die "Usage:\n fs-migrate-supplemental user main_pkgpart\n"; +} + diff --git a/bin/megapop.pl b/bin/megapop.pl new file mode 100755 index 000000000..e2930fb55 --- /dev/null +++ b/bin/megapop.pl @@ -0,0 +1,114 @@ +#!/usr/bin/perl -Tw +# +# this will break when megapop changes the URL or format of their listing page. +# that's stupid. perhaps they can provide a machine-readable listing? + +use strict; +use LWP::UserAgent; +use FS::UID qw(adminsuidsetup); +use FS::svc_acct_pop; + +my $url = "http://www.megapop.com/location.htm"; + +my $user = shift or die &usage; +adminsuidsetup($user); + +my %state2usps = &state2usps; +$state2usps{'WASHINGTON STATE'} = 'WA'; #megapop's on crack +$state2usps{'CANADA'} = 'CANADA'; #freeside's on crack + +my $ua = new LWP::UserAgent; +my $request = new HTTP::Request('GET', $url); +my $response = $ua->request($request); +die $response->error_as_HTML unless $response->is_success; +my $line; +my $usps = ''; +foreach $line ( split("\n", $response->content) ) { + if ( $line =~ /\W(\w[\w\s]*\w)\s+LOCATIONS/i ) { + $usps = $state2usps{uc($1)} + or warn "warning: unknown state $1\n"; + } elsif ( $line =~ /(\d{3})\-(\d{3})\-(\d{4})\s+(\w[\w\s]*\w)/ ) { + print "$1 $2 $3 $4 $usps\n"; + my $svc_acct_pop = new FS::svc_acct_pop ( { + 'city' => $4, + 'state' => $usps, + 'ac' => $1, + 'exch' => $2, + } ); + my $error = $svc_acct_pop->insert; + die $error if $error; + } +} + +sub usage { + die "Usage:\n $0 user\n"; +} + +sub state2usps{ ( + 'ALABAMA' => 'AL', + 'ALASKA' => 'AK', + 'AMERICAN SAMOA' => 'AS', + 'ARIZONA' => 'AZ', + 'ARKANSAS' => 'AR', + 'CALIFORNIA' => 'CA', + 'COLORADO' => 'CO', + 'CONNECTICUT' => 'CT', + 'DELAWARE' => 'DE', + 'DISTRICT OF COLUMBIA' => 'DC', + 'FEDERATED STATES OF MICRONESIA' => 'FM', + 'FLORIDA' => 'FL', + 'GEORGIA' => 'GA', + 'GUAM' => 'GU', + 'HAWAII' => 'HI', + 'IDAHO' => 'ID', + 'ILLINOIS' => 'IL', + 'INDIANA' => 'IN', + 'IOWA' => 'IA', + 'KANSAS' => 'KS', + 'KENTUCKY' => 'KY', + 'LOUISIANA' => 'LA', + 'MAINE' => 'ME', + 'MARSHALL ISLANDS' => 'MH', + 'MARYLAND' => 'MD', + 'MASSACHUSETTS' => 'MA', + 'MICHIGAN' => 'MI', + 'MINNESOTA' => 'MN', + 'MISSISSIPPI' => 'MS', + 'MISSOURI' => 'MO', + 'MONTANA' => 'MT', + 'NEBRASKA' => 'NE', + 'NEVADA' => 'NV', + 'NEW HAMPSHIRE' => 'NH', + 'NEW JERSEY' => 'NJ', + 'NEW MEXICO' => 'NM', + 'NEW YORK' => 'NY', + 'NORTH CAROLINA' => 'NC', + 'NORTH DAKOTA' => 'ND', + 'NORTHERN MARIANA ISLANDS' => 'MP', + 'OHIO' => 'OH', + 'OKLAHOMA' => 'OK', + 'OREGON' => 'OR', + 'PALAU' => 'PW', + 'PENNSYLVANIA' => 'PA', + 'PUERTO RICO' => 'PR', + 'RHODE ISLAND' => 'RI', + 'SOUTH CAROLINA' => 'SC', + 'SOUTH DAKOTA' => 'SD', + 'TENNESSEE' => 'TN', + 'TEXAS' => 'TX', + 'UTAH' => 'UT', + 'VERMONT' => 'VT', + 'VIRGIN ISLANDS' => 'VI', + 'VIRGINIA' => 'VA', + 'WASHINGTON' => 'WA', + 'WEST VIRGINIA' => 'WV', + 'WISCONSIN' => 'WI', + 'WYOMING' => 'WY', + 'ARMED FORCES AFRICA' => 'AE', + 'ARMED FORCES AMERICAS' => 'AA', + 'ARMED FORCES CANADA' => 'AE', + 'ARMED FORCES EUROPE' => 'AE', + 'ARMED FORCES MIDDLE EAST' => 'AE', + 'ARMED FORCES PACIFIC' => 'AP', +) } + |