summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorivan <ivan>2007-08-14 21:46:18 +0000
committerivan <ivan>2007-08-14 21:46:18 +0000
commit6b74e2e7a98a11b8b4f62a10676ba8b37ee2ef4f (patch)
tree1bfb827ae9ab09efd0faec58a8b559402ae68baa
parent0cd9e13c265f6acc6b2cccf58af54c445100e4d0 (diff)
add package churn report/graph
-rw-r--r--FS/FS/Report/Table/Monthly.pm84
-rwxr-xr-xhttemplate/browse/agent.cgi5
-rw-r--r--httemplate/elements/menu.html4
-rw-r--r--httemplate/graph/cust_bill_pkg.cgi12
-rw-r--r--httemplate/graph/cust_pkg.cgi63
-rw-r--r--httemplate/graph/elements/monthly.html475
-rw-r--r--httemplate/graph/money_time.cgi12
-rw-r--r--httemplate/graph/report_cust_pkg.html27
8 files changed, 395 insertions, 287 deletions
diff --git a/FS/FS/Report/Table/Monthly.pm b/FS/FS/Report/Table/Monthly.pm
index 145f2a85c..a44cafe3e 100644
--- a/FS/FS/Report/Table/Monthly.pm
+++ b/FS/FS/Report/Table/Monthly.pm
@@ -147,7 +147,11 @@ sub netsales { #net sales
FROM cust_credit_bill
LEFT JOIN cust_bill USING ( invnum )
LEFT JOIN cust_main USING ( custnum )
- WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum, 'cust_bill')
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ 'cust_bill._date'
+ )
);
#horrible local kludge
@@ -158,7 +162,11 @@ sub netsales { #net sales
LEFT JOIN cust_main USING ( custnum )
LEFT JOIN cust_pkg USING ( pkgnum )
LEFT JOIN part_pkg USING ( pkgpart )
- WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum, 'cust_bill'). "
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ 'cust_bill._date'
+ ). "
AND LOWER(part_pkg.pkg) LIKE 'expense _%'
");
@@ -183,7 +191,11 @@ sub receipts { #cashflow
FROM cust_bill_pay
LEFT JOIN cust_bill USING ( invnum )
LEFT JOIN cust_main USING ( custnum )
- WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum, 'cust_bill_pay'). "
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ 'cust_bill_pay._date'
+ ). "
AND 0 < ( SELECT COUNT(*) from cust_bill_pkg, cust_pkg, part_pkg
WHERE cust_bill.invnum = cust_bill_pkg.invnum
AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum
@@ -285,54 +297,38 @@ sub cust_bill_pkg {
}
-# NEEDS TO BE AGENTNUM-capable
-sub canceled { #active
- my( $self, $speriod, $eperiod, $agentnum ) = @_;
- $self->scalar_sql("
- SELECT COUNT(*)
- FROM cust_pkg
- LEFT JOIN cust_main USING ( custnum )
- WHERE 0 = ( SELECT COUNT(*)
- FROM cust_pkg
- WHERE cust_pkg.custnum = cust_main.custnum
- AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
- )
- AND cust_pkg.cancel > $speriod AND cust_pkg.cancel < $eperiod
- ");
-}
-
-# NEEDS TO BE AGENTNUM-capable
-sub newaccount { #newaccount
- my( $self, $speriod, $eperiod, $agentnum ) = @_;
- $self->scalar_sql("
- SELECT COUNT(*) FROM cust_pkg
- WHERE cust_pkg.custnum = cust_main.custnum
- AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
- AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
- AND cust_pkg.setup > $speriod AND cust_pkg.setup < $eperiod
- ");
-}
+sub setup_pkg { shift->pkg_field( @_, 'setup' ); }
+sub susp_pkg { shift->pkg_field( @_, 'susp' ); }
+sub cancel_pkg { shift->pkg_field( @_, 'cancel'); }
-# NEEDS TO BE AGENTNUM-capable
-sub suspended { #suspended
- my( $self, $speriod, $eperiod, $agentnum ) = @_;
+sub pkg_field {
+ my( $self, $speriod, $eperiod, $agentnum, $field ) = @_;
$self->scalar_sql("
- SELECT COUNT(*) FROM cust_pkg
- WHERE cust_pkg.custnum = cust_main.custnum
- AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
- AND 0 = ( SELECT COUNT(*) FROM cust_pkg
- WHERE cust_pkg.custnum = cust_main.custnum
- AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )
- )
- AND cust_pkg.susp > $speriod AND cust_pkg.susp < $eperiod
- ");
+ SELECT COUNT(*) FROM cust_pkg
+ LEFT JOIN cust_main USING ( custnum )
+ WHERE ". $self->in_time_period_and_agent( $speriod,
+ $eperiod,
+ $agentnum,
+ "cust_pkg.$field",
+ )
+ );
+
}
+#this is going to be harder..
+#sub unsusp_pkg {
+# my( $self, $speriod, $eperiod, $agentnum ) = @_;
+# $self->scalar_sql("
+# SELECT COUNT(*) FROM h_cust_pkg
+# WHERE
+#
+#}
+
sub in_time_period_and_agent {
my( $self, $speriod, $eperiod, $agentnum ) = splice(@_, 0, 4);
- my $table = @_ ? shift().'.' : '';
+ my $col = @_ ? shift() : '_date';
- my $sql = "${table}_date >= $speriod AND ${table}_date < $eperiod";
+ my $sql = "$col >= $speriod AND $col < $eperiod";
#agent selection
$sql .= " AND agentnum = $agentnum"
diff --git a/httemplate/browse/agent.cgi b/httemplate/browse/agent.cgi
index 46a924433..fdbf0f272 100755
--- a/httemplate/browse/agent.cgi
+++ b/httemplate/browse/agent.cgi
@@ -282,9 +282,10 @@ cancelled
</TD>
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <A HREF="<% $p %>search/report_cust_pay.html?agentnum=<% $agent->agentnum %>">Payments</A>
+ <A HREF="<% $p %>graph/report_cust_pkg.html?agentnum=<% $agent->agentnum %>">Package&nbsp;Churn</A>
+ <BR><A HREF="<% $p %>search/report_cust_pay.html?agentnum=<% $agent->agentnum %>">Payments</A>
<BR><A HREF="<% $p %>search/report_cust_credit.html?agentnum=<% $agent->agentnum %>">Credits</A>
- <BR><A HREF="<% $p %>search/report_receivables.cgi?agentnum=<% $agent->agentnum %>">A/R Aging</A>
+ <BR><A HREF="<% $p %>search/report_receivables.cgi?agentnum=<% $agent->agentnum %>">A/R&nbsp;Aging</A>
<!--<BR><A HREF="<% $p %>search/money_time.cgi?agentnum=<% $agent->agentnum %>">Sales/Credits/Receipts</A>-->
</TD>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 42e2a8389..616900c2d 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -141,6 +141,10 @@ if ( $curuser->access_right('Configuration') ) {
$report_packages{'Package definitions'} = [ $fsurl.'browse/part_pkg.cgi?active=1', 'Package definitions by number of active packages' ];
$report_packages{'separator'} = '';
}
+if ( $curuser->access_right('Financial reports') ) {
+ $report_packages{'Package churn'} = [ $fsurl.'graph/report_cust_pkg.html', 'Orders, suspensions and cancellations summary graph' ];
+ $report_packages{'separator2'} = '';
+}
$report_packages{'All customer packages'} = [ $fsurl.'search/cust_pkg.cgi?pkgnum', 'List all customer packages', ];
$report_packages{'Suspended customer packages'} = [ $fsurl.'search/cust_pkg.cgi?magic=suspended', 'List suspended packages' ];
$report_packages{'Customer packages with unconfigured services'} = [ $fsurl.'search/cust_pkg.cgi?APKG_pkgnum', 'List packages which have provisionable services' ];
diff --git a/httemplate/graph/cust_bill_pkg.cgi b/httemplate/graph/cust_bill_pkg.cgi
index 4070069ac..d7cae8055 100644
--- a/httemplate/graph/cust_bill_pkg.cgi
+++ b/httemplate/graph/cust_bill_pkg.cgi
@@ -10,10 +10,6 @@
'remove_empty' => 1,
'bottom_total' => 1,
'bottom_link' => "$link;",
- 'start_month' => $smonth,
- 'start_year' => $syear,
- 'end_month' => $emonth,
- 'end_year' => $eyear,
'agentnum' => $agentnum,
)
%>
@@ -22,14 +18,6 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-#find first month
-my $syear = $cgi->param('start_year'); # || 1899+$curyear;
-my $smonth = $cgi->param('start_month'); # || $curmon+1;
-
-#find last month
-my $eyear = $cgi->param('end_year'); # || 1900+$curyear;
-my $emonth = $cgi->param('end_month'); # || $curmon+1;
-
#XXX or virtual
my( $agentnum, $sel_agent ) = ('', '');
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
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
index 035bd0314..8e20b1c86 100644
--- a/httemplate/graph/elements/monthly.html
+++ b/httemplate/graph/elements/monthly.html
@@ -1,200 +1,182 @@
+<%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' );
%
-% # options example...
-% #
-% # 'title' => 'Page title',
-% # 'items' => \@items,
-% # 'params' => \@params, # opt,
-% # 'labels' => \@labels, # or \%labels (keys are items)
-% # 'graph_labels' => \@graph_labels, # or \%graph_labels,
-% # 'colors' => \@colors, # or \%colors,
-% # 'links => \@links, # or \%link, #opt
-% # 'start_month' => $smonth,
-% # 'start_year' => $syear,
-% # 'end_month' => $emonth,
-% # 'end_year' => $eyear,
-% # 'agentnum' => $agentnum, #opt
-% # 'nototal' => 1, #opt,
-% # 'graph_type' => 'LinesPoints', #opt
-% # 'remove_empty' => 1, #opt,
-% # 'bottom_total' => 1, #opt,
-%
-% my(%opt) = @_;
-% 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);
-%
-% 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;
-%
-% 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"
-% };
+% 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->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("%.2f", $_); }
-% ( @{ shift( @{$data->{data}} ) } ),
-% ( $opt{'nototal'} ? () : sprintf("%.2f", $total) ),
-% );
-% unless ( $opt{'nototal'} ) {
-% $bottom_total[$col++] += $total;
-% }
+% 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("%.2f", $_) } @bottom_total,
-% );
+% 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("%.2f", $_) );
-% }
-% unless ( $opt{'nototal'} ) {
-% $bottom_total[$c] += $total;
-% $worksheet->write($r, $c++, sprintf("%.2f", $total) );
-% }
-% }
+% }
+%
+% } 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("%.2f", $_)) foreach @bottom_total;
-% }
-%
-% $workbook->close();# or die "Error creating .xls file: $!";
-%
-% http_header('Content-Length' => length($output) );
-%
+% $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();
-%
-%
+% } 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 {
+% } else {
%
<% include('/elements/header.html', $opt{'title'} ) %>
% $cgi->param('_type', 'png');
@@ -217,94 +199,153 @@
<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'} ) {
+% unless ( $opt{'nototal'} ) {
<TH>Total</TH>
% }
-
</TR>
+
% my @bottom_total = ();
-% foreach my $row ( @{ $data->{'items'} } ) {
+% 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>
- <TR>
+ <TH>
+ <FONT COLOR="#<% $color %>"><% $label %></FONT>
+ </TH>
- <TH><FONT COLOR="#<% $color %>"><% shift( @{ $data->{'item_labels'} } ) %></FONT></TH>
-% #my $link = exists($opt{'links'}{$row})
-% # ? qq(<A HREF="$opt{'links'}{$row})
-% # : '';
+% #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}} ) } ) { # ( @{$data->{$row}} ) {
-%
-
+% 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 %>">$<% sprintf("%.2f", $column) %></FONT><% $link ? '</A>' : '' %>
- </TD>
+ <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'} ) {
+% }
+% 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;
+% }
- <TD ALIGN="right" BGCOLOR="#f5f6be">
- <% $link ? $link. 'begin='. ${$data->{speriod}}[0]. ';end='. ${$data->{eperiod}}[-1]. '">' : '' %><FONT COLOR="#<% $color %>">$<% sprintf("%.2f", $total) %></FONT><% $link ? '</A>' : '' %>
- </TD>
-% $bottom_total[$col++] += $total;
-% }
-
+ </TR>
- </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 ) {
+% foreach my $total ( @bottom_total ) {
<TD ALIGN="right" BGCOLOR="#f5f6be">
<% $opt{'bottom_link'}
? '<A HREF="'. $opt{'bottom_link'}.
- 'begin='. shift(@speriod).
- ';end='. shift(@eperiod). '">'
+ "$fromparam=". shift(@speriod).
+ ";$toparam=". shift(@eperiod). '">'
: ''
- %>$<% sprintf("%.2f", $total) %><% $opt{'bottom_link'} ? '</A>' : '' %>
+ %>$<% 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
index 2b98af838..41b82fa84 100644
--- a/httemplate/graph/money_time.cgi
+++ b/httemplate/graph/money_time.cgi
@@ -6,10 +6,6 @@
'graph_labels' => \%graph_label,
'colors' => \%color,
'links' => \%link,
- 'start_month' => $smonth,
- 'start_year' => $syear,
- 'end_month' => $emonth,
- 'end_year' => $eyear,
'agentnum' => $agentnum,
'nototal' => scalar($cgi->param('12mo')),
)
@@ -19,14 +15,6 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-#find first month
-my $syear = $cgi->param('start_year'); # || 1899+$curyear;
-my $smonth = $cgi->param('start_month'); # || $curmon+1;
-
-#find last month
-my $eyear = $cgi->param('end_year'); # || 1900+$curyear;
-my $emonth = $cgi->param('end_month'); # || $curmon+1;
-
#XXX or virtual
my( $agentnum, $agent ) = ('', '');
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
diff --git a/httemplate/graph/report_cust_pkg.html b/httemplate/graph/report_cust_pkg.html
new file mode 100644
index 000000000..0ff01815f
--- /dev/null
+++ b/httemplate/graph/report_cust_pkg.html
@@ -0,0 +1,27 @@
+<% 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: ',
+ )
+%>
+
+</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>