+sub _did_summary {
+ my $self = shift;
+ my $end = $self->_date;
+
+ # start at date of previous invoice + 1 second or 0 if no previous invoice
+ my $start = $self->scalar_sql("SELECT max(_date) FROM cust_bill WHERE custnum = ? and invnum != ?",$self->custnum,$self->invnum);
+ $start = 0 if !$start;
+ $start++;
+
+ my $cust_main = $self->cust_main;
+ my @pkgs = $cust_main->all_pkgs;
+ my($num_activated,$num_deactivated,$num_portedin,$num_portedout,$minutes)
+ = (0,0,0,0,0);
+ my @seen = ();
+ foreach my $pkg ( @pkgs ) {
+ my @h_cust_svc = $pkg->h_cust_svc($end);
+ foreach my $h_cust_svc ( @h_cust_svc ) {
+ next if grep {$_ eq $h_cust_svc->svcnum} @seen;
+ next unless $h_cust_svc->part_svc->svcdb eq 'svc_phone';
+
+ my $inserted = $h_cust_svc->date_inserted;
+ my $deleted = $h_cust_svc->date_deleted;
+ my $phone_inserted = $h_cust_svc->h_svc_x($inserted+5);
+ my $phone_deleted;
+ $phone_deleted = $h_cust_svc->h_svc_x($deleted) if $deleted;
+
+# DID either activated or ported in; cannot be both for same DID simultaneously
+ if ($inserted >= $start && $inserted <= $end && $phone_inserted
+ && (!$phone_inserted->lnp_status
+ || $phone_inserted->lnp_status eq ''
+ || $phone_inserted->lnp_status eq 'native')) {
+ $num_activated++;
+ }
+ else { # this one not so clean, should probably move to (h_)svc_phone
+ my $phone_portedin = qsearchs( 'h_svc_phone',
+ { 'svcnum' => $h_cust_svc->svcnum,
+ 'lnp_status' => 'portedin' },
+ FS::h_svc_phone->sql_h_searchs($end),
+ );
+ $num_portedin++ if $phone_portedin;
+ }
+
+# DID either deactivated or ported out; cannot be both for same DID simultaneously
+ if($deleted >= $start && $deleted <= $end && $phone_deleted
+ && (!$phone_deleted->lnp_status
+ || $phone_deleted->lnp_status ne 'portingout')) {
+ $num_deactivated++;
+ }
+ elsif($deleted >= $start && $deleted <= $end && $phone_deleted
+ && $phone_deleted->lnp_status
+ && $phone_deleted->lnp_status eq 'portingout') {
+ $num_portedout++;
+ }
+
+ # increment usage minutes
+ if ( $phone_inserted ) {
+ my @cdrs = $phone_inserted->get_cdrs('begin'=>$start,'end'=>$end,'billsec_sum'=>1);
+ $minutes = $cdrs[0]->billsec_sum if scalar(@cdrs) == 1;
+ }
+ else {
+ warn "WARNING: no matching h_svc_phone insert record for insert time $inserted, svcnum " . $h_cust_svc->svcnum;
+ }
+
+ # don't look at this service again
+ push @seen, $h_cust_svc->svcnum;
+ }
+ }
+
+ $minutes = sprintf("%d", $minutes);
+ ("Activated: $num_activated Ported-In: $num_portedin Deactivated: "
+ . "$num_deactivated Ported-Out: $num_portedout ",
+ "Total Minutes: $minutes");
+}
+
+sub _items_accountcode_cdr {
+ my $self = shift;
+ my $escape = shift;
+ my $format = shift;
+
+ my $section = { 'amount' => 0,
+ 'calls' => 0,
+ 'duration' => 0,
+ 'sort_weight' => '',
+ 'phonenum' => '',
+ 'description' => 'Usage by Account Code',
+ 'post_total' => '',
+ 'summarized' => '',
+ 'header' => '',
+ };
+ my @lines;
+ my %accountcodes = ();
+
+ foreach my $cust_bill_pkg ( $self->cust_bill_pkg ) {
+ next unless $cust_bill_pkg->pkgnum > 0;
+
+ my @header = $cust_bill_pkg->details_header;
+ next unless scalar(@header);
+ $section->{'header'} = join(',',@header);
+
+ foreach my $detail ( $cust_bill_pkg->cust_bill_pkg_detail ) {
+
+ $section->{'header'} = $detail->formatted('format' => $format)
+ if($detail->detail eq $section->{'header'});
+
+ my $accountcode = $detail->accountcode;
+ next unless $accountcode;
+
+ my $amount = $detail->amount;
+ next unless $amount && $amount > 0;
+
+ $accountcodes{$accountcode} ||= {
+ description => $accountcode,
+ pkgnum => '',
+ ref => '',
+ amount => 0,
+ calls => 0,
+ duration => 0,
+ quantity => '',
+ product_code => 'N/A',
+ section => $section,
+ ext_description => [ $section->{'header'} ],
+ detail_temp => [],
+ };
+
+ $section->{'amount'} += $amount;
+ $accountcodes{$accountcode}{'amount'} += $amount;
+ $accountcodes{$accountcode}{calls}++;
+ $accountcodes{$accountcode}{duration} += $detail->duration;
+ push @{$accountcodes{$accountcode}{detail_temp}}, $detail;
+ }
+ }
+
+ foreach my $l ( values %accountcodes ) {
+ $l->{amount} = sprintf( "%.2f", $l->{amount} );
+ my @sorted_detail = sort { $a->startdate <=> $b->startdate } @{$l->{detail_temp}};
+ foreach my $sorted_detail ( @sorted_detail ) {
+ push @{$l->{ext_description}}, $sorted_detail->formatted('format'=>$format);
+ }
+ delete $l->{detail_temp};
+ push @lines, $l;
+ }
+
+ my @sorted_lines = sort { $a->{'description'} <=> $b->{'description'} } @lines;
+
+ return ($section,\@sorted_lines);
+}
+