diff options
Diffstat (limited to 'httemplate/graph')
| -rw-r--r-- | httemplate/graph/cust_bill_pkg.cgi | 142 | ||||
| -rw-r--r-- | httemplate/graph/cust_bill_pkg_detail.cgi | 137 | ||||
| -rw-r--r-- | httemplate/graph/cust_pkg.cgi | 63 | ||||
| -rw-r--r-- | httemplate/graph/elements/monthly.html | 351 | ||||
| -rw-r--r-- | httemplate/graph/money_time.cgi | 98 | ||||
| -rw-r--r-- | httemplate/graph/report_cust_bill_pkg.html | 54 | ||||
| -rw-r--r-- | httemplate/graph/report_cust_bill_pkg_detail.html | 48 | ||||
| -rw-r--r-- | httemplate/graph/report_cust_pkg.html | 28 | ||||
| -rw-r--r-- | httemplate/graph/report_money_time.html | 43 | 
9 files changed, 964 insertions, 0 deletions
| diff --git a/httemplate/graph/cust_bill_pkg.cgi b/httemplate/graph/cust_bill_pkg.cgi new file mode 100644 index 000000000..ccd41b8a9 --- /dev/null +++ b/httemplate/graph/cust_bill_pkg.cgi @@ -0,0 +1,142 @@ +<% include('elements/monthly.html', +                'title'        => $title, +                'graph_type'   => 'Mountain', +                'items'        => \@items, +                'params'       => \@params, +                'labels'       => \@labels, +                'graph_labels' => \@labels, +                'colors'       => \@colors, +                'links'        => \@links, +                'remove_empty' => 1, +                'bottom_total' => 1, +                'bottom_link'  => $bottom_link, +                'agentnum'     => $agentnum, +             ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +my $link = "${p}search/cust_bill_pkg.cgi?nottax=1;include_comp_cust=1"; +my $bottom_link = "$link;"; + +my $use_override         = $cgi->param('use_override')         ? 1 : 0; +my $use_usage            = $cgi->param('use_usage')            ? 1 : 0; +my $average_per_cust_pkg = $cgi->param('average_per_cust_pkg') ? 1 : 0; + +#XXX or virtual +my( $agentnum, $sel_agent ) = ('', ''); +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { +  $agentnum = $1; +  $bottom_link .= "agentnum=$agentnum;"; +  $sel_agent = qsearchs('agent', { 'agentnum' => $agentnum } ); +  die "agentnum $agentnum not found!" unless $sel_agent; +} +my $title = $sel_agent ? $sel_agent->agent.' ' : ''; +$title .= 'Sales Report (Gross)'; +$title .= ', average per customer package'  if $average_per_cust_pkg; + +#classnum (here) +# 0: all classes +# not specified: empty class +# N: classnum +#classnum (link) +# not specified: all classes +# 0: empty class +# N: classnum + +#false lazinessish w/FS::cust_pkg::search_sql (previously search/cust_pkg.cgi) +my $classnum = 0; +my @pkg_class = (); +if ( $cgi->param('classnum') =~ /^(\d*)$/ ) { +  $classnum = $1; + +  if ( $classnum ) { #a specific class + +    @pkg_class = ( qsearchs('pkg_class', { 'classnum' => $classnum } ) ); +    die "classnum $classnum not found!" unless $pkg_class[0]; +    $title .= $pkg_class[0]->classname.' '; +    $bottom_link .= "classnum=$classnum;"; + +  } elsif ( $classnum eq '' ) { #the empty class + +    $title .= 'Empty class '; +    @pkg_class = ( '(empty class)' ); +    $bottom_link .= "classnum=0;"; + +  } elsif ( $classnum eq '0' ) { #all classes + +    @pkg_class = qsearch('pkg_class', {} ); # { 'disabled' => '' } ); +    push @pkg_class, '(empty class)'; + +  } +} +#eslaf + +my $hue = 0; +#my $hue_increment = 170; +#my $hue_increment = 145; +my $hue_increment = 125; + +my @items  = (); +my @params = (); +my @labels = (); +my @colors = (); +my @links  = (); + +foreach my $agent ( $sel_agent || qsearch('agent', { 'disabled' => '' } ) ) { + +  my $col_scheme = Color::Scheme->new +                     ->from_hue($hue) #->from_hex($agent->color) +                     ->scheme('analogic') +                   ; +  my @recur_colors = (); +  my @onetime_colors = (); + +  ### fixup the color handling for package classes... +  ### and usage +  my $n = 0; + +  foreach my $pkg_class ( @pkg_class ) { +    foreach my $component ( $use_usage ? ('recurring', 'usage') : ('') ) { + +      push @items, 'cust_bill_pkg'; + +      push @labels, +        ( $sel_agent ? '' : $agent->agent.' ' ). +        ( $classnum eq '0' +            ? ( ref($pkg_class) ? $pkg_class->classname : $pkg_class )  +            : '' +        ). +        " $component"; + +      my $row_classnum = ref($pkg_class) ? $pkg_class->classnum : 0; +      my $row_agentnum = $agent->agentnum; +      push @params, [ 'classnum'             => $row_classnum, +                      'agentnum'             => $row_agentnum, +                      'use_override'         => $use_override, +                      'use_usage'            => $component, +                      'average_per_cust_pkg' => $average_per_cust_pkg, +                    ]; + +      push @links, "$link;agentnum=$row_agentnum;classnum=$row_classnum;". +                   "use_override=$use_override;use_usage=$component;"; + +      @recur_colors = ($col_scheme->colors)[0,4,8,1,5,9] +        unless @recur_colors; +      @onetime_colors = ($col_scheme->colors)[2,6,10,3,7,11] +        unless @onetime_colors; +      push @colors, shift @recur_colors; + +    } +  } + +  $hue += $hue_increment; + +} + +#use Data::Dumper; +#warn Dumper(\@items); + +</%init> diff --git a/httemplate/graph/cust_bill_pkg_detail.cgi b/httemplate/graph/cust_bill_pkg_detail.cgi new file mode 100644 index 000000000..642a9ecf3 --- /dev/null +++ b/httemplate/graph/cust_bill_pkg_detail.cgi @@ -0,0 +1,137 @@ +<% include('elements/monthly.html', +                'title'        => $title. 'Rated Call Sales Report (Gross)', +                'graph_type'   => 'Mountain', +                'items'        => \@items, +                'params'       => \@params, +                'labels'       => \@labels, +                'graph_labels' => \@labels, +                'colors'       => \@colors, +                'remove_empty' => 1, +                'bottom_total' => 1, +                'agentnum'     => $agentnum, +             ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +#XXX or virtual +my( $agentnum, $sel_agent ) = ('', ''); +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { +  $agentnum = $1; +  $sel_agent = qsearchs('agent', { 'agentnum' => $agentnum } ); +  die "agentnum $agentnum not found!" unless $sel_agent; +} +my $title = $sel_agent ? $sel_agent->agent.' ' : ''; + +#false lazinessish w/FS::cust_pkg::search_sql (previously search/cust_pkg.cgi) +my $classnum = ''; +if ( $cgi->param('classnum') =~ /^(\d*)$/ ) { +  $classnum = $1; + +  if ( $classnum ) { #a specific class + +    my $pkg_class = ( qsearchs('pkg_class', { 'classnum' => $classnum } ) ); +    die "classnum $classnum not found!" unless $pkg_class; +    $title .= $pkg_class->classname.' '; + +  } elsif ( $classnum eq '' ) { #the empty class + +    $title .= 'Empty class '; +    # FS::Report::Table::Monthly.pm has the converse view +    $classnum = 0; + +  } elsif ( $classnum eq '0' ) { #all classes + +    # FS::Report::Table::Monthly.pm has the converse view +    $classnum = ''; +  } +} +#eslaf + +my $use_override = 0; +$use_override = 1 if ( $cgi->param('use_override') ); + +my $usageclass = 0; +my @usage_class = (); +if ( $cgi->param('usageclass') =~ /^(\d*)$/ ) { +  $usageclass = $1; + +  if ( $usageclass ) { #a specific class + +    @usage_class = ( qsearchs('usage_class', { 'classnum' => $usageclass } ) ); +    die "usage class $usageclass not found!" unless $usage_class[0]; +    $title .= $usage_class[0]->classname.' '; + +  } elsif ( $usageclass eq '' ) { #the empty class -- legacy + +    $title .= 'Empty usage class '; +    @usage_class = ( '(empty usage class)' ); + +  } elsif ( $usageclass eq '0' ) { #all classes + +    @usage_class = qsearch('usage_class', {} ); # { 'disabled' => '' } ); +    push @usage_class, '(empty usage class)'; + +  } +} +#eslaf + +my $hue = 0; +#my $hue_increment = 170; +#my $hue_increment = 145; +my $hue_increment = 125; + +my @items  = (); +my @params = (); +my @labels = (); +my @colors = (); + +foreach my $agent ( $sel_agent || qsearch('agent', { 'disabled' => '' } ) ) { + +  my $col_scheme = Color::Scheme->new +                     ->from_hue($hue) #->from_hex($agent->color) +                     ->scheme('analogic') +                   ; +  my @recur_colors = (); +  my @onetime_colors = (); + +  ### fixup the color handling for usage classes... +  my $n = 0; + +  foreach my $usage_class ( @usage_class ) { + +    push @items, 'cust_bill_pkg_detail'; + +    push @labels, +      ( $sel_agent ? '' : $agent->agent.' ' ). +      ( $usageclass eq '0' +          ? ( ref($usage_class) ? $usage_class->classname : $usage_class )  +          : '' +      ); + +    my $row_classnum = ref($usage_class) ? $usage_class->classnum : 0; +    my $row_agentnum = $agent->agentnum; +    push @params, [ 'usageclass'   => $row_classnum, +                    'agentnum'     => $row_agentnum, +                    'use_override' => $use_override, +                    'classnum'     => $classnum, +                  ]; + +    @recur_colors = ($col_scheme->colors)[0,4,8,1,5,9] +      unless @recur_colors; +    @onetime_colors = ($col_scheme->colors)[2,6,10,3,7,11] +      unless @onetime_colors; +    push @colors, shift @recur_colors; + +  } + +  $hue += $hue_increment; + +} + +#use Data::Dumper; +#warn Dumper(\@items); + +</%init> diff --git a/httemplate/graph/cust_pkg.cgi b/httemplate/graph/cust_pkg.cgi new file mode 100644 index 000000000..21ce07d21 --- /dev/null +++ b/httemplate/graph/cust_pkg.cgi @@ -0,0 +1,63 @@ +<% include('elements/monthly.html', +                'title'         => $agentname. 'Package Churn', +                'items'         => \@items, +                'labels'        => \%label, +                'graph_labels'  => \%graph_label, +                'colors'        => \%color, +                'links'         => \%link, +                'agentnum'      => $agentnum, +                'sprintf'       => '%u', +                'disable_money' => 1, +             ) +%> +<%init> + +#XXX use a different ACL for package churn? +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +#false laziness w/money_time.cgi, cust_bill_pkg.cgi + +#XXX or virtual +my( $agentnum, $agent ) = ('', ''); +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { +  $agentnum = $1; +  $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); +  die "agentnum $agentnum not found!" unless $agent; +} + +my $agentname = $agent ? $agent->agent.' ' : ''; + +my @items = qw( setup_pkg susp_pkg cancel_pkg ); + +my %label = ( +  'setup_pkg'  => 'New orders', +  'susp_pkg'   => 'Suspensions', +#  'unsusp' => 'Unsuspensions', +  'cancel_pkg' => 'Cancellations', +); +my %graph_label = %label; + +my %color = ( +  'setup_pkg'   => '00cc00', #green +  'susp_pkg'    => 'ff9900', #yellow +  #'unsusp'  => '', #light green? +  'cancel_pkg'  => 'cc0000', #red ? 'ff0000' +); + +my %link = ( +  'setup_pkg'  => { 'link' => "${p}search/cust_pkg.cgi?agentnum=$agentnum;", +                    'fromparam' => 'setup_begin', +                    'toparam'   => 'setup_end', +                  }, +  'susp_pkg'   => { 'link' => "${p}search/cust_pkg.cgi?agentnum=$agentnum;", +                    'fromparam' => 'susp_begin', +                    'toparam'   => 'susp_end', +                  }, +  'cancel_pkg' => { 'link' => "${p}search/cust_pkg.cgi?agentnum=$agentnum;", +                    'fromparam' => 'cancel_begin', +                    'toparam'   => 'cancel_end', +                  }, +); + +</%init> diff --git a/httemplate/graph/elements/monthly.html b/httemplate/graph/elements/monthly.html new file mode 100644 index 000000000..7039bfe56 --- /dev/null +++ b/httemplate/graph/elements/monthly.html @@ -0,0 +1,351 @@ +<%doc> + +Example: + +  include('elements/monthly.html', +    #required +    'title'           => 'Page title', +    'items'           => \@items, +    'labels'          => \@labels,       # or \%labels (keys are items) + +    #required? +    'colors'          => \@colors,       # or \%colors, + +    #recommended +    'graph_labels'    => \@graph_labels, # or \%graph_labels, + +    #optional +    'params'          => \@params, # opt, +    'links'           => \@links,      # or \%link, #opt +    'link_fromparam'  => 'param_from', #defaults to 'begin' +    'link_toparam'    => 'param_to',   #defaults to 'end' + +    #optional, pulled from CGI params if not specified +    'start_month'     => $smonth, +    'start_year'      => $syear, +    'end_month'       => $emonth, +    'end_year'        => $eyear, + +    #optional +    'agentnum'        => $agentnum, +    'nototal'         => 1, +    'graph_type'      => 'LinesPoints', +    'remove_empty'    => 1, +    'bottom_total'    => 1, +    'sprintf'         => '%u', #sprintf format, overrides default %.2f +    'disable_money'   => 1, +  ); + +</%doc> +% if ( $cgi->param('_type') =~ /^(csv)$/ ) { +% +%   #http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes +%   http_header('Content-Type' => 'text/plain' ); +% +%   my $csv = new Text::CSV_XS { 'always_quote' => 1, +%                                'eol'          => "\n", #"\015\012", #"\012" +%                              }; +% +%   $csv->combine(map { my $m=$_; $m =~ s/^(\d+)\//$mon[$1-1] /; $m; } +%                     ('', @{$data->{label}}, $opt{'nototal'} ? () : 'Total') +%                ); +%    +<% $csv->string %> +% +%   my @bottom_total = (); +%   foreach ( @{ $data->{'items'} } ) { +% +%     my $col = 0; +%     my $total = 0; +%     $csv->combine( +%       shift( @{ $data->{'item_labels'} } ), +%       map { $total += $_; $bottom_total[$col++] += $_; sprintf($sprintf, $_); } +%         ( @{ shift( @{$data->{data}} ) } ), +%       ( $opt{'nototal'} ? () : sprintf($sprintf, $total) ), +%     ); +%     unless ( $opt{'nototal'} ) {  +%       $bottom_total[$col++] += $total;  +%     }  +% +<% $csv->string %> +% +%   } +%  +%   if ( $opt{'bottom_total'} ) { +%     $csv->combine( +%       'Total', +%       map { sprintf($sprintf, $_) } @bottom_total, +%     ); +% +<% $csv->string %> +% +%   }  +%    +% } elsif ( $cgi->param('_type') =~ /(\.xls)$/ ) { +% +%   #http_header('Content-Type' => 'application/excel' ); #eww +%   http_header('Content-Type' => 'application/vnd.ms-excel' ); +%   #http_header('Content-Type' => 'application/msexcel' ); #alas +% +%   my $output = ''; +%   my $XLS = new IO::Scalar \$output; +%   my $workbook = Spreadsheet::WriteExcel->new($XLS) +%     or die "Error opening .xls file: $!"; +% +%   my $worksheet = $workbook->add_worksheet(substr($opt{'title'},0,31)); +% +%   my($r,$c) = (0,0); +% +%   foreach ('', @{$data->{label}}, ($opt{'nototal'} ? () : 'Total') ) { +%     my $header = $_; +%     $header =~ s/^(\d+)\//$mon[$1-1] /; +%     $worksheet->write($r, $c++, $header) +%   } +% +%   my @bottom_total = (); +%   foreach ( @{ $data->{'items'} } ) { +%     $r++; +%     $c = 0; +%     my $total = 0; +%     $worksheet->write( $r, $c++, shift( @{ $data->{'item_labels'} } ) ); +%     foreach ( @{ shift( @{$data->{data}} ) } ) { +%       $total += $_; +%       $bottom_total[$c] += $_; +%       $worksheet->write($r, $c++,  sprintf($sprintf, $_) ); +%     } +%     unless ( $opt{'nototal'} ) {  +%       $bottom_total[$c] += $total;  +%       $worksheet->write($r, $c++,  sprintf($sprintf, $total) ); +%     }  +%   } +%  +%   $c = 0; +%   if ( $opt{'bottom_total'} ) { +%     $r++; +%     $worksheet->write($r, $c++, 'Total'); +%     $worksheet->write($r, $c++, sprintf($sprintf, $_)) foreach @bottom_total; +%   }  +%    +%   $workbook->close();# or die "Error creating .xls file: $!"; +% +%   http_header('Content-Length' => length($output) ); +%    +<% $output %> +% } elsif ( $cgi->param('_type') =~ /^(png)$/ ) { +% +%   #my $chart = Chart::LinesPoints->new(1024,480); +%   #my $chart = Chart::LinesPoints->new(768,480); +% +%   my $graph_type = 'LinesPoints'; +%   if ( $opt{'graph_type'} =~ /^(LinesPoints|Mountain)$/ ) { +%     $graph_type = $1; +%   } +%   my $class = "Chart::$graph_type"; +% +%   my $chart = $class->new(976,384); +%    +%   my $d = 0; +%   $chart->set( +%     #'min_val' => 0, +%     'legend' => 'bottom', +%     'colors' => { (  +%                     map { my $color = $_; +%                           'dataset'.$d++ => +%                             [ map hex($_), unpack 'a2a2a2', $color ] +%                         } +%                         #@{ $opt{'colors'} } +%                         @{ $data->{'colors'} } +%                   ), +%                   #'grey_background' => [ 211, 211, 211 ], +%                   'grey_background' => 'white', +%                   'background' => [ 0xe8, 0xe8, 0xe8 ], #grey +%                 }, +%     #'grey_background' => 'false', +%     'legend_labels' => $data->{'item_labels'}, +%     'brush_size' => 4, +%     #'pt_size' => 12, +%   ); +% +%   #my @data = map { $data->{$_} } ( 'label', @items ); +%   my @data = @{ $data->{data} }; +%   unshift @data, $data->{'label'}; +%    +%   http_header('Content-Type' => 'image/png' ); +% +%   $chart->_set_colors(); +%    +<% $chart->scalar_png(\@data) %> +% +% } else { +% +<% include('/elements/header.html', $opt{'title'} ) %> +% $cgi->param('_type', 'png');  + +<IMG SRC="<% $cgi->self_url %>" WIDTH="976" HEIGHT="384"> +<P ALIGN="right"> + +% unless ( $opt{'disable_download'} ) {  +%   $cgi->param('_type', "monthly.xls" );  +            Download full results<BR> +            as <A HREF="<% $cgi->self_url %>">Excel spreadsheet</A><BR> +%   $cgi->param('_type', 'csv');  +            as <A HREF="<% $cgi->self_url %>">CSV file</A></P> +%   $cgi->param('_type', "html" );  +% }  +% +</P> +<% include('/elements/table.html', 'e8e8e8') %> + +<TR> + +  <TD></TD> + +% foreach my $column ( @{$data->{label}} ) { +%       #$column =~ s/^(\d+)\//$mon[$1-1]<BR>/e; +%       $column =~ s/^(\d+)\//$mon[$1-1]<BR>/; +    <TH><% $column %></TH> +% }  + +% unless ( $opt{'nototal'} ) {  +    <TH>Total</TH> +% }  + +</TR> + +% my @bottom_total = (); +% foreach my $row ( @{ $data->{'items'} } ) { +% +%     #my $color = shift( @{ $opt{'colors'} } ); +%     my $color = shift( @{ $data->{'colors'} } ); +%     my $link = shift( @{ $data->{'links'} } ); +%     my ( $begin, $end ) = ( $fromparam, $toparam ); +%     if ( ref($link) ) { +%       my $ref = $link; +%       $link =  $ref->{link}; +%       $begin = $ref->{fromparam}; +%       $end =   $ref->{toparam}; +%     } +%     $link = $link ? qq(<A HREF="$link) : ''; +%     my $label = shift( @{ $data->{'item_labels'} } ); + +      <TR> + +        <TH> +          <FONT COLOR="#<% $color %>"><% $label %></FONT> +        </TH> + +%       #my $link = exists($opt{'links'}{$row}) +%             #  ? qq(<A HREF="$opt{'links'}{$row}) +%             #  : ''; +%       my @speriod = @{$data->{speriod}}; +%       my @eperiod = @{$data->{eperiod}}; +%       my $total = 0; +%     +%       my $col = 0; +%       foreach my $column ( @{ shift( @{$data->{data}} ) } ) { + +          <TD ALIGN="right" BGCOLOR="#ffffff"> +            <% $link ? $link. "$begin=". shift(@speriod). ";$end=". shift(@eperiod). '">' : '' %><FONT COLOR="#<% $color %>"><% $money_char %><% sprintf($sprintf,, $column) %></FONT><% $link ? '</A>' : '' %> +          </TD> +% +%         $total += $column; +%         $bottom_total[$col++] += $column; +%       +%       }  + +%       unless ( $opt{'nototal'} ) {  +            <TD ALIGN="right" BGCOLOR="#f5f6be"> +              <% $link ? $link. "$begin=". ${$data->{speriod}}[0]. ";$end=". ${$data->{eperiod}}[-1]. '">' : '' %><FONT COLOR="#<% $color %>"><% $money_char %><% sprintf($sprintf, $total) %></FONT><% $link ? '</A>' : '' %> +            </TD> +%           $bottom_total[$col++] += $total;  +%       }  + +      </TR> + +% }  + +% if ( $opt{'bottom_total'} ) { +%     my @speriod = ( @{$data->{speriod}}, ${$data->{speriod}}[0] ); +%     my @eperiod = ( @{$data->{eperiod}}, ${$data->{eperiod}}[-1] ); + +  <TR> +    <TH>Total</TH> + +% foreach my $total ( @bottom_total ) {  + +      <TD ALIGN="right" BGCOLOR="#f5f6be"> +        <% $opt{'bottom_link'} +              ? '<A HREF="'. $opt{'bottom_link'}. +                "$fromparam=". shift(@speriod). +                ";$toparam=". shift(@eperiod). '">' +              : '' +        %>$<% sprintf($sprintf, $total) %><% $opt{'bottom_link'} ? '</A>' : '' %> + +      </TD> + +% }  + +  </TR> + +% }  + +</TABLE> + +<% include('/elements/footer.html') %> +% }  +<%once> + +</%once> +<%init> + +my(%opt) = @_; + +my $sprintf = $opt{'sprintf'} || '%.2f'; +my $fromparam = $opt{'link_fromparam'} || 'begin'; +my $toparam =   $opt{'link_toparam'}   || 'end'; + +my $conf = new FS::Conf; +my $money_char = $opt{'disable_money'} ? '' : $conf->config('money_char'); + +my @items = @{ $opt{'items'} }; + +foreach my $other (qw( labels graph_labels colors links )) { +#foreach my $other (qw( labels graph_labels colors )) { +  if ( ref($opt{$other}) eq 'HASH' ) { +    $opt{$other} = [ map $opt{$other}{$_}, @items ]; +  } +} + +my @mon = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); + +#find first month +$opt{'start_month'} ||= $cgi->param('start_month'); # || $curmon+1; +$opt{'start_year'}  ||= $cgi->param('start_year'); # || 1899+$curyear; + +#find last month +$opt{'end_month'} ||= $cgi->param('end_month'); # || $curmon+1; +$opt{'end_year'}  ||= $cgi->param('end_year'); # || 1900+$curyear; + +my $report = new FS::Report::Table::Monthly ( + +  #'items'       => $opt{'items'}, +  'items'        => \@items, +  'params'       => $opt{'params'}, +  'item_labels'  => ( $cgi->param('_type') =~ /^(png)$/ +                        ? $opt{'graph_labels'} +                        : $opt{'labels'} +                    ), +  'colors'       => $opt{'colors'}, +  'links'        => $opt{'links'}, + +  'start_month'  => $opt{'start_month'}, +  'start_year'   => $opt{'start_year'}, +  'end_month'    => $opt{'end_month'}, +  'end_year'     => $opt{'end_year'}, + +  'agentnum'     => $opt{'agentnum'}, +  'remove_empty' => $opt{'remove_empty'}, +); +my $data = $report->data; + +</%init> diff --git a/httemplate/graph/money_time.cgi b/httemplate/graph/money_time.cgi new file mode 100644 index 000000000..4e4157ea1 --- /dev/null +++ b/httemplate/graph/money_time.cgi @@ -0,0 +1,98 @@ +<% include('elements/monthly.html', +                'title'        => $agentname. +                                  'Sales, Credits and Receipts Summary', +                'items'        => \@items, +                'labels'       => \%label, +                'graph_labels' => \%graph_label, +                'colors'       => \%color, +                'links'        => \%link, +                'agentnum'     => $agentnum, +                'nototal'      => scalar($cgi->param('12mo')), +             ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +#XXX or virtual +my( $agentnum, $agent ) = ('', ''); +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { +  $agentnum = $1; +  $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); +  die "agentnum $agentnum not found!" unless $agent; +} + +my $agentname = $agent ? $agent->agent.' ' : ''; + +my @items = qw( invoiced netsales +                credits  netcredits +                payments receipts +                refunds  netrefunds +                cashflow netcashflow +              ); +if ( $cgi->param('12mo') == 1 ) { +  @items = map $_.'_12mo', @items; +} + +my %label = ( +  'invoiced'    => 'Gross Sales', +  'netsales'    =>   'Net Sales', +  'credits'     => 'Gross Credits', +  'netcredits'  =>   'Net Credits', +  'payments'    => 'Gross Receipts', +  'receipts'    =>   'Net Receipts', +  'refunds'     => 'Gross Refunds', +  'netrefunds'  =>   'Net Refunds', +  'cashflow'    => 'Gross Cashflow', +  'netcashflow' =>   'Net Cashflow', +); + +my %graph_suffix = ( + 'invoiced'    => ' (invoiced)',  + 'netsales'    => ' (invoiced - applied credits)', + 'credits'     => ' (credited)', + 'netcredits'  => ' (applied credits)', + 'payments'    => ' (payments)', + 'receipts'    => ' (applied payments)', + 'refunds'     => ' (refunds)', + 'netrefunds'  => ' (applied refunds)', + 'cashflow'    => ' (payments - refunds)', + 'netcashflow' => ' (applied payments - applied refunds)', +); +my %graph_label = map { $_ => $label{$_}.$graph_suffix{$_} } keys %label; + +$label{$_.'_12mo'} = $label{$_}. " (prev 12 months)" +  foreach keys %label; + +$graph_label{$_.'_12mo'} = $graph_label{$_}. " (prev 12 months)" +  foreach keys %graph_label; + +my %color = ( +  'invoiced'    => '9999ff', #light blue +  'netsales'    => '0000cc', #blue +  'credits'     => 'ff9999', #light red +  'netcredits'  => 'cc0000', #red +  'payments'    => '99cc99', #light green +  'receipts'    => '00cc00', #green +  'refunds'     => 'ffcc99', #light orange +  'netrefunds'  => 'ff9900', #orange +  'cashflow'    => '99cc33', #light olive +  'netcashflow' => '339900', #olive +); +$color{$_.'_12mo'} = $color{$_} +  foreach keys %color; + +my %link = ( +  'invoiced'   => "${p}search/cust_bill.html?agentnum=$agentnum;", +  'netsales'   => "${p}search/cust_bill.html?agentnum=$agentnum;net=1;", +  'credits'    => "${p}search/cust_credit.html?agentnum=$agentnum;", +  'netcredits' => "${p}search/cust_credit_bill.html?agentnum=$agentnum;", +  'payments'   => "${p}search/cust_pay.cgi?magic=_date;agentnum=$agentnum;", +  'receipts'   => "${p}search/cust_bill_pay.html?agentnum=$agentnum;", +  'refunds'    => "${p}search/cust_refund.html?magic=_date;agentnum=$agentnum;", +  'netrefunds' => "${p}search/cust_credit_refund.html?agentnum=$agentnum;", +); +# XXX link 12mo? + +</%init> diff --git a/httemplate/graph/report_cust_bill_pkg.html b/httemplate/graph/report_cust_bill_pkg.html new file mode 100644 index 000000000..4a6433272 --- /dev/null +++ b/httemplate/graph/report_cust_bill_pkg.html @@ -0,0 +1,54 @@ +<% include('/elements/header.html', 'Sales Report' ) %> + +<FORM ACTION="cust_bill_pkg.cgi" METHOD="GET"> + +<TABLE> + +<% include('/elements/tr-select-from_to.html' ) %> + +<% include('/elements/tr-select-agent.html', +             'label'         => 'For agent: ', +             'disable_empty' => 0, +          ) +%> + +<% include('/elements/tr-select-pkg_class.html', +              'pre_options' => [ '0' => 'all' ], +              'empty_label' => '(empty class)', +           ) +%> + +<!-- +<TR> +  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="separate_0freq" VALUE="1"></TD> +  <TD>Separate one-time vs. recurring sales</TD> +</TR> +--> + +<TR> +  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="use_override" VALUE="1"></TD> +  <TD>Separate sub-packages from parents</TD> +</TR> + +<TR> +  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="use_usage" VALUE="1"></TD> +  <TD>Separate rated usage from recurring fees</TD> +</TR> + +<TR> +  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="average_per_cust_pkg" VALUE="1"></TD> +  <TD>Average per customer package</TD> +</TR> + +</TABLE> + +<BR><INPUT TYPE="submit" VALUE="Display"> +</FORM> + +<% include('/elements/footer.html') %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +</%init> diff --git a/httemplate/graph/report_cust_bill_pkg_detail.html b/httemplate/graph/report_cust_bill_pkg_detail.html new file mode 100644 index 000000000..3b85d521c --- /dev/null +++ b/httemplate/graph/report_cust_bill_pkg_detail.html @@ -0,0 +1,48 @@ +<% include('/elements/header.html', 'Usage Sales Report' ) %> + +<FORM ACTION="cust_bill_pkg_detail.cgi" METHOD="GET"> + +<TABLE> + +<% include('/elements/tr-select-from_to.html' ) %> + +<% include('/elements/tr-select-agent.html', +             'label'         => 'For agent: ', +             'disable_empty' => 0, +          ) +%> + +<% include('/elements/tr-select-pkg_class.html', +              'pre_options' => [ '0' => 'all' ], +              'empty_label' => '(empty class)', +           ) +%> + +<TR> +  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="use_override" VALUE="1"></TD> +  <TD>Separate sub-packages from parents</TD> +</TR> + +<% include('/elements/tr-select-table.html', +              'label'        => 'Usage class: ', +              'element_name' => 'usageclass', +              'table'        => 'usage_class', +              'name_col'     => 'classname', +              'hashref'      => { 'disabled' => '' }, +              'pre_options'  => [ '0' => 'all' ], +              'empty_label'  => '(empty class)', +           ) +%> + +</TABLE> + +<BR><INPUT TYPE="submit" VALUE="Display"> +</FORM> + +<% include('/elements/footer.html') %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +</%init> diff --git a/httemplate/graph/report_cust_pkg.html b/httemplate/graph/report_cust_pkg.html new file mode 100644 index 000000000..22ccd5def --- /dev/null +++ b/httemplate/graph/report_cust_pkg.html @@ -0,0 +1,28 @@ +<% include('/elements/header.html', 'Package Churn Summary' ) %> + +<FORM ACTION="cust_pkg.cgi" METHOD="GET"> + +<TABLE> + +<% include('/elements/tr-select-from_to.html' ) %> + +<% include('/elements/tr-select-agent.html', +             'curr_value'    => scalar($cgi->param('agentnum')), +             'label'         => 'For agent: ', +             'disable_empty' => 0, +          ) +%> + +</TABLE> + +<BR><INPUT TYPE="submit" VALUE="Display"> +</FORM> + +<% include('/elements/footer.html') %> +<%init> + +#XXX use a different ACL for package churn? +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +</%init> diff --git a/httemplate/graph/report_money_time.html b/httemplate/graph/report_money_time.html new file mode 100644 index 000000000..b85bb6552 --- /dev/null +++ b/httemplate/graph/report_money_time.html @@ -0,0 +1,43 @@ +<% include('/elements/header.html', 'Sales, Credits and Receipts Summary' ) %> + +<FORM ACTION="money_time.cgi" METHOD="GET"> + +<!-- +<INPUT TYPE="checkbox" NAME="ar"> +  Accounts receivable (invoices - applied credits)<BR> +<INPUT TYPE="checkbox" NAME="charged"> +  Just Invoices<BR> +<INPUT TYPE="checkbox" NAME="defer"> +  Accounts receivable, with deferred revenue (invoices - applied credits, with charges for annual/semi-annual/quarterly/etc. services deferred over applicable time period) (there has got to be a shorter description for this)<BR> +<INPUT TYPE="checkbox" NAME="cash"> +  Cashflow (payments - refunds)<BR> +<BR> +--> + +<TABLE> + +<% include('/elements/tr-select-from_to.html' ) %> + +<% include('/elements/tr-select-agent.html', +             'label'         => 'For agent: ', +             'disable_empty' => 0, +          ) +%> + +<TR> +  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="12mo" VALUE="1"></TD> +  <TD>Show 12 month totals instead of monthly values</TD> +</TR> + +</TABLE> + +<BR><INPUT TYPE="submit" VALUE="Display"> +</FORM> + +<% include('/elements/footer.html') %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +</%init> | 
