summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rwxr-xr-xbin/bulk_void75
-rwxr-xr-xbin/rate-intl.import113
2 files changed, 188 insertions, 0 deletions
diff --git a/bin/bulk_void b/bin/bulk_void
new file mode 100755
index 000000000..a1428180e
--- /dev/null
+++ b/bin/bulk_void
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+
+use FS::Misc::Getopt;
+use FS::Record qw(qsearch qsearchs dbh);
+
+getopts('cpifXr:');
+my $dbh = dbh;
+$FS::UID::AutoCommit = 0;
+
+sub usage() {
+ "Usage: bulk_void -s start -e end
+ -r void_reason
+ { -c | -p | -i }
+ [ -X ]
+ <user>
+-s, -e: date range (required)
+-r: void reason text (required)
+-c, -p, -i, -f: void credits, payments, invoices
+-X: commit changes
+";
+}
+
+if (!$opt{start} or !$opt{end} or !$opt{r}) {
+ die usage;
+}
+
+print "DRY RUN--changes will not be committed.\n" unless $opt{X};
+
+my $date = " WHERE _date >= $opt{start} AND _date <= $opt{end}";
+
+my %tables = (
+ c => 'cust_credit',
+ p => 'cust_pay',
+ i => 'cust_bill',
+);
+
+my $reason = $opt{r};
+
+foreach my $k (keys %tables) {
+ next unless $opt{$k};
+ my $table = $tables{$k};
+ debug("$table:");
+ my $done_count = 0;
+ my $error_count = 0;
+
+ my $cursor = FS::Cursor->new({
+ table => $table,
+ extra_sql => $date,
+ });
+ my $error;
+ while (my $record = $cursor->fetch) {
+ $error = $record->void($reason);
+ if ( $error ) {
+ $error = "$table #" . $record->get($record->primary_key) . ": $error";
+ print "$error\n";
+ $error_count++;
+ if ( $opt{X} ) {
+ $dbh->rollback;
+ exit(1);
+ }
+ } else {
+ $done_count++;
+ }
+ }
+ print " $table voided: $done_count\n errors: $error_count\n";
+}
+
+if ( $opt{X} ) {
+ $dbh->commit;
+ print "Committed changes.\n";
+} else {
+ $dbh->rollback;
+ print "Reverted changes.\n";
+}
+
diff --git a/bin/rate-intl.import b/bin/rate-intl.import
new file mode 100755
index 000000000..7eef5878f
--- /dev/null
+++ b/bin/rate-intl.import
@@ -0,0 +1,113 @@
+#!/usr/bin/perl
+
+use strict;
+use Text::CSV;
+use FS::Misc::Getopt;
+use FS::Record qw(qsearchs qsearch dbh);
+use FS::rate;
+use FS::rate_region;
+use FS::rate_prefix;
+use FS::rate_detail;
+
+getopts('');
+
+$FS::UID::AutoCommit = 0;
+my $dbh = dbh;
+
+my $file = shift or usage();
+open my $in, '<', $file or die "$file: $!\n";
+my $csv = Text::CSV->new({ binary => 1, auto_diag => 2 });
+# set header row
+$csv->column_names($csv->getline($in));
+
+my $error;
+
+my $granularity = 1;
+# default is to charge per second; edit this if needed
+
+while (my $row = $csv->getline_hr($in)) {
+ print $csv->string;
+
+ # ProfileKey is just a number
+ my $rate = qsearchs('rate', { 'ratename' => $row->{'ProfileKey'} });
+ if (!$rate) {
+ $rate = FS::rate->new({ 'ratename' => $row->{'ProfileKey'} });
+ $error = $rate->insert;
+ die $error if $error;
+ }
+
+ # DestinationId looks like "Country - City" or "Country - Mobile -
+ # Carrier" (or sometimes just "Country - Mobile").
+ my $region = qsearchs('rate_region', {
+ 'regionname' => $row->{'DestinationId'}
+ });
+ if (!$region) {
+ $region = FS::rate_region->new({
+ 'regionname' => $row->{'DestinationId'}
+ });
+ $error = $region->insert;
+ die $error if $error;
+ }
+
+ # Prefix strings found in there look like
+ # "e164:123-45-6nnnnnnn-"
+ # The first group of digits is the country code, any others are the
+ # prefix. Sometimes the nnnn's are NNNN's. The dashes are not guaranteed
+ # to be anywhere specific.
+ # Catchall prefixes start with "-A", which has a meaning like "match
+ # anything, but at a lower priority than a digit match".
+ # NANPA numbers use "1-", and for a catchall area code use "1-AAA-".
+ my $cc_long = $row->{CountryCodeLong};
+ $cc_long =~ /^e164:(\d+)-([\d-]*)A*-?n+-$/i;
+ my $countrycode = $1;
+ if (!$countrycode) { # totally legit reasons for this, e.g. 1-AAA-411
+ warn "can't parse number prefix:\n$cc_long\n";
+ next;
+ }
+ my $prefix = $2;
+ $prefix =~ s/-//g;
+
+ my %prefix = (
+ 'regionnum' => $region->regionnum,
+ 'countrycode' => $countrycode,
+ 'npa' => $prefix,
+ );
+ my $rate_prefix = qsearchs('rate_prefix', \%prefix);
+ if (!$rate_prefix) {
+ $rate_prefix = FS::rate_prefix->new(\%prefix);
+ $error = $rate_prefix->insert;
+ die $error if $error;
+ }
+
+ # enough to identify the detail
+ my %detail = (
+ 'ratenum' => $rate->ratenum,
+ 'dest_regionnum' => $region->regionnum,
+ 'cdrtypenum' => '',
+ 'ratetimenum' => '',
+ );
+ my $dest_detail = qsearchs('rate_detail', \%detail);
+ # ProfileRate is 5 decimal places, same as rate_detail.min_charge
+ if (!$dest_detail) {
+ $dest_detail = FS::rate_detail->new({
+ %detail,
+ 'min_included' => 0,
+ 'min_charge' => $row->{ProfileRate},
+ 'sec_granularity' => $granularity,
+ });
+ $error = $dest_detail->insert;
+ } else {
+ local $FS::Record::nowarn_identical = 1;
+ $dest_detail->set('min_charge' => $row->{ProfileRate});
+ $error = $dest_detail->replace;
+ }
+ die $error if $error;
+}
+dbh->commit;
+print "Finished.\n";
+
+
+sub usage {
+ die "Usage: rate-intl.import <user> <file>.csv\n\n";
+}
+