[freeside-commits] branch master updated. edea94b6bd41a3fceac47267a3df3dc7fcf7c565

Mark Wells mark at 420.am
Tue Sep 16 16:10:47 PDT 2014


The branch, master has been updated
       via  edea94b6bd41a3fceac47267a3df3dc7fcf7c565 (commit)
      from  202e6945393b47f5aa75a65682ab0d14e04fe7eb (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit edea94b6bd41a3fceac47267a3df3dc7fcf7c565
Author: Mark Wells <mark at freeside.biz>
Date:   Tue Sep 16 14:54:44 2014 -0700

    package start_on_hold flag, and better behavior for automatic timers + packages on hold, #25853

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 1de3709..04e5dd4 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -3052,6 +3052,7 @@ sub tables_hashref {
         'successor',     'int',     'NULL', '', '', '',
         'family_pkgpart','int',     'NULL', '', '', '',
         'delay_start',   'int',     'NULL', '', '', '',
+        'start_on_hold', 'char',    'NULL',  1, '', '',
         'agent_pkgpartid', 'varchar', 'NULL', 20, '', '',
       ],
       'primary_key'  => 'pkgpart',
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 174d5cf..6d3ed2e 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -241,6 +241,39 @@ sub cust_unlinked_msg {
   ' (cust_pkg.pkgnum '. $self->pkgnum. ')';
 }
 
+=item set_initial_timers
+
+If required by the package definition, sets any automatic expire, adjourn,
+or contract_end timers to some number of months after the start date 
+(or setup date, if the package has already been setup). If the package has
+a delayed setup fee after a period of "free days", will also set the 
+start date to the end of that period.
+
+=cut
+
+sub set_initial_timers {
+  my $self = shift;
+  my $part_pkg = $self->part_pkg;
+  foreach my $action ( qw(expire adjourn contract_end) ) {
+    my $months = $part_pkg->option("${action}_months",1);
+    if($months and !$self->get($action)) {
+      my $start = $self->start_date || $self->setup || time;
+      $self->set($action, $part_pkg->add_freq($start, $months) );
+    }
+  }
+
+  # if this package has "free days" and delayed setup fee, then
+  # set start date that many days in the future.
+  # (this should have been set in the UI, but enforce it here)
+  if ( $part_pkg->option('free_days',1)
+       && $part_pkg->option('delay_setup',1)
+     )
+  {
+    $self->start_date( $part_pkg->default_start_date );
+  }
+  '';
+}
+
 =item insert [ OPTION => VALUE ... ]
 
 Adds this billing item to the database ("Orders" the item).  If there is an
@@ -305,6 +338,9 @@ sub insert {
 
   if ( ! $import && ! $options{'change'} ) {
 
+    # set order date to now
+    $self->order_date(time) unless ($import && $self->order_date);
+
     # if the package def says to start only on the first of the month:
     if ( $part_pkg->option('start_1st', 1) && !$self->start_date ) {
       my ($sec,$min,$hour,$mday,$mon,$year) = (localtime(time) )[0,1,2,3,4,5];
@@ -313,35 +349,17 @@ sub insert {
       $self->start_date( timelocal_nocheck(0,0,0,1,$mon,$year) );
     }
 
-    # set up any automatic expire/adjourn/contract_end timers
-    # based on the start date
-    foreach my $action ( qw(expire adjourn contract_end) ) {
-      my $months = $part_pkg->option("${action}_months",1);
-      if($months and !$self->$action) {
-        my $start = $self->start_date || $self->setup || time;
-        $self->$action( $part_pkg->add_freq($start, $months) );
-      }
-    }
-
-    # if this package has "free days" and delayed setup fee, then 
-    # set start date that many days in the future.
-    # (this should have been set in the UI, but enforce it here)
-    if (    ! $options{'change'}
-         && $part_pkg->option('free_days', 1)
-         && $part_pkg->option('delay_setup',1)
-         #&& ! $self->start_date
-       )
-    {
-      $self->start_date( $part_pkg->default_start_date );
+    if ($self->susp eq 'now' or $part_pkg->start_on_hold) {
+      # if the package was ordered on hold:
+      # - suspend it
+      # - don't set the start date (it will be started manually)
+      $self->set('susp', $self->order_date);
+      $self->set('start_date', '');
+    } else {
+      # set expire/adjourn/contract_end timers, and free days, if appropriate
+      $self->set_initial_timers;
     }
-  }
-
-  # set order date unless it was specified as part of an import
-  # or this was previously a different package
-  $self->order_date(time) unless ($import && $self->order_date)
-                              or $self->change_pkgnum;
-
-  $self->susp( $self->order_date ) if $self->susp eq 'now';
+  } # else this is a package change, and shouldn't have "new package" behavior
 
   my $oldAutoCommit = $FS::UID::AutoCommit;
   local $FS::UID::AutoCommit = 0;
@@ -1470,6 +1488,8 @@ sub unsuspend {
     return "";  # no error                     # complain instead?
   }
 
+  # handle the case of setting a future unsuspend (resume) date
+  # and do not continue to actually unsuspend the package
   my $date = $opt{'date'};
   if ( $date and $date > time ) { # return an error if $date <= time?
 
@@ -1493,6 +1513,11 @@ sub unsuspend {
   
   } #if $date 
 
+  if (!$self->setup) {
+    # then this package is being released from on-hold status
+    $self->set_initial_timers;
+  }
+
   my @labels = ();
 
   foreach my $cust_svc (
diff --git a/FS/FS/part_pkg.pm b/FS/FS/part_pkg.pm
index 77f70e4..59bc051 100644
--- a/FS/FS/part_pkg.pm
+++ b/FS/FS/part_pkg.pm
@@ -122,6 +122,10 @@ part_pkg, will be equal to pkgpart.
 
 =item delay_start - Number of days to delay package start, by default
 
+=item start_on_hold - 'Y' to suspend this package immediately when it is 
+ordered. The package will not start billing or have a setup fee charged 
+until it is manually unsuspended.
+
 =back
 
 =head1 METHODS
@@ -672,14 +676,15 @@ sub check {
     || $self->ut_textn('comment')
     || $self->ut_textn('promo_code')
     || $self->ut_alphan('plan')
-    || $self->ut_enum('setuptax', [ '', 'Y' ] )
-    || $self->ut_enum('recurtax', [ '', 'Y' ] )
+    || $self->ut_flag('setuptax')
+    || $self->ut_flag('recurtax')
     || $self->ut_textn('taxclass')
-    || $self->ut_enum('disabled', [ '', 'Y' ] )
-    || $self->ut_enum('custom', [ '', 'Y' ] )
-    || $self->ut_enum('no_auto', [ '', 'Y' ])
-    || $self->ut_enum('recur_show_zero', [ '', 'Y' ])
-    || $self->ut_enum('setup_show_zero', [ '', 'Y' ])
+    || $self->ut_flag('disabled')
+    || $self->ut_flag('custom')
+    || $self->ut_flag('no_auto')
+    || $self->ut_flag('recur_show_zero')
+    || $self->ut_flag('setup_show_zero')
+    || $self->ut_flag('start_on_hold')
     #|| $self->ut_moneyn('setup_cost')
     #|| $self->ut_moneyn('recur_cost')
     || $self->ut_floatn('setup_cost')
@@ -1130,7 +1135,10 @@ sub is_free {
 sub can_discount { 0; }
  
 # whether the plan allows changing the start date
-sub can_start_date { 1; }
+sub can_start_date {
+  my $self = shift;
+  $self->start_on_hold ? 0 : 1;
+}
 
 # whether the plan supports part_pkg_usageprice add-ons (a specific kind of
 #  pre-selectable usage pricing, there's others this doesn't refer to)
diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm
index 1594a13..cb2986e 100644
--- a/FS/FS/part_pkg/flat.pm
+++ b/FS/FS/part_pkg/flat.pm
@@ -272,6 +272,7 @@ sub is_prepaid { 0; } #no, we're postpaid
 sub can_start_date {
   my $self = shift;
   my %opt = @_;
+  return 0 if $self->start_on_hold;
 
   ! $self->option('start_1st', 1) && (   ! $self->option('sync_bill_date',1)
                                       || ! $self->option('prorate_defer_bill',1)
diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi
index 65eca6c..cc5606e 100755
--- a/httemplate/edit/part_pkg.cgi
+++ b/httemplate/edit/part_pkg.cgi
@@ -44,6 +44,7 @@
                    'plan'             => 'Price plan',
                    'disabled'         => 'Disable new orders',
                    'disable_line_item_date_ranges' => 'Disable line item date ranges',
+                   'start_on_hold'    => 'Start on hold',
                    'setup_cost'       => 'Setup cost',
                    'recur_cost'       => 'Recur cost',
                    'pay_weight'       => 'Payment weight',
@@ -110,6 +111,10 @@
                      ),
                      {field=>'disabled', type=>$disabled_type, value=>'Y'},
                      {field=>'disable_line_item_date_ranges', type=>$disabled_type, value=>'Y'},
+                     { field => 'start_on_hold',
+                       type => 'checkbox',
+                       value => 'Y'
+                     },
 
                      { type     => 'tablebreak-tr-title',
                        value    => 'Pricing', #better name?
diff --git a/httemplate/elements/order_pkg.js b/httemplate/elements/order_pkg.js
index a145cbb..3586a54 100644
--- a/httemplate/elements/order_pkg.js
+++ b/httemplate/elements/order_pkg.js
@@ -10,7 +10,7 @@ function pkg_changed () {
     var date_text = document.getElementById('start_date_text');
 
     var radio_now = document.getElementById('start_now');
-    //var radio_on_hold = document.getElementById('start_on_hold');
+    var radio_on_hold = document.getElementById('start_on_hold');
     var radio_on_date = document.getElementById('start_on_date');
 
     form.submitButton.disabled = false;
@@ -36,23 +36,35 @@ function pkg_changed () {
       date_button.style.display = '';
       date_button_disabled.style.display = 'none';
       if ( radio_on_date ) {
+        // un-disable all the buttons that might get disabled
         radio_on_date.disabled = false;
-        if ( form.start_date_text.value.length > 0 && radio_now.checked ) {
+        radio_now.disabled = false;
+        // if a start date has been entered, assume the user wants it
+        if ( form.start_date_text.value.length > 0 ) {
           radio_now.checked = false;
           radio_on_date.checked = true;
+        } else {
+          // if not, default to now
+          radio_now.checked = true;
         }
       }
-    } else {
+    } else { // the package is either fixed start date or start-on-hold
       date_text.style.backgroundColor = '#dddddd';
       date_text.disabled = true;
       date_button.style.display = 'none';
       date_button_disabled.style.display = '';
       if ( radio_on_date ) {
-        if ( radio_on_date.checked ) {
-          radio_on_date.checked = false;
+        if ( opt.getAttribute('data-start_on_hold') == 1 ) {
+          // disallow all options but "On hold"
+          radio_on_hold.checked = true;
+          radio_now.checked = false;
+          radio_now.disabled = true;
+        } else {
+          // disallow all options but "On date"
+          radio_on_hold.checked = false;
           radio_now.checked = true;
+          radio_now.disabled = false;
         }
-        radio_on_date.disabled = true;
       }
     }
 
diff --git a/httemplate/elements/tr-select-cust-part_pkg.html b/httemplate/elements/tr-select-cust-part_pkg.html
index 696baff..0db989a 100644
--- a/httemplate/elements/tr-select-cust-part_pkg.html
+++ b/httemplate/elements/tr-select-cust-part_pkg.html
@@ -5,9 +5,10 @@
 
   <SCRIPT TYPE="text/javascript">
 
-    function part_pkg_opt(what, value, text, can_discount, can_start_date, start_date) {
+    function part_pkg_opt(what, value, text, can_discount, start_on_hold, can_start_date, start_date) {
       var optionName = new Option(text, value, false, false);
       optionName.setAttribute('data-can_discount',   can_discount);
+      optionName.setAttribute('data-start_on_hold',  start_on_hold);
       optionName.setAttribute('data-can_start_date', can_start_date);
       optionName.setAttribute('data-start_date',     start_date || '');
       var length = what.length;
@@ -37,14 +38,16 @@
         // add the new packages
         opt(what.form.pkgpart, '', 'Select package');
         var packagesArray = eval('(' + part_pkg + ')' );
-        for ( var s = 0; s < packagesArray.length; s=s+5 ) {
+        while (packagesArray.length > 0) {
           //surely this should be some kind of JSON structure
-          var packagesLabel  = packagesArray[s+1];
-          var can_discount   = packagesArray[s+2];
-          var can_start_date = packagesArray[s+3];
-          var start_date     = packagesArray[s+4];
+          var pkgpart        = packagesArray.shift();
+          var label          = packagesArray.shift();
+          var can_discount   = packagesArray.shift();
+          var start_on_hold  = packagesArray.shift();
+          var can_start_date = packagesArray.shift();
+          var start_date     = packagesArray.shift();
           part_pkg_opt(
-            what.form.pkgpart, packagesArray[s], packagesLabel, can_discount, can_start_date, start_date
+            what.form.pkgpart, pkgpart, label, can_discount, start_on_hold, can_start_date, start_date
           );
         }
 
diff --git a/httemplate/misc/cust-part_pkg.cgi b/httemplate/misc/cust-part_pkg.cgi
index e129347..dc9ba2a 100644
--- a/httemplate/misc/cust-part_pkg.cgi
+++ b/httemplate/misc/cust-part_pkg.cgi
@@ -56,6 +56,7 @@ my @return = map  {
                     ( $_->pkgpart,
                       $_->pkg_comment,
                       $_->can_discount,
+                      ($_->start_on_hold ? 1 : 0),
                       $_->can_start_date(
                         num_ncancelled_pkgs => $num_ncancelled_pkgs,
                       ),

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Schema.pm                                  |    1 +
 FS/FS/cust_pkg.pm                                |   81 ++++++++++++++--------
 FS/FS/part_pkg.pm                                |   24 ++++---
 FS/FS/part_pkg/flat.pm                           |    1 +
 httemplate/edit/part_pkg.cgi                     |    5 ++
 httemplate/elements/order_pkg.js                 |   24 +++++--
 httemplate/elements/tr-select-cust-part_pkg.html |   17 +++--
 httemplate/misc/cust-part_pkg.cgi                |    1 +
 8 files changed, 105 insertions(+), 49 deletions(-)




More information about the freeside-commits mailing list