[freeside-commits] freeside/bin import-did-inventory,NONE,1.1.2.2

Erik Levinson levinse at wavetail.420.am
Fri May 20 17:52:52 PDT 2011


Update of /home/cvs/cvsroot/freeside/bin
In directory wavetail.420.am:/tmp/cvs-serv11714/bin

Added Files:
      Tag: FREESIDE_2_1_BRANCH
	import-did-inventory 
Log Message:
DID inventory import, RT12754

--- NEW FILE: import-did-inventory ---
#!/usr/bin/perl

use strict;
use warnings;
use Text::CSV;
use FS::UID qw(adminsuidsetup);
use FS::cust_main_county;
use FS::Record qw(qsearch qsearchs);
use DateTime::Format::Natural;
use FS::lata;
use FS::msa;
use FS::cust_main;
use FS::cust_main::Search qw(smart_search);
use FS::did_order;
use FS::did_order_item;
use FS::rate_center;
use FS::phone_avail;

my $user = shift;
adminsuidsetup $user;

#### SET THESE! #################################
my $file = '/home/levinse/dids1.csv';
my $did_vendor_id = 1;
my %custname2num = (); # MyCust => 12345,
################################################

my $debug = 1;
my $max_date = time;
my $min_date = 1262304000; # January 1st 2010
my %did_order = ();
my %rate_center_cache = ();
my $linenum = 1;

my $csv = new Text::CSV;
open (CSV, "<", $file) or die $!;

sub parsedt {
    my ($dt,$min,$max) = (shift,shift,shift);
    my $parser = new DateTime::Format::Natural( 'time_zone' => 'local' );
    my $epoch = $parser->parse_datetime($dt);
    return $epoch->epoch 
        if ($parser->success && $epoch->epoch >= $min && $epoch->epoch <= $max);
    die "invalid date $dt (min=$min, max=$max)";
}

# XXX: transactions? so that we can fail the import when we "die"
sub suffer {
    my $linenum = shift;
    my @columns = @_;

    my $did = $columns[0];
    my $npa = $columns[1];
    my $state = $columns[2];
    my $rate_center_abbrev = $columns[3];
    my $rate_center = $columns[4];
    my $customer = $columns[5];
    my $submitted = parsedt($columns[7],$min_date,$max_date);
    my $ordernum = $columns[8];
    my $confirmed = parsedt($columns[9],$submitted,$max_date);

    # sometimes, we're in a non-Y2K-compliant bullshit format, differing from
    # all the other dates. Other times, we randomly change formats multiple times
    # in the middle of the file for absolutely no reason...wtf
    my $received = $columns[10]; 
    if ( $received =~ /^(\d{1,2})\/(\d{1,2})\/(\d{2})$/ ) {
        $received = $2."/".$1."/20".$3;
    } elsif ( $received !~ /^\d{2}\/\d{2}\/\d{4}$/ ) {
        die "invalid received date $received";
    }
    $received = parsedt($received,$confirmed,$max_date);

    my $latanum = $columns[12];
    my $latadesc = $columns[13];
    my $msadesc = $columns[14];

    die "invalid DID and/or NPA or NPA doesn't match DID"
        unless ($did =~ /^(\d{3})\d{7}$/ && $npa == $1);
    die "invalid state, order #, LATA #, or LATA description"
        unless ($state =~ /^[A-Z]{2}$/  && $ordernum =~ /^\d+$/ 
                                        && $latanum =~ /^\d{3}$/ 
                                        && $latadesc =~ /^[\w\s]+$/);

    my $lata = qsearchs('lata', { 'latanum' => $latanum });
    die "no lata found for latanum $latanum or multiple results" unless $lata;

    # unsurprisingly, our idea of a LATA name doesn't always match their idea 
    # of the same. Specifically, they randomly expand the state portion and
    # abbreviate it arbitrarily
    my $latadescription = $lata->description;
    $latadescription =~ s/ ..$//; # strip off the fixed state abbreviation portion in ours
    $latadesc =~ s/\s\w+$//; # strip off the variable state abbreviation (or full name) portion in theirs
    $latadesc = 'CONNECTICUT (SNET)' if $latanum == 920; # hax!
    die "CSV file LATA description ($latadesc) doesn't match our LATA description ($latadescription)"
        unless uc($latadescription) eq uc($latadesc);

    # here comes the bigger unsurprising mess
    my $msanum = -1; # means no msa entered

    # 1. Danbury isn't a MSA
    $msadesc = '' if $msadesc eq 'Danbury';

    # 2. not everything in their file has a MSA
    if ( $msadesc =~ /^[\w\s]+$/ ) {
        # 3. replace this bullshit
        $msadesc = "Washington" if $msadesc eq 'Washington DC';

        # 4. naturally enough, their idea of a MSA differs from our idea of it
        my @msa = qsearch('msa', { 'description' => {
                                            'op' => 'ILIKE',
                                            'value' => $msadesc."%" 
                                        }
                                  });

        # 5. so now we have two cases for a match and everything else is a non-match
        foreach my $msa ( @msa ) {
            # a. our MSA stripped of state portion matches their MSA exactly
            my $msatest1 = $msa->description;
            $msatest1 =~ s/,.*?$//;
            if($msatest1 eq $msadesc) {
                die "multiple MSA matches" unless $msanum == -1;
                $msanum = $msa->msanum;
            }
            
            # b. our MSA stripped of state portion and up to the first hyphen matches their MSA exactly
            my $msatest2 = $msa->description;
            if($msatest2 =~ /^([\w\s]+)-/ && $1 eq $msadesc) {
                die "multiple MSA matches" unless $msanum == -1;
                $msanum = $msa->msanum;
            }
        }
        die "msa $msadesc not found" if $msanum == -1;
        print "$msadesc matched msanum $msanum for line $linenum\n" if $debug;
    }

    print "Pass $linenum\n" if $debug;

    my $order = order($ordernum,$submitted,$confirmed,$received,$customer);

}

sub order {
    my($ordernum,$submitted,$confirmed,$received,$customer) = (shift,shift,shift,shift,shift);
    
    my %cust = ();
    if ( $customer ne 'Stock' ) {
        if ( exists($custname2num{$customer}) ) {
            $cust{'custnum'} = $custname2num{$customer};
        } else {
            my @cust_main = smart_search('search' => $customer);
            die scalar(@cust_main) . " customers found for $customer" 
                unless scalar(@cust_main) == 1;
            $cust{'custnum'} = $cust_main[0]->custnum;
            
            # cache it, or we'll be going even slower than we already are
            $custname2num{$customer} = $cust_main[0]->custnum; 
        }
    }

    my $o;
    if( exists $did_order{$ordernum} ) {
        $o = $did_order{$ordernum};
        die "vendor order #$ordernum - order data differs from one item to another"
            unless ($o->submitted == $submitted && $o->confirmed == $confirmed
                    && $o->received == $received);
        die "customer mismatch for vendor order #$ordernum" 
            unless (($o->custnum && $cust{'custnum'} && $o->custnum == $cust{'custnum'})
                || (!$o->custnum && !exists($cust{'custnum'})) );
    } else {
        $did_order{$ordernum} = new FS::did_order{ vendornum => $did_vendor_id,
                                                   vendor_order_id => $ordernum,
                                                   submitted => $submitted,
                                                   confirmed => $confirmed,
                                                   received => $received,
                                                   %cust,
                                                 };
        $o = $did_order{$ordernum};
    }

    die "wtf" unless $o;
    $o;
}

sub provision {

    local $FS::svc_Common::noexport_hack = 1;

}

while (<CSV>) {
    if ( $linenum == 1 ) { # skip header
        $linenum++;
        next;
    }

    if ($csv->parse($_)) {
        my @columns = $csv->fields();
        suffer($linenum, at columns);
    } else {
        my $err = $csv->error_input;
        print "Failed to parse line $linenum: $err";
    }
    $linenum++;
}
close CSV;



More information about the freeside-commits mailing list