RT#15413 (Duplicate rating prefixes)
[freeside.git] / httemplate / browse / part_pkg.cgi
index 5dee5b8..87e7aad 100755 (executable)
@@ -1,5 +1,6 @@
 <% include( 'elements/browse.html',
                  'title'                 => 'Package Definitions',
+                 'menubar'               => \@menubar,
                  'html_init'             => $html_init,
                  'html_form'             => $html_form,
                  'html_posttotal'        => $html_posttotal,
@@ -9,7 +10,7 @@
                  'agent_virt'            => 1,
                  'agent_null_right'      => [ $edit, $edit_global ],
                  'agent_null_right_link' => $edit_global,
-                 'agent_pos'             => 6,
+                 'agent_pos'             => 7, #5?
                  'query'                 => { 'select'    => $select,
                                               'table'     => 'part_pkg',
                                               'hashref'   => \%hash,
@@ -75,12 +76,16 @@ if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
 }
 $cgi->delete('classnum');
 
+if ( $cgi->param('pkgpartbatch') =~ /^([\w\/\-\:\. ]+)$/ ) {
+  push @where, "pkgpartbatch = '$1' ";
+}
+
 if ( $cgi->param('missing_recur_fee') ) {
-  push @where, "0 = ( SELECT COUNT(*) FROM part_pkg_option
-                        WHERE optionname = 'recur_fee'
-                          AND part_pkg_option.pkgpart = part_pkg.pkgpart
-                          AND CAST( optionvalue AS NUMERIC ) > 0
-                    )";
+  push @where, "NOT EXISTS ( SELECT 1 FROM part_pkg_option
+                               WHERE optionname = 'recur_fee'
+                                 AND part_pkg_option.pkgpart = part_pkg.pkgpart
+                                 AND CAST( optionvalue AS NUMERIC ) > 0
+                           )";
 }
 
 if ( $cgi->param('family') =~ /^(\d+)$/ ) {
@@ -107,6 +112,14 @@ my $count_cust_pkg = "
     WHERE cust_pkg.pkgpart = part_pkg.pkgpart
       AND $agentnums_sql
 ";
+my $count_cust_pkg_cancel = "
+  SELECT COUNT(*) FROM cust_pkg LEFT JOIN cust_main USING ( custnum )
+    LEFT JOIN cust_pkg AS cust_pkg_next
+      ON (cust_pkg.pkgnum = cust_pkg_next.change_pkgnum)
+    WHERE cust_pkg.pkgpart = part_pkg.pkgpart
+      AND $agentnums_sql
+      AND cust_pkg.cancel IS NOT NULL AND cust_pkg.cancel != 0
+";
 
 $select = "
 
@@ -127,13 +140,25 @@ $select = "
   ( $count_cust_pkg
       AND ( cancel IS NULL OR cancel = 0 )
       AND susp IS NOT NULL AND susp != 0
+      AND setup IS NOT NULL AND setup != 0
   ) AS num_suspended,
 
   ( $count_cust_pkg
-      AND cancel IS NOT NULL AND cancel != 0
+      AND ( cancel IS NULL OR cancel = 0 )
+      AND susp IS NOT NULL AND susp != 0
+      AND ( setup IS NULL OR setup = 0 )
+  ) AS num_on_hold,
+
+  ( $count_cust_pkg_cancel
+      AND (cust_pkg_next.pkgnum IS NULL
+           OR cust_pkg_next.pkgpart != cust_pkg.pkgpart)
   ) AS num_cancelled
 
 ";
+# About the num_cancelled expression: packages that were changed, but 
+# kept the same pkgpart, are considered "moved", not "canceled" (because
+# this is the part_pkg UI).  We could show the count of those but it's 
+# probably not interesting.
 
 my $html_init = qq!
     One or more service definitions are grouped together into a package 
@@ -240,10 +265,10 @@ push @fields, sub {
     ],
     [
       { data =>$money_char.
-               sprintf('%.2f', $part_pkg->option('setup_fee') ),
+               sprintf('%.2f ', $part_pkg->option('setup_fee') ),
         align=>'right'
       },
-      { data => ( ( $is_recur ? ' setup' : ' one-time' ).
+      { data => ( ( $is_recur ? ' &nbsp; setup' : ' &nbsp; one-time' ).
                   ( $part_pkg->option('recur_fee') == 0
                       && $part_pkg->setup_show_zero
                     ? ' (printed on invoices)'
@@ -256,7 +281,7 @@ push @fields, sub {
     [
       { data=>(
           $is_recur
-            ? $money_char. sprintf('%.2f ', $part_pkg->option('recur_fee'))
+            ? $money_char. sprintf('%.2f', $part_pkg->option('recur_fee'))
             : $part_pkg->freq_pretty
         ),
         align=> ( $is_recur ? 'right' : 'center' ),
@@ -264,7 +289,7 @@ push @fields, sub {
       },
       ( $is_recur
         ?  { data => ( $is_recur
-               ? $part_pkg->freq_pretty.
+               ? ' &nbsp; '. $part_pkg->freq_pretty.
                  ( $part_pkg->option('recur_fee') == 0
                      && $part_pkg->recur_show_zero
                    ? ' (printed on invoices)'
@@ -276,6 +301,20 @@ push @fields, sub {
         : ()
       ),
     ],
+    (
+      map { my $amount = $_->amount / ($_->target_info->{multiplier} || 1);
+            my $label = $_->target_info->{label};
+            [
+              { data    => "Plus&nbsp;$money_char". $_->price. '&nbsp;'.
+                           ( $_->action eq 'increment' ? 'per' : 'for' ).
+                           "&nbsp;$amount&nbsp;$label",
+                align   => 'center', #left?
+                colspan => 2,
+              },
+            ];
+          }
+        $part_pkg->part_pkg_usageprice
+    ),
     ( map { my $dst_pkg = $_->dst_pkg;
             [
               { data => 'Supplemental: &nbsp;'.
@@ -332,6 +371,51 @@ push @fields, sub {
 #    $part_pkg->freq_pretty; #.'<BR>'
 };
 
+push @header, 'Cost&nbsp;tracking';
+$align .= 'r'; #?
+push @fields, sub {
+  my $part_pkg = shift;
+  #(my $plan = $plan_labels{$part_pkg->plan} ) =~ s/ /&nbsp;/g;
+  my $is_recur = ( $part_pkg->freq ne '0' );
+
+  [
+    [
+      { data => '&nbsp;', # $plan,
+        align=>'center',
+        colspan=>2,
+      },
+    ],
+    [
+      { data =>$money_char.
+               sprintf('%.2f ', $part_pkg->setup_cost ),
+        align=>'right'
+      },
+      { data => ( $is_recur ? '&nbsp;setup' : '&nbsp;one-time' ),
+        align=>'left',
+      },
+    ],
+    [
+      { data=>(
+          $is_recur
+            ? $money_char. sprintf('%.2f', $part_pkg->recur_cost)
+            : '(no&nbsp;recurring)' #$part_pkg->freq_pretty
+        ),
+        align=> ( $is_recur ? 'right' : 'center' ),
+        colspan=> ( $is_recur ? 1 : 2 ),
+      },
+      ( $is_recur
+        ?  { data => ( $is_recur
+                         ? '&nbsp;'. $part_pkg->freq_pretty
+                         : ''
+                     ),
+             align=>'left',
+           }
+        : ()
+      ),
+    ],
+  ];
+};
+
 ###
 # Agent goes here if displayed
 ###
@@ -339,7 +423,8 @@ push @fields, sub {
 #agent type
 if ( $acl_edit_global ) {
   #really we just want a count, but this is fine unless someone has tons
-  my @all_agent_types = map {$_->typenum} qsearch('agent_type',{});
+  my @all_agent_types = map {$_->typenum}
+                          qsearch('agent_type', { 'disabled'=>'' });
   if ( scalar(@all_agent_types) > 1 ) {
     push @header, 'Agent types';
     my $typelink = $p. 'edit/agent_type.cgi?';
@@ -367,6 +452,7 @@ if ( $acl_edit_global ) {
 #if ( $cgi->param('active') ) {
   push @header, 'Customer<BR>packages';
   my %col = (
+    'on hold'         => '7E0079', #purple!
     'not yet billed'  => '009999', #teal? cyan?
     'active'          => '00CC00',
     'suspended'       => 'FF9900',
@@ -382,10 +468,11 @@ if ( $acl_edit_global ) {
                               my $label = $_;
                               if ( $magic eq 'active' && $part_pkg->freq == 0 ) {
                                 $magic = 'inactive';
-                                #$label = 'one-time charge',
-                                $label = 'charge',
+                                #$label = 'one-time charge';
+                                $label = 'charge';
                               }
                               $label= 'not yet billed' if $magic eq 'not_yet_billed';
+                              $label= 'on hold' if $magic eq 'on_hold';
                           
                               [
                                 {
@@ -410,7 +497,7 @@ if ( $acl_edit_global ) {
                                             ),
                                 },
                               ],
-                            } (qw( not_yet_billed active suspended cancelled ))
+                            } (qw( on_hold not_yet_billed active suspended cancelled ))
                           ),
                       ($acl_config ? 
                         [ {}, 
@@ -517,6 +604,8 @@ push @fields,
 
               sub {
                     my $part_pkg = shift;
+                    my @part_pkg_usage = sort { $a->priority <=> $b->priority }
+                                         $part_pkg->part_pkg_usage;
 
                     [ 
                       (map {
@@ -559,7 +648,27 @@ push @fields,
                               ]
                             }
                         $part_pkg->svc_part_pkg_link
-                      )
+                      ),
+                      ( scalar(@part_pkg_usage) ? 
+                          [ { data  => 'Usage minutes',
+                              align => 'center',
+                              colspan    => 2,
+                              data_style => 'b',
+                              link  => $p.'browse/part_pkg_usage.html#pkgpart'.
+                                       $part_pkg->pkgpart 
+                            } ]
+                          : ()
+                      ),
+                      ( map {
+                              [ { data  => $_->minutes,
+                                  align => 'right'
+                                },
+                                { data  => $_->description,
+                                  align => 'left'
+                                },
+                              ]
+                            } @part_pkg_usage
+                      ),
                     ];
 
                   };
@@ -590,4 +699,9 @@ if ( $acl_edit_bulk ) {
   ) . '</FORM>';
 }
 
+my @menubar;
+# show this if there are any voip_cdr packages defined
+if ( FS::part_pkg->count("plan = 'voip_cdr'") ) {
+  push @menubar, 'Per-package usage minutes' => $p.'browse/part_pkg_usage.html';
+}
 </%init>