From 83e442de6a041fa3b7580d53912eeb1490ddf47c Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Fri, 10 Oct 2014 16:36:07 -0700 Subject: upstream-markup call rating and global default rates, #30633 --- httemplate/edit/elements/rate_detail.html | 102 +++++++++++++++++++++++------- httemplate/edit/process/rate_detail.html | 32 ++++++++-- httemplate/edit/rate_detail.html | 22 ++++--- 3 files changed, 120 insertions(+), 36 deletions(-) (limited to 'httemplate') diff --git a/httemplate/edit/elements/rate_detail.html b/httemplate/edit/elements/rate_detail.html index 14b52110b..7b5ec314a 100644 --- a/httemplate/edit/elements/rate_detail.html +++ b/httemplate/edit/elements/rate_detail.html @@ -47,34 +47,85 @@ with row headers showing the region name and prefixes. % } % foreach my $rate_time (@rate_time, '') { -% my $detail = $details[$row][$col]; -% if($detail) { + <& .detail_box, + detail => $details[$row][$col], + ratetimenum => ($rate_time ? $rate_time->ratetimenum : ''), + cdrtypenum => $cdrtypenum, + regionnum => $region->regionnum, + ratenum => $rate->ratenum + &> +% $col++; + +% } # foreach @rate_time + +% $row++; +% }# foreach @rate_region +% if ( !$opt{regionnum} ) { +%# global default + + + Global default (for calls not matching any prefix) + + +% # default rate: set a null region + + <& .detail_box, + detail => $rate->default_detail, + ratetimenum => '', + cdrtypenum => '', + regionnum => '', + ratenum => $rate->ratenum + &> + + +% } + +<%def .detail_box> +<%args> +$detail => undef, +$ratetimenum +$cdrtypenum +$regionnum +$ratenum + +% if ($detail) { - <% granularity_detail($detail) %> <% min_included_detail($detail) %> <% conn_charge_detail($detail) %> -
<% edit_link($detail) %><% $money_char.$detail->min_charge %> +
<% edit_link($detail) %> +% if ( $detail->min_charge > 0 or $detail->conn_charge > 0) { + <% $money_char.$detail->min_charge %> <% $detail->sec_granularity ? ' / minute':' / call' %> % if ( $detail->min_cost ) { (<% $money_char.$detail->min_cost %> cost) % } +% if ( $detail->upstream_mult_charge > 0 +% or $detail->upstream_mult_cost > 0) { +
+ +% } +% } +% if ( $detail->upstream_mult_charge > 0 +% or $detail->upstream_mult_cost > 0) { + <% $detail->upstream_mult_charge %> × upstream price +% if ( $detail->upstream_mult_cost > 0 ) { + (<% $detail->upstream_mult_cost %> cost) +% } +% } +% if ( $detail->upstream_mult_charge == 0 +% and $detail->min_charge == 0 +% and $detail->conn_charge == 0 ) { + Free +% } <% $edit_hint %>
<% ( $rate_time || $cdrtypenum ) ? delete_link($detail) : '' %> +
<% ( $ratetimenum || $cdrtypenum ) ? delete_link($detail) : '' %>
-% } -% else { #!$detail - <% add_link($rate, $region, $rate_time, $cdrtypenum) %> -% } -% $col++; - -% } # foreach @rate_time - -% $row++; -% }# foreach @rate_region - - +% } else { + <% add_link($ratenum, $regionnum, $ratetimenum, $cdrtypenum) %> +% } + <%once> tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); @@ -95,25 +146,27 @@ sub edit_link { include( '/elements/popup_link_onclick.html', 'action' => "${p}edit/rate_detail.html?$ratedetailnum", 'actionlabel' => 'Edit rate', - 'height' => 460, + 'height' => 550, + 'width' => 580, #default# 'width' => 540, #default# 'color' => '#333399', ) . '">' } sub add_link { - my ($rate, $region, $rate_time, $cdrtypenum) = @_; + my ($ratenum, $regionnum, $ratetimenum, $cdrtypenum) = @_; ' 'Add rate', - 'height' => 460, + 'width' => 580, + 'height' => 550, ).'">'.small('(add)').'' } @@ -133,7 +186,10 @@ sub delete_link { sub granularity_detail { my $rate_detail = shift; - if($rate_detail->sec_granularity != 60 && $rate_detail->sec_granularity > 0) { + if( + $rate_detail->sec_granularity != 60 + && $rate_detail->sec_granularity > 0 + && $rate_detail->min_charge > 0) { ''. small('in '.$granularity{$rate_detail->sec_granularity}.' increments'). ''; diff --git a/httemplate/edit/process/rate_detail.html b/httemplate/edit/process/rate_detail.html index 6200d615f..0709d5079 100644 --- a/httemplate/edit/process/rate_detail.html +++ b/httemplate/edit/process/rate_detail.html @@ -1,13 +1,35 @@ -<% include( 'elements/process.html', - 'table' => 'rate_detail', - 'popup_reload' => 'Rate changed', #a popup "parent reload" for now +<& elements/process.html, + 'table' => 'rate_detail', + 'popup_reload' => 'Rate changed', #a popup "parent reload" for now #someday change the individual element and go away instead - ) -%> + 'noerror_callback' => $set_default_detail +&> <%init> my $conf = new FS::Conf; die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); +my $set_default_detail = sub { + my ($cgi, $rate_detail) = @_; +warn Dumper $rate_detail; + if (!$rate_detail->dest_regionnum) { + # then this is a global default rate + my $rate = $rate_detail->rate; + if ($rate->default_detailnum) { + if ($rate->default_detailnum == $rate_detail->ratedetailnum) { + return; + } else { + # there's somehow an existing default rate. remove it. + my $old_default = $rate->default_detail; + my $error = $old_default->delete; + die "$error (removing old default rate)\n" if $error; + } + } + $rate->set('default_detailnum' => $rate_detail->ratedetailnum); + my $error = $rate->replace; + die "$error (setting default rate)\n" if $error; + } +}; + diff --git a/httemplate/edit/rate_detail.html b/httemplate/edit/rate_detail.html index 0de6ecc1e..3e800726e 100644 --- a/httemplate/edit/rate_detail.html +++ b/httemplate/edit/rate_detail.html @@ -15,6 +15,8 @@ 'conn_cost' => 'Wholesale connection cost', 'min_cost' => 'Wholesale cost per minute/call', 'classnum' => 'Usage class', + 'upstream_mult_charge'=> 'Upstream multiplier (retail)', + 'upstream_mult_cost' => 'Upstream multiplier (cost)', }, 'fields' => [ { field=>'ratenum', type=>'hidden', }, @@ -46,13 +48,15 @@ labels => \%granularity, disable_empty => 1, }, - { field =>'classnum', - type =>'select-table', - table =>'usage_class', - name_col =>'classname', - empty_label =>'(default)', - hashref =>{ disabled => '' }, + { field => 'classnum', + type => 'select-table', + table => 'usage_class', + name_col => 'classname', + empty_label => '(default)', + hashref => { disabled => '' }, }, + { field => 'upstream_mult_charge', type => 'text', }, + { field => 'upstream_mult_cost', type => 'text', }, ], 'new_hashref_callback' => sub { @@ -62,6 +66,8 @@ cdrtypenum => scalar($cgi->param('cdrtypenum')), min_included => 0, conn_charge => 0, + upstream_mult_charge => 0, + upstream_mult_cost => 0, } }, ) @@ -85,8 +91,8 @@ if ( $keywords =~ /^(\d+)$/ || $cgi->param('ratedetailnum') =~ /^(\d+)$/ ) { my $rate_detail = qsearchs('rate_detail', { 'ratedetailnum' => $1 } ) or die "unknown ratedetailnum $1"; - $name = - $rate_detail->rate->ratename. ' rate for '. $rate_detail->dest_regionname; + $name = $rate_detail->rate->ratename. ' rate for '. + ($rate_detail->dest_regionname || 'global default'); } #sec_granularity should default to 60! for new rates when this gets used for em -- cgit v1.2.1 From 98ea15536afc6896cce08a41b877d6cb52444d14 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Thu, 16 Oct 2014 16:23:11 -0700 Subject: make package churn report actually show package churn, #7990 --- httemplate/graph/cust_pkg.cgi | 161 ---------------------------- httemplate/graph/cust_pkg.html | 159 ++++++++++++++++++++++++++++ httemplate/graph/elements/monthly.html | 16 ++- httemplate/graph/elements/report.html | 6 +- httemplate/graph/report_cust_pkg.html | 8 +- httemplate/search/cust_pkg_churn.html | 186 +++++++++++++++++++++++++++++++++ 6 files changed, 361 insertions(+), 175 deletions(-) delete mode 100644 httemplate/graph/cust_pkg.cgi create mode 100644 httemplate/graph/cust_pkg.html create mode 100644 httemplate/search/cust_pkg_churn.html (limited to 'httemplate') diff --git a/httemplate/graph/cust_pkg.cgi b/httemplate/graph/cust_pkg.cgi deleted file mode 100644 index cdd95e10a..000000000 --- a/httemplate/graph/cust_pkg.cgi +++ /dev/null @@ -1,161 +0,0 @@ -<& elements/monthly.html, - 'title' => $agentname. 'Package Churn', - 'items' => \@items, - 'labels' => \@labels, - 'graph_labels' => \@labels, - 'colors' => \@colors, - 'links' => \@links, - 'params' => \@params, - 'agentnum' => $agentnum, - 'sprintf' => '%u', - 'disable_money' => 1, - 'remove_empty' => (scalar(@group_keys) > 1 ? 1 : 0), -&> -<%init> - -#XXX use a different ACL for package churn? -my $curuser = $FS::CurrentUser::CurrentUser; -die "access denied" - unless $curuser->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 @base_items = qw( setup_pkg susp_pkg cancel_pkg ); - -my %base_labels = ( - 'setup_pkg' => 'New orders', - 'susp_pkg' => 'Suspensions', -# 'unsusp' => 'Unsuspensions', - 'cancel_pkg' => 'Cancellations', -); - -my %base_colors = ( - 'setup_pkg' => '00cc00', #green - 'susp_pkg' => 'ff9900', #yellow - #'unsusp' => '', #light green? - 'cancel_pkg' => 'cc0000', #red ? 'ff0000' -); - -my %base_links = ( - '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', - }, -); - -my %filter_params = ( - # not agentnum, that's elsewhere - 'refnum' => [ $cgi->param('refnum') ], - 'classnum' => [ $cgi->param('classnum') ], - 'towernum' => [ $cgi->param('towernum') ], -); -if ( $cgi->param('zip') =~ /^(\w+)/ ) { - $filter_params{zip} = $1; -} -foreach my $link (values %base_links) { - foreach my $key (keys(%filter_params)) { - my $value = $filter_params{$key}; - if (ref($value)) { - $value = join(',', @$value); - } - $link->{'link'} .= "$key=$value;" if length($value); - } -} - - -# In order to keep this from being the same trainwreck as cust_bill_pkg.cgi, -# we allow ONE breakdown axis, besides the setup/susp/cancel inherent in -# the report. - -my $breakdown = $cgi->param('breakdown_by'); -my ($name_col, $table); -if ($breakdown eq 'classnum') { - $table = 'pkg_class'; - $name_col = 'classname'; -} elsif ($breakdown eq 'refnum') { - $table = 'part_referral'; - $name_col = 'referral'; -} elsif ($breakdown eq 'towernum') { - $table = 'tower'; - $name_col = 'towername'; -} elsif ($breakdown) { - die "unknown breakdown column '$breakdown'\n"; -} - -my @group_keys; -my @group_labels; -if ( $table ) { - my @groups; - if ( $cgi->param($breakdown) ) { - foreach my $key ($cgi->param($breakdown)) { - next if $key =~ /\D/; - push @groups, qsearch( $table, { $breakdown => $key }); - } - } else { - @groups = qsearch( $table ); - } - foreach (@groups) { - push @group_keys, $_->get($breakdown); - push @group_labels, $_->get($name_col); - } -} - -my (@items, @labels, @colors, @links, @params); -if (scalar(@group_keys) > 1) { - my $hue = 180; - foreach my $key (@group_keys) { - # this gives a decent level of contrast as long as there aren't too many - # result sets - my $scheme = Color::Scheme->new - ->scheme('triade') - ->from_hue($hue) - ->distance(0.5); - my $label = shift @group_labels; - my $i = 0; # item index - foreach (@base_items) { - # append the item - push @items, $_; - # and its parameters - push @params, [ - %filter_params, - $breakdown => $key - ]; - # and a label prefixed with the group label - push @labels, "$label - $base_labels{$_}"; - # and colors (?!) - push @colors, $scheme->colorset->[$i]->[1]; - # and links... - my %this_link = %{ $base_links{$_} }; - $this_link{link} .= "$breakdown=$key;"; - push @links, \%this_link; - $i++; - } #foreach (@base_items - $hue += 35; - } # foreach @group_keys -} else { - @items = @base_items; - @labels = @base_labels{@base_items}; - @colors = @base_colors{@base_items}; - @links = @base_links{@base_items}; - @params = map { [ %filter_params ] } @base_items; -} - - diff --git a/httemplate/graph/cust_pkg.html b/httemplate/graph/cust_pkg.html new file mode 100644 index 000000000..3b6552ba8 --- /dev/null +++ b/httemplate/graph/cust_pkg.html @@ -0,0 +1,159 @@ +<& elements/monthly.html, + 'title' => $agentname. 'Package Churn', + 'items' => \@items, + 'labels' => \@labels, + 'graph_labels' => \@labels, + 'colors' => \@colors, + 'links' => \@links, + 'params' => \@params, + 'agentnum' => $agentnum, + 'sprintf' => ( $normalize ? '%0.1f%%' : '%u'), + 'normalize' => ( $normalize ? 0 : undef ), + 'disable_money' => 1, + 'remove_empty' => (scalar(@group_keys) > 1 ? 1 : 0), + 'nototal' => 1, + 'no_graph' => [ 1, 0, 0, 0, 0 ], # don't graph 'active' +&> +<%init> + +#XXX use a different ACL for package churn? +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->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 @base_items = qw( active_pkg setup_pkg susp_pkg unsusp_pkg cancel_pkg ); + +my %base_labels = ( + 'active_pkg' => 'Active packages', + 'setup_pkg' => 'New orders', + 'susp_pkg' => 'Suspensions', + 'unsusp_pkg' => 'Unsuspensions', + 'cancel_pkg' => 'Cancellations', +); + +my %base_colors = ( + 'active_pkg' => '000000', #black + 'setup_pkg' => '00cc00', #green + 'susp_pkg' => 'ff9900', #yellow + 'unsusp_pkg' => '44ff44', #light green + 'cancel_pkg' => 'cc0000', #red +); + +my %base_links; +foreach my $status (qw(active setup cancel susp unsusp)) { + $base_links{$status.'_pkg'} = + "${p}search/cust_pkg_churn.html?agentnum=$agentnum;status=$status;"; +} + +my %filter_params = ( + # not agentnum, that's elsewhere + 'refnum' => [ $cgi->param('refnum') ], + 'classnum' => [ $cgi->param('classnum') ], + 'towernum' => [ $cgi->param('towernum') ], +); +if ( $cgi->param('zip') =~ /^(\w+)/ ) { + $filter_params{zip} = $1; +} +foreach my $link (values %base_links) { + foreach my $key (keys(%filter_params)) { + my $value = $filter_params{$key}; + if (ref($value)) { + $value = join(',', @$value); + } + $link .= "$key=$value;" if length($value); + } +} + + +# In order to keep this from being the same trainwreck as cust_bill_pkg.cgi, +# we allow ONE breakdown axis, besides the setup/susp/cancel inherent in +# the report. + +my $breakdown = $cgi->param('breakdown_by'); +my ($name_col, $table); +if ($breakdown eq 'classnum') { + $table = 'pkg_class'; + $name_col = 'classname'; +} elsif ($breakdown eq 'refnum') { + $table = 'part_referral'; + $name_col = 'referral'; +} elsif ($breakdown eq 'towernum') { + $table = 'tower'; + $name_col = 'towername'; +} elsif ($breakdown) { + die "unknown breakdown column '$breakdown'\n"; +} + +my @group_keys; +my @group_labels; +if ( $table ) { + my @groups; + if ( $cgi->param($breakdown) ) { + foreach my $key ($cgi->param($breakdown)) { + next if $key =~ /\D/; + push @groups, qsearch( $table, { $breakdown => $key }); + } + } else { + @groups = qsearch( $table ); + } + foreach (@groups) { + push @group_keys, $_->get($breakdown); + push @group_labels, $_->get($name_col); + } +} + +my (@items, @labels, @colors, @links, @params); +if (scalar(@group_keys) > 1) { + my $hue = 180; + foreach my $key (@group_keys) { + # this gives a decent level of contrast as long as there aren't too many + # result sets + my $scheme = Color::Scheme->new + ->scheme('triade') + ->from_hue($hue) + ->distance(0.5); + my $label = shift @group_labels; + my $i = 0; # item index + foreach (@base_items) { + # append the item + push @items, $_; + # and its parameters + push @params, [ + %filter_params, + $breakdown => $key + ]; + # and a label prefixed with the group label + push @labels, "$label - $base_labels{$_}"; + # and colors (?!) + push @colors, $scheme->colorset->[$i]->[1]; + # and links... + my $this_link = $base_links{$_}; + $this_link .= "$breakdown=$key;"; + push @links, $this_link; + $i++; + } #foreach (@base_items + $hue += 35; + } # foreach @group_keys +} else { + @items = @base_items; + @labels = @base_labels{@base_items}; + @colors = @base_colors{@base_items}; + @links = @base_links{@base_items}; + @params = map { [ %filter_params ] } @base_items; +} + +my $normalize = $cgi->param('normalize'); + + diff --git a/httemplate/graph/elements/monthly.html b/httemplate/graph/elements/monthly.html index 939f18a35..4b988f166 100644 --- a/httemplate/graph/elements/monthly.html +++ b/httemplate/graph/elements/monthly.html @@ -125,6 +125,7 @@ my %reportopts = ( 'cust_classnum'=> $opt{'cust_classnum'}, 'remove_empty' => $opt{'remove_empty'}, 'doublemonths' => $opt{'doublemonths'}, + 'normalize' => $opt{'normalize'}, ); warn Dumper({ 'REPORTOPTS' => \%reportopts }) if $opt{'debug'}; @@ -147,17 +148,12 @@ $col_labels = $data->{label} if $opt{'daily'}; my @colors; my @graph_labels; my @no_graph; -if ( $opt{'remove_empty'} ) { +#if ( $opt{'remove_empty'} ) { # no, always do this # then filter out per-item things for collapsed rows - foreach my $i (@{ $data->{'indices'} }) { - push @colors, $opt{'colors'}[$i]; - push @graph_labels, $opt{'graph_labels'}[$i]; - push @no_graph, $opt{'no_graph'}[$i]; - } -} else { - @colors = @{ $opt{'colors'} }; - @graph_labels = @{ $opt{'graph_labels'} }; - @no_graph = @{ $opt{'no_graph'} || [] }; +foreach my $i (@{ $data->{'indices'} }) { + push @colors, $opt{'colors'}[$i]; + push @graph_labels, $opt{'graph_labels'}[$i]; + push @no_graph, $opt{'no_graph'}[$i]; } my @links; diff --git a/httemplate/graph/elements/report.html b/httemplate/graph/elements/report.html index b3ba9ee22..cffc82816 100644 --- a/httemplate/graph/elements/report.html +++ b/httemplate/graph/elements/report.html @@ -108,11 +108,11 @@ any delimiter and linked from the elements in @data. % foreach ( @{ shift( @data ) } ) { % $total += $_; % $bottom_total[$col-1] += $_ unless $opt{no_graph}[$row]; -% $worksheet->write($row, $col++, sprintf($sprintf, $_) ); +% $worksheet->write_number($row, $col++, sprintf($sprintf, $_) ); % } % if ( !$opt{'nototal'} ) { % $bottom_total[$col-1] += $total unless $opt{no_graph}[$row]; -% $worksheet->write($row, $col++, sprintf($sprintf, $total) ); +% $worksheet->write_number($row, $col++, sprintf($sprintf, $total) ); % } % } % @@ -120,7 +120,7 @@ any delimiter and linked from the elements in @data. % if ( $opt{'bottom_total'} ) { % $row++; % $worksheet->write($row, $col++, 'Total'); -% $worksheet->write($row, $col++, sprintf($sprintf, $_)) foreach @bottom_total; +% $worksheet->write_number($row, $col++, sprintf($sprintf, $_)) foreach @bottom_total; % } % % $workbook->close();# or die "Error creating .xls file: $!"; diff --git a/httemplate/graph/report_cust_pkg.html b/httemplate/graph/report_cust_pkg.html index 1425ff089..0da5016a7 100644 --- a/httemplate/graph/report_cust_pkg.html +++ b/httemplate/graph/report_cust_pkg.html @@ -1,6 +1,6 @@ <% include('/elements/header.html', 'Package Churn Summary' ) %> -
+ @@ -54,6 +54,12 @@ }, &> +<& /elements/tr-checkbox.html, + 'field' => 'normalize', + 'value' => 1, + 'label' => 'Show percentages' +&> +

diff --git a/httemplate/search/cust_pkg_churn.html b/httemplate/search/cust_pkg_churn.html new file mode 100644 index 000000000..0ab99aa97 --- /dev/null +++ b/httemplate/search/cust_pkg_churn.html @@ -0,0 +1,186 @@ +<& elements/search.html, + 'title' => $title, + 'name' => 'packages', + 'query' => $sql_query, + 'count_query' => $count_query, + 'header' => [ emt('#'), + emt('Quantity'), + emt('Package'), + emt('Class'), + emt('Sales Person'), + emt('Ordered by'), + emt('Setup Fee'), + emt('Base Recur'), + emt('Freq.'), + emt('Setup'), + emt('Last bill'), + emt('Next bill'), + emt('Susp.'), + emt('Changed'), + emt('Cancel'), + #emt('Reason'), # hard to do this right + FS::UI::Web::cust_header( + $cgi->param('cust_fields') + ), + #emt('Services'), # even harder + ], + 'fields' => [ + 'pkgnum', + 'quantity', + 'pkg', + 'classname', + 'salesperson', + 'otaker', + sub { sprintf( $money_char.'%.2f', + shift->part_pkg->option('setup_fee'), + ); + }, + sub { my $c = shift; + sprintf( $money_char.'%.2f', + $c->part_pkg->base_recur($c) + ); + }, + sub { FS::part_pkg::freq_pretty(shift); }, + + ( map { time_or_blank($_) } + qw( setup last_bill bill susp change_date cancel ) ), + + \&FS::UI::Web::cust_fields, + ], + 'sort_fields' => [ + 'cust_pkg.pkgnum', + ('') x 5, # can use as-is + ('') x 3, # can't use at all + # use the plain SQL column names + qw( setup last_bill bill susp change_date cancel ), + # cust_fields can take care of themselves + ], + 'color' => [ + ('') x 15, + FS::UI::Web::cust_colors(), + ], + 'style' => [ ('') x 15, + FS::UI::Web::cust_styles() ], + 'size' => [ '', '', '', '', '-1' ], + 'align' => 'rrlcccrrlrrrrrr'. FS::UI::Web::cust_aligns(). 'r', + 'links' => [ + $link, + $link, + $link, + ('') x 12, + ( map { $_ ne 'Cust. Status' ? $clink : '' } + FS::UI::Web::cust_header( + $cgi->param('cust_fields') + ) + ), + ], +&> +<%once> +my %title = ( + 'active' => 'Active packages as of ', + 'setup' => 'Packages started between ', + 'cancel' => 'Packages canceled between ', + 'susp' => 'Packages suspended between ', + 'unsusp' => 'Packages unsuspended between ', +); + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('List packages'); + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; + +my %search_hash = (); + +# pass a very limited set of parameters through +#scalars +for (qw( agentnum zip )) +{ + $search_hash{$_} = $cgi->param($_) if length($cgi->param($_)); +} + +#arrays / comma-separated lists +for my $param (qw( pkgpart classnum refnum towernum )) { + my @values = map { split(',') } $cgi->param($param); + $search_hash{$param} = \@values if scalar(@values); +} + +### +# do not pass dates to FS::cust_pkg->search; use the special churn_fromwhere +# logic. +### + +my $pkg_query = FS::cust_pkg->search(\%search_hash); +#warn Dumper $pkg_query; + +my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi); +my $status = $cgi->param('status'); + +my $title = emt($title{$status}) . + time2str('%b %o %Y', $beginning); +if ($status ne 'active') { + $title .= emt(' to ') . time2str('%b %o %Y', $ending); +} + +my ($from, @where) = FS::h_cust_pkg->churn_fromwhere_sql($status, $beginning, $ending); + +push @where, "freq != '0'"; + +# split off the primary table name +$from =~ s/^(\w+)(.*)$/$2/s; +my $table = $1; + +# merge with $pkg_query +$from .= ' ' . $pkg_query->{addl_from}; + +my $extra_sql; +if ($pkg_query->{extra_sql}) { + $extra_sql = $pkg_query->{extra_sql} . ' AND '; +} else { + $extra_sql = 'WHERE '; +} +$extra_sql .= join(' AND ', @where); + +my $sql_query = { + 'table' => $table, + 'addl_from' => $from, + 'extra_sql' => $extra_sql, +}; +warn (Dumper $sql_query) if $cgi->param('debug'); + +my $count_query = "SELECT COUNT(*) FROM $table $from $extra_sql"; + +my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/ + ? '' + : ';show=packages'; + +my $link = sub { + my $self = shift; + my $frag = 'cust_pkg'. $self->pkgnum; #hack for IE ignoring real #fragment + [ "${p}view/cust_main.cgi?custnum=".$self->custnum. + "$show;fragment=$frag#cust_pkg", + 'pkgnum' + ]; +}; + +my $clink = sub { + my $cust_pkg = shift; + $cust_pkg->cust_main_custnum + ? [ "${p}view/cust_main.cgi?", 'custnum' ] + : ''; +}; + +sub time_or_blank { + my $column = shift; + return sub { + my $record = shift; + my $value = $record->get($column); #mmm closures + $value ? time2str('%b %d %Y', $value ) : ''; + }; +} + + -- cgit v1.2.1