Reverted menu-left-example.png back to original and cleaned up menu-top-example to...
[freeside.git] / FS / bin / freeside-cdr-portaone-import
1 #!/usr/bin/perl
2
3 use strict;
4
5 use Date::Format 'time2str';
6 use Date::Parse 'str2time';
7 use Getopt::Long;
8 use Cpanel::JSON::XS;
9 use Net::HTTPS::Any qw(https_post);
10 use Time::Local;
11
12 use FS::Record qw(qsearchs dbh);
13 use FS::UID qw(adminsuidsetup);
14 use FS::cdr;
15 use FS::cdr_batch;
16
17 sub usage {
18   "Usage:
19 freeside-cdr-portaone-import -x exportnum [-s startdate] [-e enddate] [-v] freesideuser
20
21 freeside-cdr-portaone-import -h 'your.domain.com:443' -u switchusername -p switchpass 
22   [-s startdate] [-e enddate] [-v] freesideuser
23 ";
24 }
25
26 my ($host,$username,$password,$startdate,$enddate,$verbose,$exportnum);
27 GetOptions(
28   "enddate=s"   => \$enddate,
29   "host=s"      => \$host,
30   "password=s"  => \$password,
31   "startdate=s" => \$startdate,
32   "username=s"  => \$username,
33   "verbose"     => \$verbose,
34   "x=s"         => \$exportnum,
35 );
36
37 my $fsuser = $ARGV[-1];
38
39 die usage() unless $fsuser;
40
41 adminsuidsetup($fsuser);
42
43 my $port = 443;
44 if ($host =~ /^(.*)\:(.*)$/) {
45   $host = $1;
46   $port = $2;
47 }
48
49 if ($exportnum) {
50   my $export = qsearchs('part_export', { 'exportnum' => $exportnum });
51   die "Export not found" unless $export;
52   $host = $export->machine;
53   $port = $export->option('port');
54   $username = $export->option('username');
55   $password = $export->option('password');
56   die "Auth info not fully specified in export"
57     unless $host && $port && $username && $password;
58 }
59
60 die usage() unless $host && $password && $username;
61
62 if ($startdate) {
63   $startdate = str2time($startdate) or die "Can't parse startdate $startdate";
64   $startdate = time2str("%Y-%m-%d %H:%M:%S",$startdate);
65 }
66 unless ($startdate) {
67   my $lastbatch = qsearchs({
68     'table'     => 'cdr_batch',
69     'hashref'   => { 'cdrbatch' => {op=>'like', value=>'portaone-import%'}},
70     'order_by'  => 'ORDER BY _date DESC LIMIT 1',
71   });
72   $startdate = time2str("%Y-%m-%d %H:%M:%S", $lastbatch->_date) if $lastbatch;
73 }
74 $startdate ||= '2010-01-01 00:00:00'; #seems decently in the past
75
76 my @now = localtime();
77 my $now = timelocal(0,0,0,$now[3],$now[4],$now[5]); #most recent midnight
78 if ($enddate) {
79   $enddate = str2time($enddate) or die "Can't parse enddate $enddate";
80   $now = $enddate;
81   $enddate = time2str("%Y-%m-%d %H:%M:%S",$enddate);
82 }
83 $enddate ||= time2str("%Y-%m-%d %H:%M:%S",$now);
84
85
86 $FS::UID::AutoCommit = 0;
87
88 my $cdrbatchname = 'portaone-import-'. time2str('%Y/%m/%d-%T',$now);
89 die "Batch $cdrbatchname already exists, please specify a different end date.  " . usage()
90   if FS::cdr_batch->row_exists('cdrbatch = ?', $cdrbatchname);
91 my $cdr_batch = new FS::cdr_batch({ 
92   'cdrbatch' => $cdrbatchname,
93   '_date'    => $now,
94 });
95 my $error = $cdr_batch->insert;
96 if ($error) {
97   dbh->rollback;
98   die "Error creating batch: $error";
99 }
100
101 print "Downloading records from $startdate to $enddate\n" if $verbose;
102
103 my $auth_info; # needs to be declared undef for call_api
104 $auth_info = call_api('Session','login',{
105   'login'    => $username,
106   'password' => $password,
107 });
108
109 my $results = {};
110 my $custlist = call_api('Customer','get_customer_list');
111 my @custnum = map { $_->{'i_customer'} } @{$custlist->{'customer_list'}};
112 foreach my $custnum (@custnum) {
113   print "Retrieving for customer $custnum\n" if $verbose;
114   my $step = 500; # too many records was crashing server, so we request in chunks
115   my $lastcount = $step; # to get the while loop rolling
116   my $totalcount = 0; # for verbose display only
117   my $offset = 0;
118   while ($lastcount == $step) {
119     my $xdrs = call_api('Customer','get_customer_xdrs',{
120       'i_customer' => $custnum,
121       'from_date'  => $startdate,
122       'to_date'    => $enddate,
123       'cdr_entity' => 'A',
124       'limit'      => $step,
125       'offset'     => $offset,
126     });
127     my @xdrs = @{$xdrs->{'xdr_list'}};
128     print "Retrieved ".@xdrs." records\n" if $verbose && @xdrs;
129     my $skipped = 0; # for verbose display only
130     foreach my $xdr (@xdrs) {
131       if ( FS::cdr->row_exists('uniqueid = ?', $xdr->{'i_xdr'}) ) {
132         $skipped += 1;
133         next;
134       }
135       my $desc = $xdr->{'country'};
136       if ($xdr->{'subdivision'}) {
137         $desc = ', ' . $desc if $desc;
138         $desc = $xdr->{'subdivision'} . $desc;
139       }
140       if ($xdr->{'description'}) {
141         $desc = ' (' . $desc . ')' if $desc;
142         $desc = $xdr->{'description'} . $desc;
143       }
144       my $cdr = FS::cdr->new ({
145         'cdrbatchnum'             => $cdr_batch->cdrbatchnum,
146         'uniqueid'                => $xdr->{'i_xdr'},
147         'src'                     => $xdr->{'CLI'},
148         'dst'                     => $xdr->{'CLD'},
149         'upstream_price'          => $xdr->{'charged_amount'},
150         'startdate'               => $xdr->{'unix_connect_time'},
151         'enddate'                 => $xdr->{'unix_disconnect_time'},
152         'accountcode'             => $xdr->{'account_id'},
153         'billsec'                 => $xdr->{'charged_quantity'},
154         'upstream_dst_regionname' => $desc,
155       });
156       $error = $cdr->insert;
157       if ($error) {
158         dbh->rollback;
159         die "Error inserting cdr: $error";
160       }
161     } #foreach $xdr
162     print "Skipped $skipped duplicate records\n" if $verbose && $skipped;
163     $totalcount += @xdrs - $skipped;
164     $lastcount = @xdrs;
165     $offset += $step;
166   } #while $lastcount == $step
167   print scalar($totalcount) . " records inserted\n" if $verbose;
168 } #foreach $custnum
169
170 call_api('Session','logout',$auth_info);
171
172 ### Full list of fields returned by API:
173 #i_xdr                     int      The unique ID of the xdr record
174 #account_id                   int      The unique ID of the account database record
175 #CLI                       string   Calling Line Identification
176 #CLD                       string   Called Line Identification
177 #charged_amount            float    Amount charged
178 #charged_quantity          int      Units charged
179 #country                   string   Country
180 #subdivision               string   Country subdivision
181 #description               string   Destination description
182 #disconnect_cause          string   The code of disconnect cause
183 #bill_status               string   Call bill status
184 #disconnect_reason         string   Call disconnect reason
185 #connect_time              dateTime Call connect time
186 #unix_connect_time         int      Call connect time (expressed in Unix time format - seconds since epoch)
187 #disconnect_time           dateTime Call disconnect time 
188 #unix_disconnect_time      int      Call disconnect time (expressed in Unix time format - seconds since epoch)
189 #bill_time                 dateTime Call bill time 
190 #bit_flags                 int      Extended information how the service was used; the integer field that should be treated as a bit-map. Each currently used bit is listed in the Transaction_Flag_Types table (bit_offset indicates position)
191 #call_recording_url        string   Path to recorded .wav files
192 #call_recording_server_url string   URL to the recording server 
193
194 dbh->commit;
195
196 exit;
197
198 sub call_api {
199   my ($service,$method,$params) = @_;
200   my %auth_info = $auth_info ? ('auth_info' => encode_json($auth_info)) : ();
201   $params ||= {};
202   print "Calling $service/$method\n" if $verbose;
203   my ( $page, $response, %reply_headers ) = https_post(
204     'host'    => $host,
205     'port'    => $port,
206     'path'    => '/rest/'.$service.'/'.$method.'/',
207     'args'    => [ %auth_info, 'params' => encode_json($params) ],
208   );
209   return decode_json($page) if $response eq '200 OK';
210   dbh->rollback;
211   if ($response =~ /^500/) {
212     my $error = decode_json($page);
213     die "Server returned error during $service/$method: ".$error->{'faultstring'}
214       if $error->{'faultcode'};
215   }
216   die "Bad response from server during $service/$method: $response";
217 }
218
219