diff options
| -rw-r--r-- | FS/FS/cust_bill.pm | 26 | ||||
| -rw-r--r-- | FS/FS/cust_bill_pkg.pm | 29 | ||||
| -rw-r--r-- | FS/FS/usage_class.pm | 69 | 
3 files changed, 103 insertions, 21 deletions
| diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index cefbfac81..56099ccf7 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -3741,6 +3741,9 @@ sub _items_svc_phone_sections {    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); +      foreach my $detail ( $cust_bill_pkg->cust_bill_pkg_detail ) {        my $phonenum = $detail->phonenum; @@ -3789,6 +3792,7 @@ sub _items_svc_phone_sections {            'duration' => 0,            'sort_weight' => $usage_class{$detail->classnum}->weight,            'phonenum' => $phonenum, +          'header'  => [ @header ],          };        $sections{"$phonenum $line"}{amount} += $amount;  #subtotal        $sections{"$phonenum $line"}{calls}++; @@ -3819,11 +3823,17 @@ sub _items_svc_phone_sections {    my %sectionmap = ();    my $simple = new FS::usage_class { format => 'simple' }; #bleh -  my $usage_simple = new FS::usage_class { format => 'usage_simple' }; #bleh    foreach ( keys %sections ) { +    my @header = @{ $sections{$_}{header} || [] }; +    my $usage_simple = +      new FS::usage_class { format => 'usage_'. (scalar(@header) || 6). 'col' };      my $summary = $sections{$_}{sort_weight} < 0 ? 1 : 0;      my $usage_class = $summary ? $simple : $usage_simple;      my $ending = $summary ? ' usage charges' : ''; +    my %gen_opt = (); +    unless ($summary) { +      $gen_opt{label} = [ map{ &{$escape}($_) } @header ]; +    }      $sectionmap{$_} = { 'description' => &{$escape}($_. $ending),                          'amount'    => $sections{$_}{amount},    #subtotal                          'calls'       => $sections{$_}{calls}, @@ -3834,7 +3844,7 @@ sub _items_svc_phone_sections {                          'sort_weight' => $sections{$_}{sort_weight},                          'post_total'  => $summary, #inspire pagebreak                          ( -                          ( map { $_ => $usage_class->$_($format) } +                          ( map { $_ => $usage_class->$_($format, %gen_opt) }                              qw( description_generator                                  header_generator                                  total_generator @@ -3943,12 +3953,12 @@ sub _items_pkg {  }  sub _taxsort { -  return 0 unless $a cmp $b; -  return -1 if $b eq 'Tax'; -  return 1 if $a eq 'Tax'; -  return -1 if $b eq 'Other surcharges'; -  return 1 if $a eq 'Other surcharges'; -  $a cmp $b; +  return 0 unless $a->itemdesc cmp $b->itemdesc; +  return -1 if $b->itemdesc eq 'Tax'; +  return 1 if $a->itemdesc eq 'Tax'; +  return -1 if $b->itemdesc eq 'Other surcharges'; +  return 1 if $a->itemdesc eq 'Other surcharges'; +  $a->itemdesc cmp $b->itemdesc;  }  sub _items_tax { diff --git a/FS/FS/cust_bill_pkg.pm b/FS/FS/cust_bill_pkg.pm index c825c1567..d396f8239 100644 --- a/FS/FS/cust_bill_pkg.pm +++ b/FS/FS/cust_bill_pkg.pm @@ -480,6 +480,35 @@ sub details {      #qsearch ( 'cust_bill_pkg_detail', { 'lineitemnum' => $self->lineitemnum });  } +=item details_header [ OPTION => VALUE ... ] + +Returns a list representing an invoice line item detail header, if any. +This relies on the behavior of voip_cdr in that it expects the header +to be the first CSV formatted detail (as is expected by invoice generation +routines).  Returns the empty list otherwise. + +=cut + +sub details_header { +  my $self = shift; +  return '' unless defined dbdef->table('cust_bill_pkg_detail'); + +  eval "use Text::CSV_XS;"; +  die $@ if $@; +  my $csv = new Text::CSV_XS; + +  my @detail =  +    qsearch ({ 'table'    => 'cust_bill_pkg_detail', +               'hashref'  => { 'billpkgnum' => $self->billpkgnum, +                               'format'     => 'C', +                             }, +               'order_by' => 'ORDER BY detailnum LIMIT 1', +            }); +  return() unless scalar(@detail); +  $csv->parse($detail[0]->detail) or return (); +  $csv->fields; +} +  =item desc  Returns a description for this line item.  For typical line items, this is the diff --git a/FS/FS/usage_class.pm b/FS/FS/usage_class.pm index b64d26ac4..26520e579 100644 --- a/FS/FS/usage_class.pm +++ b/FS/FS/usage_class.pm @@ -127,6 +127,7 @@ my %summary_formats = (      'align'  => [ qw( l r r r ) ],      'span'   => [ qw( 4 1 1 1 ) ],            # unitprices?      'width'  => [ qw( 8.2cm 2.5cm 1.4cm 1.6cm ) ],   # don't like this +    'show'   => 1,    },    'simpler' => {       'label' =>  [ qw( Description Calls Amount ) ], @@ -138,6 +139,7 @@ my %summary_formats = (      'align'  => [ qw( l r r ) ],      'span'   => [ qw( 5 1 1 ) ],      'width'  => [ qw( 10.7cm 1.4cm 1.6cm ) ],   # don't like this +    'show'   => 1,    },    'usage_simple' => {       'label' => [ qw( Date Time Number Destination Duration Amount ) ], @@ -147,16 +149,56 @@ my %summary_formats = (                    sub { ' ' },                    sub { ' ' },                    sub { ' ' }, +                  sub { my $href = shift;  #ugh! making bunk of 'normalization' +                        $href->{subtotal} ? $href->{subtotal} : ' ' +                      }, +                ], +    'align'  => [ qw( l l l l r r ) ], +    'span'   => [ qw( 1 1 1 1 1 2 ) ],            # unitprices? +    'width'  => [ qw( 4.3cm 1.4cm 2.5cm 2.5cm 1.4cm 1.6cm ) ],# don't like this +    'show'   => 0, +  }, +  'usage_6col' => {  +    'label' => [ qw( col1 col2 col3 col4 col5 col6 ) ], +    'fields' => [                    sub { ' ' }, +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { my $href = shift;  #ugh! making bunk of 'normalization' +                        $href->{subtotal} ? $href->{subtotal} : ' ' +                      },                  ],      'align'  => [ qw( l l l l r r ) ], -    'span'   => [ qw( 2 1 1 1 1 1 ) ],            # unitprices? +    'span'   => [ qw( 1 1 1 1 1 2 ) ],            # unitprices?      'width'  => [ qw( 4.3cm 1.4cm 2.5cm 2.5cm 1.4cm 1.6cm ) ],# don't like this +    'show'   => 0, +  }, +  'usage_7col' => {  +    'label' => [ qw( col1 col2 col3 col4 col5 col6 col7 ) ], +    'fields' => [ +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { ' ' }, +                  sub { my $href = shift;  #ugh! making bunk of 'normalization' +                        $href->{subtotal} ? $href->{subtotal} : ' ' +                      }, +                ], +    'align'  => [ qw( l l l l l r r ) ], +    'span'   => [ qw( 1 1 1 1 1 1 1 ) ],            # unitprices? +    'width'  => [ qw( 2.9cm 1.4cm 1.4cm 2.5cm 2.5cm 1.4cm 1.6cm ) ],# don't like this +    'show'   => 0,    },  );  sub summary_formats_labelhash { -  map { $_ => join(',', @{$summary_formats{$_}{label}}) } keys %summary_formats; +  map { $_ => join(',', @{$summary_formats{$_}{label}}) } +    grep { $summary_formats{$_}{show} } +    keys %summary_formats;  }  =item header_generator FORMAT @@ -173,15 +215,16 @@ my %html_align = (  );  sub _generator_defaults { -  my ( $self, $format ) = ( shift, shift ); -  return ( $summary_formats{$self->format}, ' ', ' ', ' ', sub { shift } ); +  my ( $self, $format, %opt ) = @_; +  my %format = ( %{ $summary_formats{$self->format} }, %opt ); +  return ( \%format, ' ', ' ', ' ', sub { shift } );  }  sub header_generator { -  my ( $self, $format ) = ( shift, shift ); +  my ( $self, $format, %opt ) = @_;    my ( $f, $prefix, $suffix, $separator, $column ) = -    $self->_generator_defaults($format); +    $self->_generator_defaults($format, %opt);    if ($format eq 'latex') {      $prefix = "\\hline\n\\rule{0pt}{2.5ex}\n\\makebox[1.4cm]{}&\n"; @@ -205,7 +248,7 @@ sub header_generator {      my @args = @_;      my @result = (); -    foreach  (my $i = 0; $f->{label}->[$i]; $i++) { +    foreach  (my $i = 0; exists($f->{label}->[$i]); $i++) {        push @result,          &{$column}( map { $f->{$_}->[$i] } qw(label align span width) );      } @@ -223,10 +266,10 @@ usage_class.  FORMAT is either html or latex  =cut  sub description_generator { -  my ( $self, $format ) = ( shift, shift ); +  my ( $self, $format, %opt ) = @_;    my ( $f, $prefix, $suffix, $separator, $column ) = -    $self->_generator_defaults($format); +    $self->_generator_defaults($format, %opt);    if ($format eq 'latex') {      $prefix = "\\hline\n\\multicolumn{1}{c}{\\rule{0pt}{2.5ex}~} &\n"; @@ -269,13 +312,13 @@ usage_class.  FORMAT is either html or latex  =cut  sub total_generator { -  my ( $self, $format ) = ( shift, shift ); +  my ( $self, $format, %opt ) = @_;  #  $OUT .= '\FStotaldesc{' . $section->{'description'} . ' Total}' .  #          '{' . $section->{'subtotal'} . '}' . "\n";    my ( $f, $prefix, $suffix, $separator, $column ) = -    $self->_generator_defaults($format); +    $self->_generator_defaults($format, %opt);    my $style = '';    if ($format eq 'latex') { @@ -328,13 +371,13 @@ usage_class.  FORMAT is either html or latex  # total_item and amount vs total_amount -- another array of functions?  sub total_line_generator { -  my ( $self, $format ) = ( shift, shift ); +  my ( $self, $format, %opt ) = @_;  #     $OUT .= '\FStotaldesc{' . $line->{'total_item'} . '}' .  #             '{' . $line->{'total_amount'} . '}' . "\n";    my ( $f, $prefix, $suffix, $separator, $column ) = -    $self->_generator_defaults($format); +    $self->_generator_defaults($format, %opt);    my $style = '';    if ($format eq 'latex') { | 
