summaryrefslogtreecommitdiff
path: root/FS/bin/freeside-cdr-portaone-import
blob: e2023c8aac8fdea580689e7f636ab6b5efe6fb80 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/perl

use strict;

use Date::Format 'time2str';
use Date::Parse 'str2time';
use Getopt::Long;
use Cpanel::JSON::XS;
use Net::HTTPS::Any qw(https_post);
use Time::Local;

use FS::Record qw(qsearchs dbh);
use FS::UID qw(adminsuidsetup);
use FS::cdr;
use FS::cdr_batch;

sub usage {
  "Usage:
freeside-cdr-portaone-import -h 'your.domain.com:443' -u switchusername -p switchpass 
  [-s startdate] [-e enddate] [-v] freesideuser
";
}

my ($host,$username,$password,$startdate,$enddate,$verbose);
GetOptions(
  "enddate=s"   => \$enddate,
  "host=s"      => \$host,
  "password=s"  => \$password,
  "startdate=s" => \$startdate,
  "username=s"  => \$username,
  "verbose"     => \$verbose,
);

my $fsuser = $ARGV[-1];

die usage() unless $host && $password && $username && $fsuser;

adminsuidsetup($fsuser);

my $port = 443;
if ($host =~ /^(.*)\:(.*)$/) {
  $host = $1;
  $port = $2;
}

if ($startdate) {
  $startdate = str2time($startdate) or die "Can't parse startdate $startdate";
  $startdate = time2str("%Y-%m-%d %H:%M:%S",$startdate);
}
unless ($startdate) {
  my $lastbatch = qsearchs({
    'table'     => 'cdr_batch',
    'hashref'   => { 'cdrbatch' => {op=>'like', value=>'portaone-import%'}},
    'order_by'  => 'ORDER BY _date DESC LIMIT 1',
  });
  $startdate = time2str("%Y-%m-%d %H:%M:%S", $lastbatch->_date) if $lastbatch;
}
$startdate ||= '2010-01-01 00:00:00'; #seems decently in the past

my @now = localtime();
my $now = timelocal(0,0,0,$now[3],$now[4],$now[5]); #most recent midnight
if ($enddate) {
  $enddate = str2time($enddate) or die "Can't parse enddate $enddate";
  $now = $enddate;
  $enddate = time2str("%Y-%m-%d %H:%M:%S",$enddate);
}
$enddate ||= time2str("%Y-%m-%d %H:%M:%S",$now);


$FS::UID::AutoCommit = 0;

my $cdrbatchname = 'portaone-import-'. time2str('%Y/%m/%d-%T',$now);
die "Batch $cdrbatchname already exists, please specify a different end date.  " . usage()
  if FS::cdr_batch->row_exists('cdrbatch = ?', $cdrbatchname);
my $cdr_batch = new FS::cdr_batch({ 
  'cdrbatch' => $cdrbatchname,
  '_date'    => $now,
});
my $error = $cdr_batch->insert;
if ($error) {
  dbh->rollback;
  die "Error creating batch: $error";
}

print "Downloading records from $startdate to $enddate\n" if $verbose;

my $auth_info; # needs to be declared undef for call_api
$auth_info = call_api('Session','login',{
  'login'    => $username,
  'password' => $password,
});

my $results = {};
my $custlist = call_api('Customer','get_customer_list');
my @custnum = map { $_->{'i_customer'} } @{$custlist->{'customer_list'}};
foreach my $custnum (@custnum) {
  print "Retrieving for customer $custnum\n" if $verbose;
  my $step = 500; # too many records was crashing server, so we request in chunks
  my $lastcount = $step; # to get the while loop rolling
  my $totalcount = 0; # for verbose display only
  my $offset = 0;
  while ($lastcount == $step) {
    my $xdrs = call_api('Customer','get_customer_xdrs',{
      'i_customer' => $custnum,
      'from_date'  => $startdate,
      'to_date'    => $enddate,
      'cdr_entity' => 'A',
      'limit'      => $step,
      'offset'     => $offset,
    });
    my @xdrs = @{$xdrs->{'xdr_list'}};
    print "Retrieved ".@xdrs." records\n" if $verbose && @xdrs;
    my $skipped = 0; # for verbose display only
    foreach my $xdr (@xdrs) {
      if ( FS::cdr->row_exists('uniqueid = ?', $xdr->{'i_xdr'}) ) {
        $skipped += 1;
        next;
      }
      my $desc = $xdr->{'country'};
      if ($xdr->{'subdivision'}) {
        $desc = ', ' . $desc if $desc;
        $desc = $xdr->{'subdivision'} . $desc;
      }
      if ($xdr->{'description'}) {
        $desc = ' (' . $desc . ')' if $desc;
        $desc = $xdr->{'description'} . $desc;
      }
      my $cdr = FS::cdr->new ({
        'cdrbatchnum'             => $cdr_batch->cdrbatchnum,
        'uniqueid'                => $xdr->{'i_xdr'},
        'src'                     => $xdr->{'CLI'},
        'dst'                     => $xdr->{'CLD'},
        'upstream_price'          => $xdr->{'charged_amount'},
        'startdate'               => $xdr->{'unix_connect_time'},
        'enddate'                 => $xdr->{'unix_disconnect_time'},
        'accountcode'             => $xdr->{'account_id'},
        'billsec'                 => $xdr->{'charged_quantity'},
        'upstream_dst_regionname' => $desc,
      });
      $error = $cdr->insert;
      if ($error) {
        dbh->rollback;
        die "Error inserting cdr: $error";
      }
    } #foreach $xdr
    print "Skipped $skipped duplicate records\n" if $verbose && $skipped;
    $totalcount += @xdrs - $skipped;
    $lastcount = @xdrs;
    $offset += $step;
  } #while $lastcount == $step
  print scalar($totalcount) . " records inserted\n" if $verbose;
} #foreach $custnum

call_api('Session','logout',$auth_info);

### Full list of fields returned by API:
#i_xdr                     int      The unique ID of the xdr record
#account_id	              int      The unique ID of the account database record
#CLI                       string   Calling Line Identification
#CLD                       string   Called Line Identification
#charged_amount            float    Amount charged
#charged_quantity          int      Units charged
#country                   string   Country
#subdivision               string   Country subdivision
#description               string   Destination description
#disconnect_cause          string   The code of disconnect cause
#bill_status               string   Call bill status
#disconnect_reason         string   Call disconnect reason
#connect_time              dateTime Call connect time
#unix_connect_time         int      Call connect time (expressed in Unix time format - seconds since epoch)
#disconnect_time           dateTime Call disconnect time 
#unix_disconnect_time      int      Call disconnect time (expressed in Unix time format - seconds since epoch)
#bill_time                 dateTime Call bill time 
#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)
#call_recording_url        string   Path to recorded .wav files
#call_recording_server_url string   URL to the recording server 

dbh->commit;

exit;

sub call_api {
  my ($service,$method,$params) = @_;
  my %auth_info = $auth_info ? ('auth_info' => encode_json($auth_info)) : ();
  $params ||= {};
  print "Calling $service/$method\n" if $verbose;
  my ( $page, $response, %reply_headers ) = https_post(
    'host'    => $host,
    'port'    => $port,
    'path'    => '/rest/'.$service.'/'.$method.'/',
    'args'    => [ %auth_info, 'params' => encode_json($params) ],
  );
  return decode_json($page) if $response eq '200 OK';
  dbh->rollback;
  if ($response =~ /^500/) {
    my $error = decode_json($page);
    die "Server returned error during $service/$method: ".$error->{'faultstring'}
      if $error->{'faultcode'};
  }
  die "Bad response from server during $service/$method: $response";
}