rework Billsoft interface for new Avalara data format, fix many things, #73063
[freeside.git] / FS / bin / freeside-cdr-a2billing-import
1 #!/usr/bin/perl
2
3 use strict;
4 use vars qw( $DEBUG );
5 use Date::Parse 'str2time';
6 use Date::Format 'time2str';
7 use FS::UID qw(adminsuidsetup dbh);
8 use FS::cdr;
9 use DBI;
10 use Getopt::Std;
11
12 my %opt;
13 getopts('H:U:P:D:T:s:e:c:', \%opt);
14 my $user = shift or die &usage;
15
16 my $dsn = 'dbi:mysql';
17 $dsn .= ":database=$opt{D}" if $opt{D};
18 $dsn .= ":host=$opt{H}" if $opt{H};
19
20 my $mysql = DBI->connect($dsn, $opt{U}, $opt{P}) 
21   or die $DBI::errstr;
22
23 my ($start, $end) = ('', '');
24 if ( $opt{s} ) {
25   $start = str2time($opt{s}) or die "can't parse start date $opt{s}\n";
26   $start = time2str('%Y-%m-%d', $start);
27 }
28 if ( $opt{e} ) {
29   $end = str2time($opt{e}) or die "can't parse end date $opt{e}\n";
30   $end = time2str('%Y-%m-%d', $end);
31 }
32
33 adminsuidsetup $user;
34
35 my $fsdbh = FS::UID::dbh;
36
37 # check for existence of freesidestatus
38 my $table = $opt{T} || 'cc_call';
39 my $status = $mysql->selectall_arrayref("SHOW COLUMNS FROM $table WHERE Field = 'freesidestatus'");
40 if( ! @$status ) {
41   print "Adding freesidestatus column...\n";
42   $mysql->do("ALTER TABLE $table ADD COLUMN freesidestatus varchar(32)")
43     or die $mysql->errstr;
44 }
45 else {
46   print "freesidestatus column present\n";
47 }
48
49 # Fields:
50 # id - primary key, sequential
51 # session_id - Local/<digits>-<digits> or SIP/<digits>-<digits>
52 # uniqueid - a decimal number, seems to be close to the unix timestamp
53 # card_id - probably the equipment port, 1 - 10
54 # nasipaddress - we don't care
55 # starttime, stoptime - timestamps
56 # sessiontime - duration, seconds
57 # calledstation - dst
58 # sessionbill - upstream_price
59 # id_tariffgroup - null, 0, 1
60 # id_tariffplan - null, 0, 3, 4, 5, 6, 7, 8, 9
61 # id_ratecard - larger numbers
62 # (all of the id_* fields are foreign keys: cc_tariffgroup, cc_ratecard, etc.)
63 # id_trunk - we don't care
64 # sipiax - probably don't care
65 # src - src.  Usually a phone number, but not always.
66 # id_did - always null
67 # buycost - wholesale price? correlated with sessionbill
68 # id_card_package_offer - no idea
69 # real_sessiontime - close to sessiontime, except when it's null
70 # (When sessiontime = 0, real_sessiontime is either 0 or null, and 
71 # sessionbill is 0.  When sessiontime > 0, but real_sessiontime is null, 
72 # sessionbill is 0.  So real_sessiontime seems to be the billable time, and 
73 # is null when the call is non-billable.)
74 # dnid - sometimes equals calledstation, or calledstation without the leading 
75 # "1".  But not always.
76 # terminatecauseid - integer, 0 - 7
77 # destination - seems to be the NPA or NPA+NXX sometimes, or "0".
78
79 # terminatecauseid values:
80 my %disposition = (
81   0 => '',
82   1 => 'ANSWER', #the only one that's billable
83   2 => 'BUSY',
84   3 => 'NOANSWER',
85   4 => 'CANCEL',
86   5 => 'CONGESTION',
87   6 => 'CHANUNAVAIL',
88   7 => 'DONTCALL',
89   8 => 'TORTURE', #???
90   9 => 'INVALIDARGS',
91 );
92
93 my @cols = (
94   "$table.id as id", 'cc_card.username as username',
95   qw( sessionid
96       starttime stoptime sessiontime real_sessiontime
97       terminatecauseid
98       calledstation src
99       id_tariffplan id_ratecard sessionbill
100     )
101 );
102
103 my $sql = 'SELECT '.join(',', @cols). " FROM $table".
104   " LEFT JOIN cc_card ON ( $table.card_id = cc_card.id ) ".
105   ' WHERE freesidestatus IS NULL' .
106   ($start && " AND starttime >= '$start'") .
107   ($end   && " AND starttime <  '$end'") ;
108 my $sth = $mysql->prepare($sql);
109 $sth->execute;
110 print "Importing ".$sth->rows." records...\n";
111
112 my $cdr_batch = new FS::cdr_batch({ 
113     'cdrbatch' => 'mysql-import-'. time2str('%Y/%m/%d-%T',time),
114   });
115 my $error = $cdr_batch->insert;
116 die $error if $error;
117 my $cdrbatchnum = $cdr_batch->cdrbatchnum;
118 my $imports = 0;
119 my $updates = 0;
120
121 my $row;
122 while ( $row = $sth->fetchrow_hashref ) {
123   my $dst = $row->{calledstation};
124   my $dst_ip_addr = '';
125   if ($dst =~ m[^SIP/(\d+)@(.*)$] ) {
126     $dst = $1;
127     $dst_ip_addr = $2;
128   }
129   $dst =~ s/^1//;
130   $row->{src} =~ s/^1//;
131   my $cdr = FS::cdr->new ({
132     uniqueid            => $row->{sessionid},
133     cdrbatchnum         => $cdrbatchnum,
134     startdate           => time2str($row->{starttime}),
135     enddate             => time2str($row->{stoptime}),
136     duration            => $row->{sessiontime},
137     billsec             => $row->{real_sessiontime},
138     dst                 => $dst,
139     src                 => $row->{src},
140     dst_ip_addr         => $dst_ip_addr,
141     dstchannel          => $row->{calledstation},
142     charged_party       => $row->{username},
143     upstream_rateplanid => $row->{id_tariffplan},
144     upstream_rateid     => $row->{id_ratecard}, # I think?
145     upstream_price      => $row->{sessionbill},
146   });
147   $cdr->cdrtypenum($opt{c}) if $opt{c};
148
149   my $error = $cdr->insert;
150   if($error) {
151     print "failed import: $error\n";
152   } else {
153     $imports++;
154     my $updated = $mysql->do(
155                     "UPDATE $table SET freesidestatus = 'done' WHERE id = ?",
156                     undef,
157                     $row->{'id'}
158                   );
159     $updates += $updated;
160     print "failed to set status: ".$mysql->errstr."\n" unless $updated;
161   }
162 }
163 print "Done.\nImported $imports CDRs, marked $updates as done in source database.\n";
164 $mysql->disconnect;
165
166 sub usage {
167   "Usage: 
168   freeside-cdr-a2billing-import
169       [ -H host ]
170       -D database
171       -U user
172       -P password
173       [ -s start ] [ -e end ] [ -c cdrtypenum ]
174       freesideuser
175 ";
176 }
177
178 =head1 NAME
179
180 freeside-cdr-a2billing-import - Download CDRs from an A2Billing MySQL database
181
182 =head1 SYNOPSIS
183
184   freeside-cdr-a2billing-import [ -H host ] -D database -U user -P password
185     [ -T tablename ]
186     [ -s start ] [ -e end ] [ -c cdrtypenum ]
187     freesideuser
188
189 -H: database hostname
190
191 -D: database name
192
193 -U: database username
194
195 -P: database password
196
197 -T: table to import, defaults to cc_call
198
199 -s: start date, e.g. 4/20/2015
200
201 -e: end date, e.g. 12/25/2015
202
203 -c: cdrtypenum to set, defaults to none
204
205 freesideuser: freeside username
206
207 =head1 DESCRIPTION
208
209 =head1 BUGS
210
211 =head1 SEE ALSO
212
213 L<FS::cdr>
214
215 =cut
216
217 1;