Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / httemplate / browse / part_pkg.cgi
index 63822c7..c2f1430 100755 (executable)
@@ -76,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+)$/ ) {
@@ -108,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 = "
 
@@ -137,11 +149,16 @@ $select = "
       AND ( setup IS NULL OR setup = 0 )
   ) AS num_on_hold,
 
-  ( $count_cust_pkg
-      AND cancel IS NOT NULL AND cancel != 0
+  ( $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 
@@ -230,6 +247,7 @@ push @fields, sub {
                   $part_pkg->part_pkg_discount;
 
   [
+    # Line 0: Family package link (if applicable)
     ( !$family_pkgpart &&
       $part_pkg->pkgpart == $part_pkg->family_pkgpart ? () : [
       {
@@ -240,13 +258,13 @@ push @fields, sub {
         'link' => $p.'browse/part_pkg.cgi?family='.$part_pkg->family_pkgpart,
       }
     ] ),
-    [
+    [ # Line 1: Plan type (Anniversary, Prorate, Call Rating, etc.)
       { data =>$plan,
         align=>'center',
         colspan=>2,
       },
     ],
-    [
+    [ # Line 2: Setup fee
       { data =>$money_char.
                sprintf('%.2f ', $part_pkg->option('setup_fee') ),
         align=>'right'
@@ -261,7 +279,7 @@ push @fields, sub {
         align=>'left',
       },
     ],
-    [
+    [ # Line 3: Recurring fee
       { data=>(
           $is_recur
             ? $money_char. sprintf('%.2f', $part_pkg->option('recur_fee'))
@@ -271,20 +289,56 @@ push @fields, sub {
         colspan=> ( $is_recur ? 1 : 2 ),
       },
       ( $is_recur
-        ?  { data => ( $is_recur
-               ? '   '. $part_pkg->freq_pretty.
-                 ( $part_pkg->option('recur_fee') == 0
-                     && $part_pkg->recur_show_zero
-                   ? ' (printed on invoices)'
-                   : ''
-                 )
-               : '' ),
+        ?  { data => '   '. $part_pkg->freq_pretty.
+                     ( $part_pkg->option('recur_fee') == 0
+                         && $part_pkg->recur_show_zero
+                       ? ' (printed on invoices)'
+                       : ''
+                     ),
              align=>'left',
            }
         : ()
       ),
     ],
-    (
+    [ { data => ' ' }, ], # Line 4: empty
+    ( $part_pkg->adjourn_months ? 
+      [ # Line 5: Adjourn months
+        { data => mt('After [quant,_1,month], <strong>suspend</strong> the package.',
+                     $part_pkg->adjourn_months),
+          align => 'left',
+          size  => -1,
+          colspan => 2,
+        }
+      ] : ()
+    ),
+    ( $part_pkg->contract_end_months ? 
+      [ # Line 6: Contract end months
+        { data => mt('After [quant,_1,month], <strong>contract ends</strong>.',
+                     $part_pkg->contract_end_months),
+          align => 'left',
+          size  => -1,
+          colspan => 2,
+        }
+      ] : ()
+    ),
+    ( $part_pkg->expire_months ? 
+      [ # Line 7: Expire months and automatic transfer
+        { data => $part_pkg->change_to_pkgpart ?
+                    mt('After [quant,_1,month], <strong>change to</strong> ',
+                      $part_pkg->expire_months) .
+                    qq(<a href="${p}edit/part_pkg.cgi?) .
+                      $part_pkg->change_to_pkgpart .
+                      qq(">) . $part_pkg->change_to_pkg->pkg . qq(</a>) . '.'
+                  : mt('After [quant,_1,month], <strong>cancel</strong> the package.',
+                     $part_pkg->expire_months)
+          ,
+          align => 'left',
+          size  => -1,
+          colspan => 2,
+        }
+      ] : ()
+    ),
+    ( # Usage prices
       map { my $amount = $_->amount / ($_->target_info->{multiplier} || 1);
             my $label = $_->target_info->{label};
             [
@@ -298,7 +352,8 @@ push @fields, sub {
           }
         $part_pkg->part_pkg_usageprice
     ),
-    ( map { my $dst_pkg = $_->dst_pkg;
+    ( # Supplementals
+      map { my $dst_pkg = $_->dst_pkg;
             [
               { data => 'Supplemental: &nbsp;'.
                         '<A HREF="#'. $dst_pkg->pkgpart . '">' .
@@ -310,7 +365,8 @@ push @fields, sub {
           }
       $part_pkg->supp_part_pkg_link
     ),
-    ( map { 
+    ( # Billing add-ons/bundle packages
+      map { 
             my $dst_pkg = $_->dst_pkg;
             [ 
               { data => 'Add-on:&nbsp;'.$dst_pkg->pkg_comment,
@@ -321,7 +377,8 @@ push @fields, sub {
           }
       $part_pkg->bill_part_pkg_link
     ),
-    ( scalar(@discounts)
+    ( # Discounts available
+      scalar(@discounts)
         ?  [ 
               { data => '<b>Discounts</b>',
                 align=>'center', #?
@@ -343,7 +400,7 @@ push @fields, sub {
           @discounts
         : ()
     ),
-  ];
+  ]; # end of "middle column"
 
 #  $plan_labels{$part_pkg->plan}.'<BR>'.
 #    $money_char.sprintf('%.2f setup<BR>', $part_pkg->option('setup_fee') ).
@@ -406,7 +463,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?';
@@ -433,15 +491,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',
-    'cancelled'       => 'FF0000',
-    #'one-time charge' => '000000',
-    'charge'          => '000000',
-  );
+  my %col = %{ FS::cust_pkg->statuscolors };
   my $cust_pkg_link = $p. 'search/cust_pkg.cgi?pkgpart=';
   push @fields, sub { my $part_pkg = shift;
                         [