-
- ###
- # find the price and add detail to the invoice
- ###
-
- # if $rate_detail is not found, skip this CDR... i.e.
- # don't add it to invoice, don't set its status to done,
- # don't call downstream_csv or something on it...
- # but DO emit a warning...
- #if ( ! $rate_detail && ! scalar(@call_details) ) {}
- if ( ! $rate_detail && $charge eq '' ) {
-
- warn "no rate_detail found for CDR.acctid: ". $cdr->acctid.
- "; skipping\n"
-
- } else { # there *is* a rate_detail (or call_details), proceed...
-
- unless ( @call_details || ( $charge ne '' && $charge == 0 ) ) {
-
- my $seconds_left = $use_duration ? $cdr->duration : $cdr->billsec;
- # charge for the first (conn_sec) seconds
- $seconds = min($seconds_left, $rate_detail->conn_sec);
- $seconds_left -= $seconds;
- $weektime += $seconds;
- $charge = sprintf("%.02f", $rate_detail->conn_charge);
-
- my $total_minutes = 0;
- my $whole_minutes = 1;
- my $etime;
- while($seconds_left) {
- my $ratetimenum = $rate_detail->ratetimenum; # may be empty
-
- # find the end of the current rate interval
- if(@{ $interval_cache{$regionnum} } == 0) {
- # There are no timed rates in this group, so just stay
- # in the default rate_detail for the entire duration.
- # Set an "end" of 1 past the end of the current call.
- $etime = $weektime + $seconds_left + 1;
- }
- elsif($ratetimenum) {
- # This is a timed rate, so go to the etime of this interval.
- # If it's followed by another timed rate, the stime of that
- # interval should match the etime of this one.
- my $interval = $rate_detail->rate_time->contains($weektime);
- $etime = $interval->etime;
- }
- else {
- # This is a default rate, so use the stime of the next
- # interval in the sequence.
- my $next_int = first { $_->stime > $weektime }
- @{ $interval_cache{$regionnum} };
- if ($next_int) {
- $etime = $next_int->stime;
- }
- else {
- # weektime is near the end of the week, so decrement
- # it by a full week and use the stime of the first
- # interval.
- $weektime -= (3600*24*7);
- $etime = $interval_cache{$regionnum}->[0]->stime;
- }
- }
-
- my $charge_sec = min($seconds_left, $etime - $weektime);
-
- $seconds_left -= $charge_sec;
-
- $included_min{$regionnum}{$ratetimenum} = $rate_detail->min_included
- unless exists $included_min{$regionnum}{$ratetimenum};
-
- my $granularity = $rate_detail->sec_granularity;
- $whole_minutes = 0 if $granularity;
-
- # should this be done in every rate interval?
- $charge_sec += $granularity - ( $charge_sec % $granularity )
- if $charge_sec # don't granular-ize 0 billsec calls (bills them)
- && $granularity; # 0 is per call
- my $minutes = sprintf("%.1f", $charge_sec / 60);
- $minutes =~ s/\.0$// if $granularity == 60;
-
- $seconds += $charge_sec;
-
- # per call rather than per minute
- $minutes = 1 unless $granularity;
- $seconds_left = 0 unless $granularity;
-
- $included_min{$regionnum}{$ratetimenum} -= $minutes;
-
- if ( $included_min{$regionnum}{$ratetimenum} <= 0 ) {
- my $charge_min = 0 - $included_min{$regionnum}{$ratetimenum}; #XXX should preserve
- #(display?) this
- $included_min{$regionnum}{$ratetimenum} = 0;
- $charge += sprintf('%.2f', ($rate_detail->min_charge * $charge_min)
- + 0.00000001 ); #so 1.005 rounds to 1.01
- }
-
- # choose next rate_detail
- $rate_detail = $rate->dest_detail({ 'countrycode' => $countrycode,
- 'phonenum' => $number,
- 'weektime' => $etime })
- if($seconds_left);
- # we have now moved forward to $etime
- $weektime = $etime;
-
- } #while $seconds_left
- # this is why we need regionnum/rate_region....
- warn " (rate region $rate_region)\n" if $DEBUG;
-
- $total_minutes = sprintf("%.1f", $seconds / 60);
- $total_minutes =~ s/\.0$// if $whole_minutes;
-
- $classnum = $rate_detail->classnum;
- $charge = sprintf('%.2f', $charge);
-
- @call_details = (
- $cdr->downstream_csv( 'format' => $output_format,
- 'granularity' => $rate_detail->sec_granularity,
- 'minutes' => $total_minutes,
- # why do we go through this hocus-pocus?
- # the cdr *will* show duration here
- # if we forego the 'minutes' key
- # duration vs billsec?
- 'charge' => $charge,
- 'pretty_dst' => $pretty_destnum,
- 'dst_regionname' => $regionname,
- )
- );
- } #if(there is a rate_detail)
-
-
- if ( $charge > 0 ) {
- #just use FS::cust_bill_pkg_detail objects?
- warn "Incrementing \$charges by $charge. Now $charges\n" if $DEBUG;
- $charges += $charge;
- my $call_details;
- my $phonenum = $cust_svc->svc_x->phonenum;
-
- #if ( $self->option('rating_method') eq 'upstream_simple' ) {
- if ( scalar(@call_details) == 1 ) {
- $call_details =
- [ 'C',
- $call_details[0],
- $charge,
- $classnum,
- $phonenum,
- $seconds,
- $regionname,
- ];
- } else { #only used for $rating_method eq 'upstream' now
- $csv->combine(@call_details);
- $call_details =
- [ 'C',
- $csv->string,
- $charge,
- $classnum,
- $phonenum,
- $seconds,
- $regionname,
- ];
- }
- warn " adding details on charge to invoice: [ ".
- join(', ', @{$call_details} ). " ]"
- if ( $DEBUG && ref($call_details) );
- push @$details, $call_details; #\@call_details,
- }
-
- # if the customer flag is on, call "downstream_csv" or something
- # like it to export the call downstream!
- # XXX price plan option to pick format, or something...
- #$downstream_cdr .= $cdr->downstream_csv( 'format' => 'XXX format' )
- # if $spool_cdr;
-
- my $error = $cdr->set_status_and_rated_price( 'done',
- $charge,
- $cust_svc->svcnum,
- );
- die $error if $error;
-