diff options
| -rw-r--r-- | FS/FS/cdr.pm | 211 | ||||
| -rw-r--r-- | httemplate/misc/cdr-import.html | 6 | 
2 files changed, 131 insertions, 86 deletions
| diff --git a/FS/FS/cdr.pm b/FS/FS/cdr.pm index e9a26794a..495f5d189 100644 --- a/FS/FS/cdr.pm +++ b/FS/FS/cdr.pm @@ -462,7 +462,9 @@ sub _cdr_date_parser_maker {    my $field = shift;    return sub {      my( $cdr, $date ) = @_; -    $cdr->$field( _cdr_date_parse($date) ); +    #$cdr->$field( _cdr_date_parse($date) ); +    eval { $cdr->$field( _cdr_date_parse($date) ); }; +    die "error parsing date for $field from $date: $@\n" if $@;    };  } @@ -472,13 +474,18 @@ sub _cdr_date_parse {    return '' unless length($date); #that's okay, it becomes NULL    #$date =~ /^\s*(\d{4})[\-\/]\(\d{1,2})[\-\/](\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})\s*$/ -  $date =~ /^\s*(\d{4})\D(\d{1,2})\D(\d{1,2})\s+(\d{1,2})\D(\d{1,2})\D(\d{1,2})\s*$/ +  $date =~ /^\s*(\d{4})\D(\d{1,2})\D(\d{1,2})\s+(\d{1,2})\D(\d{1,2})\D(\d{1,2})(\D|$)/      or die "unparsable date: $date"; #maybe we shouldn't die...    my($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6 ); +  return '' if $year == 1900 && $mon == 1 && $day == 1 +            && $hour == 0    && $min == 0 && $sec == 0; +    timelocal($sec, $min, $hour, $day, $mon-1, $year);  } +#taqua  #2007-10-31 08:57:24.113000000 +  #http://www.the-asterisk-book.com/unstable/funktionen-cdr.html  my %amaflags = (    DEFAULT       => 0, @@ -511,97 +518,126 @@ my %import_formats = (      'uniqueid',      'userfield',    ], -  'taqua' => [ -    sub { my($cdr, $field) = @_; }, #RecordType -    sub { my($cdr, $field) = @_; },         #all10#RecordVersion -    sub { my($cdr, $field) = @_; }, #OrigShelfNumber -    sub { my($cdr, $field) = @_; }, #OrigCardNumber -    sub { my($cdr, $field) = @_; }, #OrigCircuit -    sub { my($cdr, $field) = @_; }, #OrigCircuitType -    sub { my($cdr, $field) = @_; }, #SequenceNumber -    sub { my($cdr, $field) = @_; }, #SessionNumber -    sub { my($cdr, $field) = @_; }, #CallingPartyNumber -    sub { my($cdr, $field) = @_; }, #CalledPartyNumber -    sub { my($cdr, $field) = @_; }, #CallArrivalTime -    sub { my($cdr, $field) = @_; }, #CallCompletionTime -    sub { my($cdr, $field) = @_; }, #Disposition -    sub { my($cdr, $field) = @_; }, #DispositionTime -    sub { my($cdr, $field) = @_; }, #TCAP -    sub { my($cdr, $field) = @_; }, #OutboundCarrierConnectTime -    sub { my($cdr, $field) = @_; }, #OutboundCarrierDisconnectTime -    sub { my($cdr, $field) = @_; }, #TermTrunkGroup -    sub { my($cdr, $field) = @_; }, #TermShelfNumber -    sub { my($cdr, $field) = @_; }, #TermCardNumber -    sub { my($cdr, $field) = @_; }, #TermCircuit -    sub { my($cdr, $field) = @_; }, #TermCircuitType -    sub { my($cdr, $field) = @_; }, #OutboundCarrierId -    sub { my($cdr, $field) = @_; }, #BillingNumber -    sub { my($cdr, $field) = @_; }, #SubscriberNumber -    sub { my($cdr, $field) = @_; }, #ServiceName -    sub { my($cdr, $field) = @_; }, #ChargeTime -    sub { my($cdr, $field) = @_; }, #ServiceInformation -    sub { my($cdr, $field) = @_; }, #FacilityInfo -    sub { my($cdr, $field) = @_; }, #CallTraceTime -    sub { my($cdr, $field) = @_; },         #all-1#UniqueIndicator -    sub { my($cdr, $field) = @_; },         #all-1#PresentationIndicator -    sub { my($cdr, $field) = @_; },         #empty#Pin -    sub { my($cdr, $field) = @_; }, #CallType -    sub { my($cdr, $field) = @_; }, #OrigRateCenter -    sub { my($cdr, $field) = @_; }, #TermRateCenter -    sub { my($cdr, $field) = @_; }, #OrigTrunkGroup -    'userfield',                            #empty#UserDefined -    sub { my($cdr, $field) = @_; },         #empty#PseudoDestinationNumber -    sub { my($cdr, $field) = @_; },         #all-1#PseudoCarrierCode -    sub { my($cdr, $field) = @_; },         #empty#PseudoANI -    sub { my($cdr, $field) = @_; },         #all-1#PseudoFacilityInfo -    sub { my($cdr, $field) = @_; }, #OrigDialedDigits -    sub { my($cdr, $field) = @_; },         #all-1#OrigOutboundCarrier -    sub { my($cdr, $field) = @_; }, #IncomingCarrierID -    sub { my($cdr, $field) = @_; }, #JurisdictionInfo -    sub { my($cdr, $field) = @_; }, #OrigDestDigits -    sub { my($cdr, $field) = @_; }, #InsertTime -    sub { my($cdr, $field) = @_; }, #key -    sub { my($cdr, $field) = @_; },         #empty#AMALineNumber -    sub { my($cdr, $field) = @_; },         #empty#AMAslpID -    sub { my($cdr, $field) = @_; },         #empty#AMADigitsDialedWC -    sub { my($cdr, $field) = @_; }, #OpxOffHook -    sub { my($cdr, $field) = @_; }, #OpxOnHook - -#acctid - primary key -#calldate - Call timestamp (SQL timestamp) +  'taqua' => [ #some of these are kind arbitrary... + +    sub { my($cdr, $field) = @_; },       #XXX interesting RecordType +             # easy to fix: Can't find cdr.cdrtypenum 1 in cdr_type.cdrtypenum + +    sub { my($cdr, $field) = @_; },             #all10#RecordVersion +    sub { my($cdr, $field) = @_; },       #OrigShelfNumber +    sub { my($cdr, $field) = @_; },       #OrigCardNumber +    sub { my($cdr, $field) = @_; },       #OrigCircuit +    sub { my($cdr, $field) = @_; },       #OrigCircuitType +    'uniqueid',                           #SequenceNumber +    'accountcode',                        #SessionNumber +    'src',                                #CallingPartyNumber +    'dst',                                #CalledPartyNumber +    _cdr_date_parser_maker('startdate'),  #CallArrivalTime +    _cdr_date_parser_maker('enddate'),    #CallCompletionTime + +    #Disposition +    #sub { my($cdr, $d ) = @_; $cdr->disposition( $disposition{$d}): }, +    'disposition', +                                          #  -1 => '', +                                          #   0 => '', +                                          # 100 => '', +                                          # 101 => '', +                                          # 102 => '', +                                          # 103 => '', +                                          # 104 => '', +                                          # 105 => '', +                                          # 201 => '', +                                          # 203 => '', + +    _cdr_date_parser_maker('answerdate'), #DispositionTime +    sub { my($cdr, $field) = @_; },       #TCAP +    sub { my($cdr, $field) = @_; },       #OutboundCarrierConnectTime +    sub { my($cdr, $field) = @_; },       #OutboundCarrierDisconnectTime + +    #TermTrunkGroup +    #it appears channels are actually part of trunk groups, but this data +    #is interesting and we need a source and destination place to put it +    'dstchannel',                         #TermTrunkGroup + + +    sub { my($cdr, $field) = @_; },       #TermShelfNumber +    sub { my($cdr, $field) = @_; },       #TermCardNumber +    sub { my($cdr, $field) = @_; },       #TermCircuit +    sub { my($cdr, $field) = @_; },       #TermCircuitType +    sub { my($cdr, $field) = @_; },       #OutboundCarrierId +    'charged_party',                      #BillingNumber +    sub { my($cdr, $field) = @_; },       #SubscriberNumber +    'lastapp',                            #ServiceName +    sub { my($cdr, $field) = @_; },       #some weirdness #ChargeTime +    'lastdata',                           #ServiceInformation +    sub { my($cdr, $field) = @_; },       #FacilityInfo +    sub { my($cdr, $field) = @_; },             #all 1900-01-01 0#CallTraceTime +    sub { my($cdr, $field) = @_; },             #all-1#UniqueIndicator +    sub { my($cdr, $field) = @_; },             #all-1#PresentationIndicator +    sub { my($cdr, $field) = @_; },             #empty#Pin +    sub { my($cdr, $field) = @_; },       #CallType +    sub { my($cdr, $field) = @_; },           #Balt/empty #OrigRateCenter +    sub { my($cdr, $field) = @_; },           #Balt/empty #TermRateCenter + +    #OrigTrunkGroup +    #it appears channels are actually part of trunk groups, but this data +    #is interesting and we need a source and destination place to put it +    'channel',                            #OrigTrunkGroup + +    'userfield',                                #empty#UserDefined +    sub { my($cdr, $field) = @_; },             #empty#PseudoDestinationNumber +    sub { my($cdr, $field) = @_; },             #all-1#PseudoCarrierCode +    sub { my($cdr, $field) = @_; },             #empty#PseudoANI +    sub { my($cdr, $field) = @_; },             #all-1#PseudoFacilityInfo +    sub { my($cdr, $field) = @_; },       #OrigDialedDigits +    sub { my($cdr, $field) = @_; },             #all-1#OrigOutboundCarrier +    sub { my($cdr, $field) = @_; },       #IncomingCarrierID +    'dcontext',                           #JurisdictionInfo +    sub { my($cdr, $field) = @_; },       #OrigDestDigits +    sub { my($cdr, $field) = @_; },       #huh?#InsertTime +    sub { my($cdr, $field) = @_; },       #key +    sub { my($cdr, $field) = @_; },             #empty#AMALineNumber +    sub { my($cdr, $field) = @_; },             #empty#AMAslpID +    sub { my($cdr, $field) = @_; },             #empty#AMADigitsDialedWC +    sub { my($cdr, $field) = @_; },       #OpxOffHook +    sub { my($cdr, $field) = @_; },       #OpxOnHook + +        #acctid - primary key +  #AUTO #calldate - Call timestamp (SQL timestamp)  #clid - Caller*ID with text -#src - Caller*ID number / Source number -#dst - Destination extension -#dcontext - Destination context -#channel - Channel used -#dstchannel - Destination channel if appropriate -#lastapp - Last application if appropriate -#lastdata - Last application data -#startdate - Start of call (UNIX-style integer timestamp) -#answerdate - Answer time of call (UNIX-style integer timestamp) -#enddate - End time of call (UNIX-style integer timestamp) -#duration - Total time in system, in seconds -#billsec - Total time call is up, in seconds -#disposition - What happened to the call: ANSWERED, NO ANSWER, BUSY -#amaflags - What flags to use: BILL, IGNORE etc, specified on a per -#channel basis like accountcode. -#accountcode - CDR account number to use: account -#uniqueid - Unique channel identifier (Unitel/RSLCOM Event ID) +        #XXX src - Caller*ID number / Source number +        #XXX dst - Destination extension +        #dcontext - Destination context +        #channel - Channel used +        #dstchannel - Destination channel if appropriate +        #lastapp - Last application if appropriate +        #lastdata - Last application data +        #startdate - Start of call (UNIX-style integer timestamp) +        #answerdate - Answer time of call (UNIX-style integer timestamp) +        #enddate - End time of call (UNIX-style integer timestamp) +  #HACK#duration - Total time in system, in seconds +  #HACK#XXX billsec - Total time call is up, in seconds +        #disposition - What happened to the call: ANSWERED, NO ANSWER, BUSY +#INT amaflags - What flags to use: BILL, IGNORE etc, specified on a per channel basis like accountcode. +        #accountcode - CDR account number to use: account + +        #uniqueid - Unique channel identifier (Unitel/RSLCOM Event ID)          #userfield - CDR user-defined field -#cdr_type - CDR type - see FS::cdr_type (Usage = 1, S&E = 7, OC&C = 8) -#charged_party - Service number to be billed + +        #X cdrtypenum - CDR type - see FS::cdr_type (Usage = 1, S&E = 7, OC&C = 8) +        #XXX charged_party - Service number to be billed  #upstream_currency - Wholesale currency from upstream -#upstream_price - Wholesale price from upstream +#X upstream_price - Wholesale price from upstream  #upstream_rateplanid - Upstream rate plan ID  #rated_price - Rated (or re-rated) price  #distance - km (need units field?)  #islocal - Local - 1, Non Local = 0  #calltypenum - Type of call - see FS::cdr_calltype -#description - Description (cdr_type 7&8 only) (used for -#cust_bill_pkg.itemdesc) +#X description - Description (cdr_type 7&8 only) (used for cust_bill_pkg.itemdesc)  #quantity - Number of items (cdr_type 7&8 only)  #carrierid - Upstream Carrier ID (see FS::cdr_carrier)  #upstream_rateid - Upstream Rate ID +          #svcnum - Link to customer service (see FS::cust_svc)          #freesidestatus - NULL, done (or something) @@ -719,7 +755,7 @@ sub batch_import {    while ( defined($line=<$fh>) ) {      #skip header... -    if ( ! $body++ && $import_header{'format'} && $line =~ /^[\w\, ]+$/ ) { +    if ( ! $body++ && $import_header{$format} ) { #&& $line =~ /^[\w, "]+$/ ) {        next;      } @@ -759,6 +795,15 @@ sub batch_import {        &{$sub}($cdr, $data);  # $cdr->&{$sub}($data);       } +    if ( $format eq 'taqua' ) { +      if ( $cdr->enddate && $cdr->startdate  ) { #a bit more? +        $cdr->duration( $cdr->enddate - $cdr->startdate  ); +      } +      if ( $cdr->enddate && $cdr->answerdate ) { #a bit more? +        $cdr->billsec(  $cdr->enddate - $cdr->answerdate ); +      }  +    } +      my $error = $cdr->insert;      if ( $error ) {        $dbh->rollback if $oldAutoCommit; diff --git a/httemplate/misc/cdr-import.html b/httemplate/misc/cdr-import.html index a71b25b67..b46607869 100644 --- a/httemplate/misc/cdr-import.html +++ b/httemplate/misc/cdr-import.html @@ -3,8 +3,8 @@  Import a CSV file containing Call Detail Records (CDRs).<BR><BR>  CDR Format:  <SELECT NAME="format"> -% foreach $format ( keys %formats ) { -    <OPTION VALUE="<% $format %>"><% $format{$format} %></OPTION> +% foreach my $format ( keys %formats ) { +    <OPTION VALUE="<% $format %>"><% $formats{$format} %></OPTION>  % }  </SELECT>  <BR><BR> @@ -21,6 +21,6 @@ Filename: <INPUT TYPE="file" NAME="csvfile"><BR><BR>  die "access denied"    unless $FS::CurrentUser::CurrentUser->access_right('Import'); -tie my %formats, FS::cdr->import_formats; +tie my %formats, 'Tie::IxHash', FS::cdr->import_formats;  </%init> | 
