- unless ( @call_details ) {
-
- $included_min{$regionnum} = $rate_detail->min_included
- unless exists $included_min{$regionnum};
-
- my $granularity = $rate_detail->sec_granularity;
- my $seconds = $cdr->billsec; # |ength($cdr->billsec) ? $cdr->billsec : $cdr->duration;
- $seconds += $granularity - ( $seconds % $granularity );
- my $minutes = sprintf("%.1f", $seconds / 60);
- $minutes =~ s/\.0$// if $granularity == 60;
-
- $included_min{$regionnum} -= $minutes;
-
- if ( $included_min{$regionnum} < 0 ) {
- my $charge_min = 0 - $included_min{$regionnum};
- $included_min{$regionnum} = 0;
- $charge = sprintf('%.2f', $rate_detail->min_charge * $charge_min );
- $charges += $charge;
- }
-
+ 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 $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.
+ $etime = 0;
+ }
+ 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;
+ $seconds += $charge_sec;
+
+ $included_min{$regionnum}{$ratetimenum} = $rate_detail->min_included
+ unless exists $included_min{$regionnum}{$ratetimenum};
+
+ my $granularity = $rate_detail->sec_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;
+
+ # 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
+ $total_minutes += $minutes;
+ }
+
+ # 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