[freeside-commits] branch FREESIDE_3_BRANCH updated. c7c021dcf27486b808c0aed629a28ced42bc3d03

Mark Wells mark at 420.am
Sat Oct 15 21:07:41 PDT 2016


The branch, FREESIDE_3_BRANCH has been updated
       via  c7c021dcf27486b808c0aed629a28ced42bc3d03 (commit)
       via  3f371be77db54c521eb2bd7aba64b381d2e38049 (commit)
       via  4321924ff5a68868a7ee622920fd4bbd53d6475f (commit)
       via  23178237bcb103cb785829bf45cdd7a2710adabf (commit)
       via  815d4f25933a9693eb461cdf2311c2844c7a965d (commit)
      from  12f912d2840df202e14a88cf655994448db9b88f (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 c7c021dcf27486b808c0aed629a28ced42bc3d03
Author: Mark Wells <mark at freeside.biz>
Date:   Sat Oct 15 21:03:27 2016 -0700

    reconcile prorate-sync behavior with prorate rounding, #72928

diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm
index 8c24882..db493e7 100644
--- a/FS/FS/part_pkg/flat.pm
+++ b/FS/FS/part_pkg/flat.pm
@@ -14,6 +14,7 @@ use Tie::IxHash;
 use List::Util qw( min );
 use FS::UI::bytecount;
 use FS::Conf;
+use Time::Local 'timelocal';
 
 #ask FS::UID to run this stuff for us later
 FS::UID->install_callback( sub {
@@ -185,14 +186,28 @@ sub cutoff_day {
   }
   if ( $self->option('sync_bill_date',1) ) {
     my $next_bill = $cust_pkg->cust_main->next_bill_date;
-    if ( defined($next_bill) ) {
-      # careful here. if the prorate calculation is going to round to 
-      # the nearest day, this needs to always return the same result
-      if ( $self->option('prorate_round_day', 1) ) {
-        my $hour = (localtime($next_bill))[2];
-        $next_bill += 64800 if $hour >= 12;
-      }
+    if ( $next_bill ) {
       return (localtime($next_bill))[3];
+    } else {
+      # This is the customer's only active package and hasn't been billed
+      # yet, so set the cutoff day to either today or tomorrow, whichever
+      # would result in a full period after rounding.
+      my $setup = $cust_pkg->setup; # because it's "now"
+      my $rounding_mode = $self->option('prorate_round_day',1);
+      return () if !$setup or !$rounding_mode;
+      my ($sec, $min, $hour, $mday, $mon, $year) = localtime($setup);
+
+      if (   ( $rounding_mode == 1 and $hour >= 12 )
+          or ( $rounding_mode == 3 and ( $sec > 0 or $min > 0 or $hour > 0 ))
+      ) {
+        # then the prorate period will be rounded down to start from
+        # midnight tomorrow, so the cutoff day should be the current day +
+        # 1.
+        $setup = timelocal(59,59,23,$mday,$mon,$year) + 1;
+        $mday = (localtime($setup))[3];
+      }
+      # otherwise, it will be rounded up, so leave the cutoff day at today.
+      return $mday;
     }
   }
   return ();

commit 3f371be77db54c521eb2bd7aba64b381d2e38049
Author: Mark Wells <mark at freeside.biz>
Date:   Sat Oct 15 21:03:17 2016 -0700

    improve testing of prorate-sync behavior, #72928, #42108, and #34622

diff --git a/FS/t/suite/05-prorate_sync_same_day.t b/FS/t/suite/05-prorate_sync_same_day.t
index 315be06..ec78f54 100755
--- a/FS/t/suite/05-prorate_sync_same_day.t
+++ b/FS/t/suite/05-prorate_sync_same_day.t
@@ -5,10 +5,15 @@
 Tests the effect of ordering and activating two sync_bill_date packages on
 the same day. Ref RT#42108.
 
-Correct: If the packages have prorate_round_day = 1 (round nearest), or 3
-(round down) then the second package should be prorated one day short. If
-they have prorate_round_day = 2 (round up), they should be billed
-for the same amount. In both cases they should have the same next bill date.
+Formerly correct: If the packages have prorate_round_day = 1 (round
+nearest), or 3 (round down) then the second package should be prorated one
+day short. If they have prorate_round_day = 2 (round up), they should be
+billed for the same amount. In both cases they should have the same next
+bill date.
+
+Revised RT#72928: The second package should be prorated one day short only
+if the rounding mode is 1 (round nearest), as the nearest day is different
+for the two packages.
 
 =cut
 
@@ -82,7 +87,7 @@ foreach my $prorate_mode (1, 2, 3) {
   $error = $cust->bill_and_collect;
 
   # Check the amount billed.
-  if ( $prorate_mode == 1 or $prorate_mode == 3 ) {
+  if ( $prorate_mode == 1 ) {
     # it should be one day short, in March
     $recur = sprintf('%.2f', $recur * 30/31);
   }
diff --git a/FS/t/suite/10-prorate_sync_same_hour.t b/FS/t/suite/10-prorate_sync_same_hour.t
new file mode 100755
index 0000000..f1e3185
--- /dev/null
+++ b/FS/t/suite/10-prorate_sync_same_hour.t
@@ -0,0 +1,102 @@
+#!/usr/bin/perl
+
+=head2 DESCRIPTION
+
+Tests the effect of ordering and activating two sync_bill_date packages
+either both before or both after noon, less than an hour apart. Ref RT#42108
+and #72928.
+
+Correct: The packages should always end up with the same next bill date,
+and should be billed for a full period, except in the case where the first
+package starts at midnight and the rounding mode is "always round down".
+
+=cut
+
+use strict;
+use Test::More tests => 27;
+use FS::Test;
+use Date::Parse 'str2time';
+use Date::Format 'time2str';
+use Test::MockTime qw(set_fixed_time);
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::Conf;
+my $FS= FS::Test->new;
+
+foreach my $prorate_mode (1, 2, 3) {
+  diag("prorate_round_day = $prorate_mode");
+  # Create a package def with the sync_bill_date option.
+  my $error;
+  my $old_part_pkg = $FS->qsearchs('part_pkg', { pkgpart => 5 });
+  my $part_pkg = $old_part_pkg->clone;
+  BAIL_OUT("existing pkgpart 5 is not a flat monthly package")
+    unless $part_pkg->freq eq '1' and $part_pkg->plan eq 'flat';
+  $error = $part_pkg->insert(
+    options => {  $old_part_pkg->options,
+                  'sync_bill_date' => 1,
+                  'prorate_round_day' => $prorate_mode, }
+  );
+
+  BAIL_OUT("can't configure package: $error") if $error;
+
+  my $pkgpart = $part_pkg->pkgpart;
+  # Create a clean customer with no other packages.
+  foreach my $hour (0, 8, 16) {
+    diag("$hour:00");
+    my $location = FS::cust_location->new({
+        address1  => '123 Example Street',
+        city      => 'Sacramento',
+        state     => 'CA',
+        country   => 'US',
+        zip       => '94901',
+    });
+    my $cust = FS::cust_main->new({
+        agentnum      => 1,
+        refnum        => 1,
+        last          => 'Customer',
+        first         => 'Sync bill date',
+        invoice_email => 'newcustomer at fake.freeside.biz',
+        payby         => 'BILL',
+        bill_location => $location,
+        ship_location => $location,
+    });
+    $error = $cust->insert;
+    BAIL_OUT("can't create test customer: $error") if $error;
+
+    my @pkgs;
+    # Create and bill the first package.
+    set_fixed_time(str2time("2016-03-10 $hour:00"));
+    $pkgs[0] = FS::cust_pkg->new({ pkgpart => $pkgpart });
+    $error = $cust->order_pkg({ 'cust_pkg' => $pkgs[0] });
+    BAIL_OUT("can't order package: $error") if $error;
+    $error = $cust->bill_and_collect;
+    # Check the amount billed.
+    my ($cust_bill_pkg) = $pkgs[0]->cust_bill_pkg;
+    my $recur = $part_pkg->base_recur;
+    ok( $cust_bill_pkg->recur == $recur, "first package recur is $recur" )
+      or diag("first package recur is ".$cust_bill_pkg->recur);
+
+    # Create and bill the second package.
+    set_fixed_time(str2time("2016-03-10 $hour:01"));
+    $pkgs[1] = FS::cust_pkg->new({ pkgpart => $pkgpart });
+    $error = $cust->order_pkg({ 'cust_pkg' => $pkgs[1] });
+    BAIL_OUT("can't order package: $error") if $error;
+    $error = $cust->bill_and_collect;
+
+    # Check the amount billed.
+    if ( $prorate_mode == 3 and $hour == 0 ) {
+      # special case: a start date of midnight won't be rounded down but any
+      # later start date will, so the second package will be one day short.
+      $recur = sprintf('%.2f', $recur * 30/31);
+    }
+    ($cust_bill_pkg) = $pkgs[1]->cust_bill_pkg;
+    ok( $cust_bill_pkg->recur == $recur, "second package recur is $recur" )
+      or diag("second package recur is ".$cust_bill_pkg->recur);
+
+    my @next_bill = map { time2str('%Y-%m-%d', $_->replace_old->get('bill')) } @pkgs;
+
+    ok( $next_bill[0] eq $next_bill[1],
+      "both packages will bill again on $next_bill[0]" )
+      or diag("first package bill date is $next_bill[0], second package is $next_bill[1]");
+  }
+}
diff --git a/FS/t/suite/11-prorate_sync_single_pkg.t b/FS/t/suite/11-prorate_sync_single_pkg.t
new file mode 100755
index 0000000..83308f5
--- /dev/null
+++ b/FS/t/suite/11-prorate_sync_single_pkg.t
@@ -0,0 +1,89 @@
+#!/usr/bin/perl
+
+=head2 DESCRIPTION
+
+Tests the effect of ordering a sync_bill_date package either before or
+after noon and billing it for two consecutive cycles, in all three prorate
+rounding modes (round nearest, round up, and round down). Ref RT#34622.
+
+Correct: It should be charged full price in both cycles regardless of
+the prorate rounding mode, as long as prorate rounding is enabled.
+
+=cut
+
+use strict;
+use Test::More tests => 18;
+use FS::Test;
+use Date::Parse 'str2time';
+use Date::Format 'time2str';
+use Test::MockTime qw(set_fixed_time);
+use FS::cust_main;
+use FS::cust_pkg;
+use FS::Conf;
+my $FS= FS::Test->new;
+
+foreach my $prorate_mode (1, 2, 3) {
+  diag("prorate_round_day = $prorate_mode");
+  # Create a package def with the sync_bill_date option.
+  my $error;
+  my $old_part_pkg = $FS->qsearchs('part_pkg', { pkgpart => 5 });
+  my $part_pkg = $old_part_pkg->clone;
+  BAIL_OUT("existing pkgpart 5 is not a flat monthly package")
+    unless $part_pkg->freq eq '1' and $part_pkg->plan eq 'flat';
+  $error = $part_pkg->insert(
+    options => {  $old_part_pkg->options,
+                  'sync_bill_date' => 1,
+                  'prorate_round_day' => $prorate_mode, }
+  );
+
+  BAIL_OUT("can't configure package: $error") if $error;
+
+  my $pkgpart = $part_pkg->pkgpart;
+  # Create a clean customer with no other packages.
+  foreach my $hour (0, 8, 16) {
+    diag("$hour:00");
+    my $location = FS::cust_location->new({
+        address1  => '123 Example Street',
+        city      => 'Sacramento',
+        state     => 'CA',
+        country   => 'US',
+        zip       => '94901',
+    });
+    my $cust = FS::cust_main->new({
+        agentnum      => 1,
+        refnum        => 1,
+        last          => 'Customer',
+        first         => 'Sync bill date',
+        invoice_email => 'newcustomer at fake.freeside.biz',
+        payby         => 'BILL',
+        bill_location => $location,
+        ship_location => $location,
+    });
+    $error = $cust->insert;
+    BAIL_OUT("can't create test customer: $error") if $error;
+
+    my $pkg;
+    # Create and bill the package.
+    set_fixed_time(str2time("2016-03-10 $hour:00"));
+    $pkg = FS::cust_pkg->new({ pkgpart => $pkgpart });
+    $error = $cust->order_pkg({ 'cust_pkg' => $pkg });
+    BAIL_OUT("can't order package: $error") if $error;
+    $error = $cust->bill_and_collect;
+    BAIL_OUT("can't bill package: $error") if $error;
+
+    # Bill it a second time.
+    $pkg = $pkg->replace_old;
+    set_fixed_time($pkg->bill);
+    $error = $cust->bill_and_collect;
+    BAIL_OUT("can't bill package: $error") if $error;
+
+    # Check the amount billed.
+    my $recur = $part_pkg->base_recur;
+    my @cust_bill = $cust->cust_bill;
+    ok( $cust_bill[0]->charged == $recur, "first bill is $recur" )
+      or diag("first bill is ".$cust_bill[0]->charged);
+    ok( $cust_bill[1]->charged == $recur, "second bill is $recur" )
+      or diag("second bill is ".$cust_bill[1]->charged);
+
+  }
+}

commit 4321924ff5a68868a7ee622920fd4bbd53d6475f
Author: Mark Wells <mark at freeside.biz>
Date:   Thu Oct 13 15:35:26 2016 -0700

    per-customer option to force anniversary packages to prorate, #38191

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 2acbe3d..26fa08d 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1210,6 +1210,7 @@ sub tables_hashref {
         'accountcode_cdr', 'char', 'NULL', 1, '', '',
         'billday',   'int', 'NULL', '', '', '',
         'prorate_day',   'int', 'NULL', '', '', '',
+        'force_prorate_day', 'char', 'NULL', 1, '', '',
         'edit_subject', 'char', 'NULL', 1, '', '',
         'locale', 'varchar', 'NULL', 16, '', '', 
         'calling_list_exempt', 'char', 'NULL', 1, '', '',
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 90b93f2..7e1a25d 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -1853,6 +1853,7 @@ sub check {
     || $self->ut_floatn('credit_limit')
     || $self->ut_numbern('billday')
     || $self->ut_numbern('prorate_day')
+    || $self->ut_flag('force_prorate_day')
     || $self->ut_flag('edit_subject')
     || $self->ut_flag('calling_list_exempt')
     || $self->ut_flag('invoice_noemail')
diff --git a/FS/FS/part_pkg/flat.pm b/FS/FS/part_pkg/flat.pm
index e10094e..8c24882 100644
--- a/FS/FS/part_pkg/flat.pm
+++ b/FS/FS/part_pkg/flat.pm
@@ -177,6 +177,12 @@ sub calc_recur {
 sub cutoff_day {
   my $self = shift;
   my $cust_pkg = shift;
+  my $cust_main = $cust_pkg->cust_main;
+  # force it to act like a prorate package, is what this means
+  # because we made a distinction once between prorate and flat packages
+  if ( $cust_main->force_prorate_day  and $cust_main->prorate_day ) {
+     return ( $cust_main->prorate_day );
+  }
   if ( $self->option('sync_bill_date',1) ) {
     my $next_bill = $cust_pkg->cust_main->next_bill_date;
     if ( defined($next_bill) ) {
diff --git a/FS/FS/part_pkg/prorate_calendar.pm b/FS/FS/part_pkg/prorate_calendar.pm
index c50cae0..a8ed8f9 100644
--- a/FS/FS/part_pkg/prorate_calendar.pm
+++ b/FS/FS/part_pkg/prorate_calendar.pm
@@ -72,7 +72,11 @@ sub check {
 sub cutoff_day {
   my( $self, $cust_pkg ) = @_;
   my @periods = @{ $freq_cutoff_days{$self->freq} };
-  my @cutoffs = ($self->option('cutoff_day') || 1); # Jan 1 = 1
+  my $prorate_day = $cust_pkg->cust_main->prorate_day
+                    || $self->option('cutoff_day')
+                    || 1;
+
+  my @cutoffs = ($prorate_day);
   pop @periods; # we don't care about the last one
   foreach (@periods) {
     push @cutoffs, $cutoffs[-1] + $_;
diff --git a/FS/FS/part_pkg/recur_Common.pm b/FS/FS/part_pkg/recur_Common.pm
index 3573cf1..07355f6 100644
--- a/FS/FS/part_pkg/recur_Common.pm
+++ b/FS/FS/part_pkg/recur_Common.pm
@@ -41,13 +41,14 @@ sub cutoff_day {
   # prorate/subscription only; we don't support sync_bill_date here
   my( $self, $cust_pkg ) = @_;
   my $recur_method = $self->option('recur_method',1) || 'anniversary';
-  return () unless $recur_method eq 'prorate'
-                || $recur_method eq 'subscription';
+  my $cust_main = $cust_pkg->cust_main;
 
-  #false laziness w/prorate.pm::cutoff_day
-  my $prorate_day = $cust_pkg->cust_main->prorate_day;
-  $prorate_day ? ( $prorate_day )
-               : split(/\s*,\s*/, $self->option('cutoff_day', 1) || '1');
+  if ( $cust_main->force_prorate_day and $cust_main->prorate_day ) {
+     return ( $cust_main->prorate_day );
+  } elsif ($recur_method eq 'prorate' || $recur_method eq 'subscription') {
+
+    return split(/\s*,\s*/, $self->option('cutoff_day', 1) || '1');
+  }
 }
 
 sub calc_recur_Common {
diff --git a/FS/FS/part_pkg/subscription.pm b/FS/FS/part_pkg/subscription.pm
index 0dfe049..bf644d4 100644
--- a/FS/FS/part_pkg/subscription.pm
+++ b/FS/FS/part_pkg/subscription.pm
@@ -88,6 +88,11 @@ use FS::part_pkg::flat;
 sub calc_recur {
   my($self, $cust_pkg, $sdate, $details, $param ) = @_;
   my $cutoff_day = $self->option('cutoff_day', 1) || 1;
+  my $cust_main = $cust_pkg->cust_main;
+  if ( $cust_main->force_prorate_day  and $cust_main->prorate_day ) {
+     $cutoff_day = $cust_main->prorate_day;
+  }
+
   my $mnow = $$sdate;
   my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($mnow) )[0,1,2,3,4,5];
 
diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html
index 519f2f8..9836810 100644
--- a/httemplate/edit/cust_main/billing.html
+++ b/httemplate/edit/cust_main/billing.html
@@ -537,8 +537,14 @@
       <TD ALIGN="right" WIDTH="200"><% mt('Prorate day (1-28)') |h %> </TD>
       <TD>
         <INPUT TYPE="text" NAME="prorate_day" VALUE="<% $cust_main->prorate_day %>" SIZE=3 MAXLENGTH=2>
+      <& /elements/checkbox.html,
+        field       => 'force_prorate_day',
+        value       => 'Y',
+        curr_value  => $cust_main->force_prorate_day
+      &>
+      <label><% emt('Force all packages to this day') %></label>
       </TD>
-    </TR>
+   </TR>
 % } else {
     <INPUT TYPE="hidden" NAME="prorate_day" VALUE="<% $cust_main->prorate_day %>">
 % }

commit 23178237bcb103cb785829bf45cdd7a2710adabf
Author: Mark Wells <mark at freeside.biz>
Date:   Fri Oct 14 09:34:06 2016 -0700

    cursorize part_event::initialize so that it works on large data sets

diff --git a/FS/FS/part_event.pm b/FS/FS/part_event.pm
index d2ec8ab..217d09c 100644
--- a/FS/FS/part_event.pm
+++ b/FS/FS/part_event.pm
@@ -6,6 +6,7 @@ use vars qw( $DEBUG );
 use Carp qw(confess);
 use FS::Record qw( dbh qsearch qsearchs );
 use FS::Conf;
+use FS::Cursor;
 use FS::part_event_option;
 use FS::part_event_condition;
 use FS::cust_event;
@@ -266,10 +267,28 @@ but can be useful when configuring events.
 
 =cut
 
-sub targets {
+sub targets { # may want to cursor this also
   my $self = shift;
   my %opt = @_;
-  my $time = $opt{'time'} || time;
+  my $time = $opt{'time'} ||= time;
+  
+  my $query = $self->_target_query(%opt);
+  my @objects = qsearch($query);
+  my @tested_objects;
+  foreach my $object ( @objects ) {
+    my $cust_event = $self->new_cust_event($object, 'time' => $time);
+    next unless $cust_event->test_conditions;
+
+    $object->set('cust_event', $cust_event);
+    push @tested_objects, $object;
+  }
+  @tested_objects;
+}
+
+sub _target_query {
+  my $self = shift;
+  my %opt = @_;
+  my $time = $opt{'time'};
 
   my $eventpart = $self->eventpart;
   $eventpart =~ /^\d+$/ or die "bad eventpart $eventpart";
@@ -300,23 +319,15 @@ sub targets {
   # and don't enforce disabled because we want to be able to see targets 
   # for a disabled event
 
-  my @objects = qsearch({
+  {
       table     => $eventtable,
       hashref   => {},
       addl_from => $join,
       extra_sql => "WHERE $where",
-  });
-  my @tested_objects;
-  foreach my $object ( @objects ) {
-    my $cust_event = $self->new_cust_event($object, 'time' => $time);
-    next unless $cust_event->test_conditions;
-
-    $object->set('cust_event', $cust_event);
-    push @tested_objects, $object;
-  }
-  @tested_objects;
+  };
 }
 
+
 =item initialize PARAMS
 
 Identify all objects eligible for this event and create L<FS::cust_event>
@@ -338,26 +349,26 @@ sub initialize {
   my $self = shift;
   my $error;
 
-  my $oldAutoCommit = $FS::UID::AutoCommit;
-  local $FS::UID::AutoCommit = 0;
-  my $dbh = dbh;
+  my $time = time;
+
+  local $FS::UID::AutoCommit = 1;
+  my $cursor = FS::Cursor->new( $self->_target_query('time' => $time) );
+  while (my $object = $cursor->fetch) {
+
+    my $cust_event = $self->new_cust_event($object, 'time' => $time);
+    next unless $cust_event->test_conditions;
 
-  my @objects = $self->targets;
-  foreach my $object ( @objects ) {
-    my $cust_event = $object->get('cust_event');
     $cust_event->status('initial');
     $error = $cust_event->insert;
-    last if $error;
+    die $error if $error;
   }
-  if ( !$error and $self->disabled ) {
+
+  # on successful completion only, re-enable the event
+  if ( $self->disabled ) {
     $self->disabled('');
     $error = $self->replace;
+    die $error if $error;
   }
-  if ( $error ) {
-    $dbh->rollback;
-    return $error;
-  }
-  $dbh->commit if $oldAutoCommit;
   return;
 }
 

commit 815d4f25933a9693eb461cdf2311c2844c7a965d
Author: Mark Wells <mark at freeside.biz>
Date:   Thu Oct 13 20:31:59 2016 -0700

    script to manually initialize events, #72949

diff --git a/bin/initialize-event b/bin/initialize-event
new file mode 100755
index 0000000..f186e19
--- /dev/null
+++ b/bin/initialize-event
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+use FS::Misc::Getopt;
+use FS::part_event;
+use FS::cust_event;
+use FS::Record 'dbdef';
+use FS::Cursor;
+
+getopts('e:x');
+
+my $eventpart = $opt{e};
+my $part_event = FS::part_event->by_key($opt{e})
+  or die "usage: initialize-event -e <eventpart> <username>\n";
+
+
+my $eventtable = $part_event->eventtable;
+my $pkey = dbdef->table($eventtable)->primary_key;
+my $from = " LEFT JOIN (SELECT DISTINCT tablenum AS $pkey FROM cust_event
+                   WHERE eventpart = $eventpart) AS done USING ($pkey)",
+my $where = " WHERE done.$pkey IS NULL";
+
+my $count = FS::Record->scalar_sql("SELECT COUNT(*) FROM $eventtable $from $where");
+print "Event ".$part_event->event."\n".
+      "Will initialize on $count $eventtable records.\n";
+if (!$opt{x}) {
+  print "Run with -x to make changes.\n";
+  exit;
+}
+
+
+print "Disabling event.\n";
+$part_event->disabled('Y');
+my $error = $part_event->replace;
+die $error if $error;
+my $cursor = FS::Cursor->new({
+  table => $eventtable,
+  addl_from => $from,
+  extra_sql => $where,
+});
+my $user = $FS::CurrentUser::CurrentUser->username;
+my $statustext = "Manually by $user";
+while (my $record = $cursor->fetch) {
+  my $cust_event = FS::cust_event->new({
+    status      => 'initial',
+    eventpart   => $eventpart,
+    tablenum    => $record->get($pkey),
+    _date       => $^T,
+    statustext  => $statustext,
+  });
+  $error = $cust_event->insert;
+  if ($error) {
+    print "$eventtable #".$record->get($pkey).": $error\n" if $error;
+  } else {
+    $count--;
+  }
+}
+print "$count unprocessed records.";
+if ($count == 0) {
+  print "Re-enabling event.\n";
+  $part_event->disabled('');
+  $error = $part_event->replace;
+  die $error if $error;
+} else {
+  print "Event is still disabled.\n";
+}
+
+print "Finished.\n";
+

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

Summary of changes:
 FS/FS/Schema.pm                         |    1 +
 FS/FS/cust_main.pm                      |    1 +
 FS/FS/part_event.pm                     |   63 +++++++++++--------
 FS/FS/part_pkg/flat.pm                  |   35 ++++++++---
 FS/FS/part_pkg/prorate_calendar.pm      |    6 +-
 FS/FS/part_pkg/recur_Common.pm          |   13 ++--
 FS/FS/part_pkg/subscription.pm          |    5 ++
 FS/t/suite/05-prorate_sync_same_day.t   |   15 +++--
 FS/t/suite/10-prorate_sync_same_hour.t  |  102 +++++++++++++++++++++++++++++++
 FS/t/suite/11-prorate_sync_single_pkg.t |   89 +++++++++++++++++++++++++++
 bin/initialize-event                    |   68 +++++++++++++++++++++
 httemplate/edit/cust_main/billing.html  |    8 ++-
 12 files changed, 360 insertions(+), 46 deletions(-)
 create mode 100755 FS/t/suite/10-prorate_sync_same_hour.t
 create mode 100755 FS/t/suite/11-prorate_sync_single_pkg.t
 create mode 100755 bin/initialize-event




More information about the freeside-commits mailing list