=item svcnum - Link to customer service (see L<FS::cust_svc>)
-=item freesidestatus - NULL, done (or something)
+=item freesidestatus - NULL, processing-tiered, done
-=item freesiderewritestatus - NULL, done (or something)
+=item freesiderewritestatus - NULL, done, skipped
=item cdrbatch
sub set_status_and_rated_price {
my($self, $status, $rated_price, $svcnum, %opt) = @_;
- if($opt{'inbound'}) {
- my $term = qsearchs('cdr_termination', {
- acctid => $self->acctid,
- termpart => 1 # inbound
- });
+
+ if ($opt{'inbound'}) {
+
+ my $term = $self->cdr_termination( 1 ); #1: inbound
my $error;
- if($term) {
+ if ( $term ) {
warn "replacing existing cdr status (".$self->acctid.")\n" if $term;
$error = $term->delete;
return $error if $error;
termpart => 1,
rated_price => $rated_price,
status => $status,
- svcnum => $svcnum,
});
+ $term->rated_seconds($opt{rated_seconds}) if exists($opt{rated_seconds});
+ $term->rated_minutes($opt{rated_minutes}) if exists($opt{rated_minutes});
+ $term->svcnum($svcnum) if $svcnum;
return $term->insert;
- }
- else {
+
+ } else {
+
$self->freesidestatus($status);
$self->rated_price($rated_price);
+ $self->rated_seconds($opt{rated_seconds}) if exists($opt{rated_seconds});
+ $self->rated_minutes($opt{rated_minutes}) if exists($opt{rated_minutes});
$self->svcnum($svcnum) if $svcnum;
return $self->replace();
+
}
}
+=item cdr_termination [ TERMPART ]
+
+=cut
+
+sub cdr_termination {
+ my $self = shift;
+
+ if ( scalar(@_) && $_[0] ) {
+ my $termpart = shift;
+
+ qsearchs('cdr_termination', { acctid => $self->acctid,
+ termpart => $termpart,
+ }
+ );
+
+ } else {
+
+ qsearch('cdr_termination', { acctid => $self->acctid, } );
+
+ }
+
+}
+
=item calldate_unix
Parses the calldate in SQL string format and returns a UNIX timestamp.
'invoice_header' => "Date,Time,Called From,Destination,Duration,Price",
#"Date,Time,Name,Called From,Destination,Duration,Price",
},
+ 'accountcode_simple' => {
+ 'name' => 'Simple with accountcode',
+ 'invoice_header' => "Date,Time,Called From,Account,Duration,Price",
+ },
'basic' => {
'name' => 'Basic',
'invoice_header' => "Date/Time,Called Number,Min/Sec,Price",
'name' => 'Default plus accountcode',
'invoice_header' => 'Date,Time,Account,Number,Destination,Duration,Price',
},
+ 'description_default' => {
+ 'name' => 'Default with description field as destination',
+ 'invoice_header' => 'Caller,Date,Time,Number,Destination,Duration,Price',
+ },
+ 'sum_duration' => {
+ 'name' => 'Summary (one line per service, with duration)',
+ 'invoice_header' => 'Caller,Rate,Calls,Minutes,Price',
+ },
+ 'sum_count' => {
+ 'name' => 'Summary (one line per service, with count)',
+ 'invoice_header' => 'Caller,Rate,Messages,Price',
+ },
);
my %export_formats = ();
my $duration_sub = sub {
my($cdr, %opt) = @_;
my $sec = $opt{seconds} || $cdr->billsec;
- if ( length($opt{granularity}) &&
+ if ( defined $opt{granularity} &&
$opt{granularity} == 0 ) { #per call
return '1 call';
}
- elsif ( $opt{granularity} == 60 ) {#full minutes
+ elsif ( defined $opt{granularity} && $opt{granularity} == 60 ) {#full minutes
my $min = int($sec/60);
$min++ if $sec%60;
return $min.'m';
}
};
+ my $price_sub = sub {
+ my ($cdr, %opt) = @_;
+ my $price;
+ if ( defined($opt{charge}) ) {
+ $price = $opt{charge};
+ }
+ elsif ( $opt{inbound} ) {
+ my $term = $cdr->cdr_termination(1); # 1 = inbound
+ $price = $term->rated_price if defined $term;
+ }
+ else {
+ $price = $cdr->rated_price;
+ }
+ length($price) ? ($opt{money_char} . $price) : '';
+ };
+
+ my $src_sub = sub { $_[0]->clid || $_[0]->src };
+
%export_formats = (
'simple' => [
sub { time2str($date_format, shift->calldate_unix ) }, #DATE
'dst', #NUMBER_DIALED
$duration_sub, #DURATION
#sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
- sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
+ $price_sub,
],
'simple2' => [
sub { time2str($date_format, shift->calldate_unix ) }, #DATE
sub { time2str('%r', shift->calldate_unix ) }, #TIME
#'userfield', #USER
- 'src', #called from
+ $src_sub, #called from
'dst', #NUMBER_DIALED
$duration_sub, #DURATION
#sub { sprintf('%.3f', shift->upstream_price ) }, #PRICE
- sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
+ $price_sub,
+ ],
+ 'accountcode_simple' => [
+ sub { time2str($date_format, shift->calldate_unix ) }, #DATE
+ sub { time2str('%r', shift->calldate_unix ) }, #TIME
+ $src_sub, #called from
+ 'accountcode', #NUMBER_DIALED
+ $duration_sub, #DURATION
+ $price_sub,
+ ],
+ 'sum_duration' => [
+ # for summary formats, the CDR is a fictitious object containing the
+ # total billsec and the phone number of the service
+ $src_sub,
+ sub { my($cdr, %opt) = @_; $opt{ratename} },
+ sub { my($cdr, %opt) = @_; $opt{count} },
+ sub { my($cdr, %opt) = @_; int($opt{seconds}/60).'m' },
+ $price_sub,
+ ],
+ 'sum_count' => [
+ $src_sub,
+ sub { my($cdr, %opt) = @_; $opt{ratename} },
+ sub { my($cdr, %opt) = @_; $opt{count} },
+ $price_sub,
],
'basic' => [
sub { time2str('%d %b - %I:%M %p', shift->calldate_unix) },
'dst',
$duration_sub,
- sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; }, #PRICE
+ $price_sub,
],
'default' => [
$duration_sub,
#PRICE
- sub { my($cdr, %opt) = @_; $opt{money_char}. $opt{charge}; },
-
+ $price_sub,
],
);
- $export_formats{'source_default'} = [ 'src', @{ $export_formats{'default'} }, ];
+ $export_formats{'source_default'} = [ $src_sub, @{ $export_formats{'default'} }, ];
$export_formats{'accountcode_default'} =
[ @{ $export_formats{'default'} }[0,1],
'accountcode',
@{ $export_formats{'default'} }[2..5],
];
+ my @default = @{ $export_formats{'default'} };
+ $export_formats{'description_default'} =
+ [ $src_sub, @default[0..2],
+ sub { my($cdr, %opt) = @_; $cdr->description },
+ @default[4,5] ];
- %export_formats
+ return %export_formats;
}
+=item downstream_csv OPTION => VALUE ...
+
+Returns a string of formatted call details for display on an invoice.
+
+Options:
+
+format
+
+charge - override the 'rated_price' field of the CDR
+
+seconds - override the 'billsec' field of the CDR
+
+count - number of usage events included in this record, for summary formats
+
+ratename - name of the rate table used to rate this call
+
+granularity
+
+=cut
+
sub downstream_csv {
my( $self, %opt ) = @_;
}
@{ $formats{$format} };
+ return @columns if defined $opt{'keeparray'};
+
my $status = $csv->combine(@columns);
die "FS::CDR: error combining ". $csv->error_input(). "into downstream CSV"
unless $status;
return $error;
}
- my @cdr_termination = qsearch('cdr_termination',
- { 'acctid' => $self->acctid } );
- foreach my $cdr_termination ( @cdr_termination ) {
- $cdr_termination->status('');
- $error = $cdr_termination->replace;
+ foreach my $cdr_termination ( $self->cdr_termination ) {
+ #$cdr_termination->status('');
+ #$error = $cdr_termination->replace;
+ $error = $cdr_termination->delete;
if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return $error;
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
}
}
},
#drop the || 'csv' to allow auto xls for csv types?
- 'format_types' => { map { $_ => ( lc($cdr_info{$_}->{'type'}) || 'csv' ); }
+ 'format_types' => { map { $_ => lc($cdr_info{$_}->{'type'} || 'csv'); }
keys %cdr_info
},
my $iopt = _import_options;
$opt->{$_} = $iopt->{$_} foreach keys %$iopt;
+ if ( defined $opt->{'cdrtypenum'} ) {
+ $opt->{'preinsert_callback'} = sub {
+ my($record,$param) = (shift,shift);
+ $record->cdrtypenum($opt->{'cdrtypenum'});
+ '';
+ };
+ }
+
FS::Record::batch_import( $opt );
}