From: mark Date: Mon, 25 Oct 2010 22:22:42 +0000 (+0000) Subject: summary display of bulk packages with many services, RT#9821 X-Git-Tag: TORRUS_1_0_9~186 X-Git-Url: http://git.freeside.biz/gitweb/?a=commitdiff_plain;h=0a51b87d75f8c93de863f729ef1ca568ca227e32;p=freeside.git summary display of bulk packages with many services, RT#9821 --- diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index b0b44e262..72e38d3d2 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -2632,6 +2632,13 @@ and customer address. Include units.', }, { + 'key' => 'cust_pkg-large_pkg_size', + 'section' => 'UI', + 'description' => "In customer view, summarize packages with more than this many services. Set to zero to never summarize packages.", + 'type' => 'text', + }, + + { 'key' => 'svc_acct-edit_uid', 'section' => 'shell', 'description' => 'Allow UID editing.', diff --git a/FS/FS/part_pkg/bulk.pm b/FS/FS/part_pkg/bulk.pm index 69fe98e92..a346b9096 100644 --- a/FS/FS/part_pkg/bulk.pm +++ b/FS/FS/part_pkg/bulk.pm @@ -29,9 +29,13 @@ $me = '[FS::part_pkg::bulk]'; ' of service at cancellation', 'type' => 'checkbox', }, + 'summarize_svcs'=> { 'name' => 'Show a count of services on the invoice, '. + 'instead of a detailed list', + 'type' => 'checkbox', + }, }, 'fieldorder' => [ 'setup_fee', 'recur_fee', 'svc_setup_fee', 'svc_recur_fee', - 'unused_credit', ], + 'unused_credit', 'summarize_svcs' ], 'weight' => 50, ); @@ -50,6 +54,11 @@ sub calc_recur { unless $$sdate > $last_bill; my $total_svc_charge = 0; + my %n_setup = (); + my %n_recur = (); + my %part_svc_label = (); + + my $summarize = $self->option('summarize_svcs',1); warn "$me billing for bulk services from ". time2str('%x', $last_bill). " to ". time2str('%x', $$sdate). "\n" @@ -61,6 +70,7 @@ sub calc_recur { my @label = $h_cust_svc->label_long( $$sdate, $last_bill ); die "fatal: no historical label found, wtf?" unless scalar(@label); #? my $svc_details = $label[0]. ': '. $label[1]. ': '; + $part_svc_label{$h_cust_svc->svcpart} ||= $label[0]; my $svc_charge = 0; @@ -70,6 +80,7 @@ sub calc_recur { } elsif ( $svc_setup_fee ) { $svc_charge += $svc_setup_fee; $svc_details .= $money_char. sprintf('%.2f setup, ', $svc_setup_fee); + $n_setup{$h_cust_svc->svcpart}++; } my $svc_end = $h_cust_svc->date_deleted; @@ -85,11 +96,21 @@ sub calc_recur { if $recur_charge; $svc_charge += $recur_charge; - - push @$details, $svc_details; + $n_recur{$h_cust_svc->svcpart}++; + push @$details, $svc_details if !$summarize; $total_svc_charge += $svc_charge; } + if ( $summarize ) { + foreach my $svcpart (keys %part_svc_label) { + push @$details, sprintf('Setup fee: %d @ '.$money_char.'%.2f', + $n_setup{$svcpart}, $svc_setup_fee ) + if $svc_setup_fee and $n_setup{$svcpart}; + push @$details, sprintf('%d services @ '.$money_char.'%.2f', + $n_recur{$svcpart}, $self->option('svc_recur_fee') ) + if $n_recur{$svcpart}; + } + } sprintf('%.2f', $self->base_recur($cust_pkg) + $total_svc_charge ); } diff --git a/FS/FS/part_pkg/voip_cdr.pm b/FS/FS/part_pkg/voip_cdr.pm index 9981da0c9..bcc2c6bc6 100644 --- a/FS/FS/part_pkg/voip_cdr.pm +++ b/FS/FS/part_pkg/voip_cdr.pm @@ -226,6 +226,10 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); 'type' => 'checkbox', }, + 'bill_inactive_svcs' => { 'name' => 'Bill for all phone numbers that were active during the billing period', + 'type' => 'checkbox', + }, + 'count_available_phones' => { 'name' => 'Consider for tax purposes the number of lines to be svc_phones that may be provisioned rather than those that actually are.', 'type' => 'checkbox', }, @@ -278,7 +282,7 @@ tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); use_duration 411_rewrite output_format usage_mandate summarize_usage usage_section - bill_every_call + bill_every_call bill_inactive_svcs count_available_phones ) ], @@ -365,11 +369,25 @@ sub calc_usage { my($svc_table, $svc_field) = split('\.', $cdr_svc_method); - foreach my $cust_svc ( - grep { $_->part_svc->svcdb eq $svc_table } $cust_pkg->cust_svc - ) { + my @cust_svc; + if( $self->option('bill_inactive_svcs',1) ) { + #XXX in this mode do we need to restrict the set of CDRs by date also? + @cust_svc = $cust_pkg->h_cust_svc($$sdate, $last_bill); + } + else { + @cust_svc = $cust_pkg->cust_svc; + } + @cust_svc = grep { $_->part_svc->svcdb eq $svc_table } @cust_svc; + + foreach my $cust_svc (@cust_svc) { - my $svc_x = $cust_svc->svc_x; + my $svc_x; + if( $self->option('bill_inactive_svcs',1) ) { + $svc_x = $cust_svc->h_svc_x($$sdate, $last_bill); + } + else { + $svc_x = $cust_svc->svc_x; + } my %options = ( 'disable_src' => $self->option('disable_src'), 'default_prefix' => $self->option('default_prefix'), @@ -709,7 +727,7 @@ sub calc_usage { if ( $charge > 0 ) { #just use FS::cust_bill_pkg_detail objects? my $call_details; - my $phonenum = $cust_svc->svc_x->phonenum; + my $phonenum = $svc_x->phonenum; if ( scalar(@call_details) == 1 ) { $call_details = diff --git a/httemplate/misc/unprovision.cgi b/httemplate/misc/unprovision.cgi index 4ab15fdc0..6f2c23815 100755 --- a/httemplate/misc/unprovision.cgi +++ b/httemplate/misc/unprovision.cgi @@ -1,6 +1,8 @@ %if ( $error ) { % errorpage($error); -%} else { +%} elsif ( $pkgnum ) { +<% $cgi->redirect(popurl(2)."search/cust_pkg_svc.html?svcpart=$svcpart;pkgnum=$pkgnum") %> +%} else { # $custnum should always exist <% $cgi->redirect(popurl(2)."view/cust_main.cgi?$custnum") %> %} <%init> @@ -9,18 +11,28 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Unprovision customer service'); #untaint svcnum -my($query) = $cgi->keywords; -$query =~ /^(\d+)$/; -my $svcnum = $1; +my @svcnums; +my ($pkgnum, $svcpart, $custnum); +if( $cgi->param('svcnum') ) { + @svcnums = grep { $_ } map { /^(\d+)$/ && $1 } $cgi->param('svcnum'); + $pkgnum = $cgi->param('pkgnum'); + $svcpart = $cgi->param('svcpart'); + $custnum = $cgi->param('custnum'); +} +else { + @svcnums = map { /^(\d+)$/ && $1 } $cgi->keywords; +} -#my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$svcnum}); -#die "Unknown svcnum!" unless $svc_acct; +my $error = ''; +foreach my $svcnum (@svcnums) { -my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum}); -die "Unknown svcnum!" unless $cust_svc; + my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum}); + die "Unknown svcnum!" unless $cust_svc; -my $custnum = $cust_svc->cust_pkg->custnum; + $custnum ||= $cust_svc->cust_pkg->custnum; -my $error = $cust_svc->cancel; + $error .= $cust_svc->cancel; + +} diff --git a/httemplate/search/cust_pkg_svc.html b/httemplate/search/cust_pkg_svc.html new file mode 100644 index 000000000..4f27d6617 --- /dev/null +++ b/httemplate/search/cust_pkg_svc.html @@ -0,0 +1,117 @@ +<% include( 'elements/search.html', + 'title' => $part_svc->svc.' services in package #'.$pkgnum, + 'name' => 'services', + 'html_form' => $html_form, + 'query' => $sql_query, + 'count_query' => $count_query, + 'redirect' => $link, + 'header' => [ '#', + 'Service', + '', #checkboxes + ], + 'fields' => [ 'svcnum', + sub { + ($_[0]->label)[1] + }, + sub { + $areboxes = 1; + '' + }, + ], + 'links' => [ $link, + $link, + '', + ], + 'align' => 'rrlc', + 'color' => [ + ('')x4, + ], + 'style' => [ + ('')x4, + ], + 'html_foot' => sub { $areboxes ? $html_foot : '' } + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('List services'); + +my $pkgnum = $cgi->param('pkgnum'); +$pkgnum =~ /^(\d+)$/ or die "invalid pkgnum: $pkgnum"; +my @extra_sql = ( "cust_svc.pkgnum = $pkgnum" ); + +my $svcpart = $cgi->param('svcpart'); +$svcpart =~ /^(\d+)$/ or die "invalid svcpart: $svcpart"; +push @extra_sql, "cust_svc.svcpart = $svcpart"; +my $part_svc = qsearchs('part_svc', {svcpart => $svcpart}); +my $svcdb = $part_svc->svcdb; + +my $orderby = 'ORDER BY svcnum'; #others? + +my $addl_from = " LEFT JOIN part_svc USING (svcpart) +LEFT JOIN cust_pkg USING (pkgnum) +LEFT JOIN cust_main USING (custnum) +INNER JOIN $svcdb USING (svcnum)"; + +my $search_string; +if ( length( $cgi->param('search_svc') ) ) { + + $search_string = $cgi->param('search_svc'); + $search_string =~ s/(^\s+|\s+$)//; + push @extra_sql, "FS::$svcdb"->search_sql($search_string); + +} + +#here is the agent virtualization +push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql( + 'null_right' => 'View/link unlinked services' + ); + +my $extra_sql = ' WHERE '. join(' AND ', @extra_sql ); + +my $sql_query = { + 'select' => join(', ', + 'cust_svc.*', + 'part_svc.svc', + ), + 'table' => 'cust_svc', + 'addl_from' => $addl_from, + 'hashref' => {}, + 'extra_sql' => "$extra_sql $orderby", +}; + +#warn Dumper($sql_query)."\n"; + +my $count_query = "SELECT COUNT(*) FROM cust_svc $addl_from $extra_sql"; + +my $link = sub { + my $cust_svc = shift; + my $url = svc_url( + 'm' => $m, + 'action' => 'view', + 'svcdb' => $svcdb, + 'query' => '', + ); + [ $url, 'svcnum' ]; +}; + +my $html_form = qq! + +
!; + +my $areboxes = 0; + +my $html_foot = qq! +
+ + + +
!; + + + diff --git a/httemplate/search/cust_svc.html b/httemplate/search/cust_svc.html index 2c17561f2..61bfc7091 100644 --- a/httemplate/search/cust_svc.html +++ b/httemplate/search/cust_svc.html @@ -93,6 +93,10 @@ if ( length( $cgi->param('search_svc') ) ) { errorpage("No search term specified"); } +if ( $cgi->param('pkgnum') =~ /^(\d+)$/ ) { + push @extra_sql, "cust_svc.pkgnum = $1"; +} + #here is the agent virtualization push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql( 'null_right' => 'View/link unlinked services' @@ -113,6 +117,8 @@ my $sql_query = { 'extra_sql' => "$extra_sql $orderby", }; +warn Dumper($sql_query)."\n"; + my $count_query = "SELECT COUNT(*) FROM cust_svc $addl_from $extra_sql"; my $link = sub { diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index f0f156bf8..660d0ef86 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -163,6 +163,7 @@ my %conf_opt = ( 'legacy_link' => $conf->exists('legacy_link'), 'svc_broadband-manage_link' => scalar($conf->config('svc_broadband-manage_link')), 'maestro-status_test' => $conf->exists('maestro-status_test'), + 'cust_pkg-large_pkg_size' => $conf->config('cust_pkg-large_pkg_size'), ); #subroutines diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html index 6e30922c5..512efccc4 100644 --- a/httemplate/view/cust_main/packages/services.html +++ b/httemplate/view/cust_main/packages/services.html @@ -4,12 +4,40 @@ + % #foreach my $svcpart (sort {$a->{svcpart} <=> $b->{svcpart}} @{$pkg->{svcparts}}) { % foreach my $part_svc ( $cust_pkg->part_svc ) { -% #foreach my $service (@{$svcpart->{services}}) { -% foreach my $cust_svc ( @{ $part_svc->cust_pkg_svc } ) { +% if ( $opt{'cust_pkg-large_pkg_size'} > 0 and +% $opt{'cust_pkg-large_pkg_size'} <= $cust_pkg->num_svcs ) { +% # summarize + + + +% } +% else { +% foreach my $cust_svc ( @{ $part_svc->cust_pkg_svc } ) { @@ -65,7 +93,8 @@ -% } +% } #foreach $cust_svc +% } % if ( ! $cust_pkg->get('cancel') % && $curuser->access_right('Provision customer service') @@ -137,4 +166,13 @@ sub svc_unprovision_link { qq!', 'Permanently unprovision and delete this service?')">Unprovision!; } +my %hints = ( +svc_acct => '(user or email)', +svc_domain => '(domain)', +svc_broadband => '(ip or mac)', +svc_forward => '(email)', +svc_phone => '(phone)', +svc_pbx => '(phone)', +); + diff --git a/httemplate/view/elements/svc_Common.html b/httemplate/view/elements/svc_Common.html index 852640e0c..8a352f3fa 100644 --- a/httemplate/view/elements/svc_Common.html +++ b/httemplate/view/elements/svc_Common.html @@ -21,6 +21,13 @@ ) + + % if ( $custnum ) { <% include("/elements/header.html","View $label: $value") %> @@ -36,18 +43,13 @@ "javascript:areyousure(\'${p}misc/cancel-unaudited.cgi?$svcnum\')" )) %> - - % } Service #<% $svcnum %> % my $url = $opt{'edit_url'} || $p. 'edit/'. $opt{'table'}. '.cgi?'; | Edit this <% $label %> +| +Unprovision this Service
<% ntable("#cccccc") %>
+% my $href="${p}search/cust_pkg_svc.html?svcpart=".$part_svc->svcpart. +% ";pkgnum=".$cust_pkg->pkgnum; + <% $part_svc->svc %>  + (view all <% $cust_pkg->num_svcs %>) +% my $hint = $hints{$part_svc->svcdb}; +% if ( $hint ) { +
+
+ + + +
+% } #$hint +
<% FS::UI::Web::svc_link($m, $part_svc, $cust_svc) %>
<% ntable("#cccccc",2) %>