+ my $included_min_total = ($self->option('min_included', 1) || 0)
+ * ($cust_pkg->quantity || 1);
+ #single price rating
+ #or region group
+ my $included_min_left = $included_min_total;
+
+ my $included_calls = $self->option('calls_included', 1) || 0;
+ $included_calls *= ($cust_pkg->quantity || 1);
+
+ my $cdr_svc_method = $self->option('cdr_svc_method',1)||'svc_phone.phonenum';
+ my $rating_method = $self->option('rating_method') || 'prefix';
+ my %detail_included_min = ();
+
+ my $output_format = $self->option('output_format', 'Hush!')
+ || ( $rating_method eq 'upstream_simple'
+ ? 'simple'
+ : 'default'
+ );
+
+ my $usage_showzero = $self->option('usage_showzero', 1);
+
+ my $formatter = FS::detail_format->new($output_format,
+ buffer => $details,
+ locale => $cust_pkg->cust_main->locale,
+ rounding => ($self->option_cacheable('rounding') || 2),
+ );
+
+ my $use_duration = $self->option('use_duration');
+
+ my($svc_table, $svc_field, $by_ip_addr) = split('\.', $cdr_svc_method);
+
+ my @cust_svc;
+ if( $self->option('bill_inactive_svcs',1) ) {
+ #XXX in this mode do we need to restrict the set of CDRs by date also?
+ @cust_svc = $cust_pkg->h_cust_svc($$sdate, $last_bill);
+ }
+ else {
+ @cust_svc = $cust_pkg->cust_svc;
+ }
+ @cust_svc = grep { $_->part_svc->svcdb eq $svc_table } @cust_svc;
+
+ foreach my $cust_svc (@cust_svc) {
+
+ my $svc_x;
+ if( $self->option('bill_inactive_svcs',1) ) {
+ $svc_x = $cust_svc->h_svc_x($$sdate, $last_bill);
+ }
+ else {
+ $svc_x = $cust_svc->svc_x;
+ }
+
+ unless ( $svc_x ) {
+ my $h = $self->option('bill_inactive_svcs',1) ? 'h_' : '';
+ warn "WARNING: no $h$svc_table for svcnum ". $cust_svc->svcnum. "\n";
+ }
+
+ my %options = (
+ 'disable_src' => $self->option('disable_src',1),
+ 'default_prefix' => $self->option('default_prefix',1),
+ 'cdrtypenum' => $self->option('use_cdrtypenum',1),
+ 'calltypenum' => $self->option('use_calltypenum',1),
+ 'status' => '',
+ 'for_update' => 1,
+ );
+ if ( $self->option('bill_only_pkg_dates') ) {
+ $options{'begin'} = $last_bill;
+ $options{'end'} = $$sdate;
+ }
+ if ( $svc_field eq 'svcnum' ) {
+ $options{'by_svcnum'} = 1;
+ } elsif ($svc_table eq 'svc_pbx' and $svc_field eq 'ip') {
+ $options{'by_ip_addr'} = $by_ip_addr;
+ }
+
+ #my @invoice_details_sort;
+
+ # for tagging invoice details
+ # (unfortunate; should be a svc_x class method or table_info item or
+ # something)
+ my $phonenum;
+ if ( $svc_table eq 'svc_phone' ) {
+ $phonenum = $svc_x->phonenum;
+ } elsif ( $svc_table eq 'svc_pbx' ) {
+ $phonenum = $svc_x->title;
+ } elsif ( $svc_table eq 'svc_acct' ) {
+ $phonenum = $svc_x->username;
+ }
+ $formatter->phonenum($phonenum);
+
+ #first rate any outstanding CDRs not yet rated
+ # use FS::Cursor for this starting in 4.x
+ my $cdr_search = $svc_x->psearch_cdrs(%options);
+ $cdr_search->limit(1000);
+ $cdr_search->increment(0); # because we're changing their status as we go
+ while ( my $cdr = $cdr_search->fetch ) {
+
+ my $error = $cdr->rate(
+ 'part_pkg' => $self,
+ 'cust_pkg' => $cust_pkg,
+ 'svcnum' => $svc_x->svcnum,
+ 'plan_included_min' => \$included_min_left,
+ 'detail_included_min_hashref' => \%detail_included_min,
+ );
+ die $error if $error; #??
+
+ $cdr_search->adjust(1) if $cdr->freesidestatus eq '';
+ # it was skipped without changing status, so increment the
+ # offset so that we don't re-fetch it on refill