5 use Date::Format 'time2str';
6 use Date::Parse 'str2time';
9 use Net::HTTPS::Any qw(https_post);
12 use FS::Record qw(qsearchs dbh);
13 use FS::UID qw(adminsuidsetup);
19 freeside-cdr-portaone-import -x exportnum [-s startdate] [-e enddate] [-v] freesideuser
21 freeside-cdr-portaone-import -h 'your.domain.com:443' -u switchusername -p switchpass
22 [-s startdate] [-e enddate] [-v] freesideuser
26 my ($host,$username,$password,$startdate,$enddate,$verbose,$exportnum);
28 "enddate=s" => \$enddate,
30 "password=s" => \$password,
31 "startdate=s" => \$startdate,
32 "username=s" => \$username,
33 "verbose" => \$verbose,
37 my $fsuser = $ARGV[-1];
39 die usage() unless $fsuser;
41 adminsuidsetup($fsuser);
44 if ($host =~ /^(.*)\:(.*)$/) {
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;
60 die usage() unless $host && $password && $username;
63 $startdate = str2time($startdate) or die "Can't parse startdate $startdate";
64 $startdate = time2str("%Y-%m-%d %H:%M:%S",$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',
72 $startdate = time2str("%Y-%m-%d %H:%M:%S", $lastbatch->_date) if $lastbatch;
74 $startdate ||= '2010-01-01 00:00:00'; #seems decently in the past
76 my @now = localtime();
77 my $now = timelocal(0,0,0,$now[3],$now[4],$now[5]); #most recent midnight
79 $enddate = str2time($enddate) or die "Can't parse enddate $enddate";
81 $enddate = time2str("%Y-%m-%d %H:%M:%S",$enddate);
83 $enddate ||= time2str("%Y-%m-%d %H:%M:%S",$now);
86 $FS::UID::AutoCommit = 0;
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,
95 my $error = $cdr_batch->insert;
98 die "Error creating batch: $error";
101 print "Downloading records from $startdate to $enddate\n" if $verbose;
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,
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
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,
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'}) ) {
135 my $desc = $xdr->{'country'};
136 if ($xdr->{'subdivision'}) {
137 $desc = ', ' . $desc if $desc;
138 $desc = $xdr->{'subdivision'} . $desc;
140 if ($xdr->{'description'}) {
141 $desc = ' (' . $desc . ')' if $desc;
142 $desc = $xdr->{'description'} . $desc;
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,
156 $error = $cdr->insert;
159 die "Error inserting cdr: $error";
162 print "Skipped $skipped duplicate records\n" if $verbose && $skipped;
163 $totalcount += @xdrs - $skipped;
166 } #while $lastcount == $step
167 print scalar($totalcount) . " records inserted\n" if $verbose;
170 call_api('Session','logout',$auth_info);
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
199 my ($service,$method,$params) = @_;
200 my %auth_info = $auth_info ? ('auth_info' => encode_json($auth_info)) : ();
202 print "Calling $service/$method\n" if $verbose;
203 my ( $page, $response, %reply_headers ) = https_post(
206 'path' => '/rest/'.$service.'/'.$method.'/',
207 'args' => [ %auth_info, 'params' => encode_json($params) ],
209 return decode_json($page) if $response eq '200 OK';
211 if ($response =~ /^500/) {
212 my $error = decode_json($page);
213 die "Server returned error during $service/$method: ".$error->{'faultstring'}
214 if $error->{'faultcode'};
216 die "Bad response from server during $service/$method: $response";