#!/usr/bin/perl my $usage = " This script is for fixing CDRs that were supposed to receive Taqua accountcode/caller ID rewrites but didn't. It will reprocess records that were already rewritten, so use with caution. Options: -s DATE: find calls on or after DATE (required) -e DATE: find calls on or before DATE (optional, defaults to right now) -v: show records as they're updated "; use strict; use FS::Misc::Getopt; use FS::Record qw(qsearch qsearchs dbh); use FS::Cursor; our %opt; our $DEBUG; getopts(''); die $usage unless $opt{start}; my $fixed = 0; my $notfound = 0; my $failed = 0; my $extra_sql = 'WHERE lastapp IS NOT NULL '. ' AND cdrtypenum = 1'. ' AND calldate >= to_timestamp('.$opt{start}.')'; if ( $opt{end} ) { $extra_sql .= ' AND calldate < to_timestamp('.$opt{end}.')'; } my $cursor = FS::Cursor->new({ table => 'cdr', hashref => {}, extra_sql => $extra_sql, }); $FS::UID::AutoCommit = 0; while (my $cdr = $cursor->fetch) { # copy-paste from cdrrewrited, except: # - move all conditions for this rewrite into the SQL # - don't check for config option to enable rewriting # - don't retry, don't remember not-found acctids my @status; #find the matching CDR my %search = ( 'sessionnum' => $cdr->sessionnum ); if ( $cdr->lastapp eq 'acctcode' ) { $search{'src'} = $cdr->subscriber; } elsif ( $cdr->lastapp eq 'CallerId' ) { $search{'dst'} = $cdr->subscriber; } if ($DEBUG) { my $desc = '#'.$cdr->acctid . ': sessionnum ' . $cdr->sessionnum . ', '. "subscriber ".$cdr->subscriber; warn $desc."\n"; } my $primary = qsearchs('cdr', \%search); unless ( $primary ) { my $cantfind = "can't find primary CDR with session ". $cdr->sessionnum . ', '; if ($search{'src'}) { $cantfind .= 'src '.$search{'src'}; } else { $cantfind .= 'dst '.$search{'dst'}; } warn "ERROR: $cantfind\n"; $notfound++; next; } else { if ( $cdr->lastapp eq 'acctcode' ) { # lastdata contains the dialed account code $primary->accountcode( $cdr->lastdata ); push @status, 'taqua-accountcode'; warn '#'.$primary->acctid . ': accountcode = '.$cdr->lastdata . "\n" if $DEBUG; } elsif ( $cdr->lastapp eq 'CallerId' ) { # lastdata contains "allowed" or "restricted" # or case variants thereof if ( lc($cdr->lastdata) eq 'restricted' ) { $primary->clid( 'PRIVATE' ); } push @status, 'taqua-callerid'; warn '#'.$primary->acctid . ': clid = '.$cdr->lastdata . "\n" if $DEBUG; } else { warn "unknown Taqua service name: ".$cdr->lastapp."\n"; } #$primary->freesiderewritestatus( 'taqua-accountcode-primary' ); my $error = $primary->replace if $primary->modified; if ( $error ) { warn "WARNING: error rewriting primary CDR: $error\n"; $failed++; dbh->rollback; next; } if ( $primary->freesidestatus eq 'done' and $cdr->lastapp eq 'acctcode' and $primary->rated_price > 0 ) { # then have to update the billing detail also my $detail; if ( $primary->detailnum ) { # has been on 3.x since January 2016... $detail = qsearchs('cust_bill_pkg_detail', { 'detailnum' => $primary->detailnum }); } else { # otherwise, try our best: find a detail with the right price, # source number, and start and end dates $detail = qsearchs('cust_bill_pkg_detail', { 'amount' => $primary->rated_price, 'classnum' => $primary->rated_classnum, 'duration' => $primary->rated_seconds, 'startdate' => $primary->startdate, 'format' => 'C', 'phonenum' => $primary->src, # not perfect }); } if ($detail) { warn "detail #".$detail->detailnum."\n" if $DEBUG; $detail->set('accountcode', $primary->accountcode); $error = $detail->replace; if ($error) { warn "WARNING: error rewriting invoice detail: $error\n"; $failed++; dbh->rollback; next; } } else { warn "ERROR: can't find invoice detail for cdr#".$primary->acctid."\n"; $notfound++; dbh->rollback; next; } } # if this is an acctcode record and the primary was billed $cdr->status('done'); #so it doesn't try to rate } $cdr->freesiderewritestatus( scalar(@status) ? join('/', @status) : 'skipped' ); my $error = $cdr->replace; if ( $error ) { warn "WARNING: error rewriting CDR: $error\n"; dbh->rollback; $failed++; } else { dbh->commit; $fixed++; } } print "Finished. Rewrites: $fixed Primary record not found: $notfound Errors: $failed ";