return '' if $date; #no errors
my @invoicing_list = grep { $_ !~ /^(POST|FAX)$/ } $self->cust_main->invoicing_list;
- if ( !$options{'quiet'} && $conf->exists('emailcancel') && @invoicing_list ) {
+ if ( !$options{'quiet'} &&
+ $conf->exists('emailcancel', $self->cust_main->agentnum) &&
+ @invoicing_list ) {
my $msgnum = $conf->config('cancel_msgnum', $self->cust_main->agentnum);
my $error = '';
if ( $msgnum ) {
=over 4
-=item locaitonnum
+=item locationnum
New locationnum, to change the location for this package.
New refnum (see L<FS::part_referral>).
+=item keep_dates
+
+Set to true to transfer billing dates (start_date, setup, last_bill, bill,
+susp, adjourn, cancel, expire, and contract_end) to the new package.
+
=back
-At least one option must be specified (otherwise, what's the point?)
+At least one of locationnum, cust_location, pkgpart, refnum must be specified
+(otherwise, what's the point?)
Returns either the new FS::cust_pkg object or a scalar error.
$opt->{'locationnum'} = $opt->{'cust_location'}->locationnum;
}
+ if ( $opt->{'keep_dates'} ) {
+ foreach my $date ( qw(setup bill last_bill susp adjourn cancel expire
+ start_date contract_end ) ) {
+ $hash{$date} = $self->getfield($date);
+ }
+ }
+
# Create the new package.
my $cust_pkg = new FS::cust_pkg {
custnum => $self->custnum,
? ()
: ( 'null' => 1 )
)
- if $part_pkg->can('reset_usage') && ! $part_pkg->option('usage_rollover');
+ if $part_pkg->can('reset_usage') && ! $part_pkg->option('usage_rollover',1);
if ($error) {
$dbh->rollback if $oldAutoCommit;
}
+use Data::Dumper;
+use Storable 'thaw';
+use MIME::Base64;
+sub process_bulk_cust_pkg {
+ my $job = shift;
+ my $param = thaw(decode_base64(shift));
+ warn Dumper($param) if $DEBUG;
+
+ my $old_part_pkg = qsearchs('part_pkg',
+ { pkgpart => $param->{'old_pkgpart'} });
+ my $new_part_pkg = qsearchs('part_pkg',
+ { pkgpart => $param->{'new_pkgpart'} });
+ die "Must select a new package type\n" unless $new_part_pkg;
+ #my $keep_dates = $param->{'keep_dates'} || 0;
+ my $keep_dates = 1; # there is no good reason to turn this off
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my @cust_pkgs = qsearch('cust_pkg', { 'pkgpart' => $param->{'old_pkgpart'} } );
+
+ my $i = 0;
+ foreach my $old_cust_pkg ( @cust_pkgs ) {
+ $i++;
+ $job->update_statustext(int(100*$i/(scalar @cust_pkgs)));
+ if ( $old_cust_pkg->getfield('cancel') ) {
+ warn '[process_bulk_cust_pkg ] skipping canceled pkgnum '.
+ $old_cust_pkg->pkgnum."\n"
+ if $DEBUG;
+ next;
+ }
+ warn '[process_bulk_cust_pkg] changing pkgnum '.$old_cust_pkg->pkgnum."\n"
+ if $DEBUG;
+ my $error = $old_cust_pkg->change(
+ 'pkgpart' => $param->{'new_pkgpart'},
+ 'keep_dates' => $keep_dates
+ );
+ if ( !ref($error) ) { # change returns the cust_pkg on success
+ $dbh->rollback;
+ die "Error changing pkgnum ".$old_cust_pkg->pkgnum.": '$error'\n";
+ }
+ }
+ $dbh->commit if $oldAutoCommit;
+ return;
+}
+
=item last_bill
Returns the last bill date, or if there is no last bill date, the setup date.
grep { $_->overlimit } $self->cust_svc(@_);
}
-=item h_cust_svc END_TIMESTAMP [ START_TIMESTAMP ]
+=item h_cust_svc END_TIMESTAMP [ START_TIMESTAMP ] [ MODE ]
Returns historical services for this package created before END TIMESTAMP and
(optionally) not cancelled before START_TIMESTAMP, as FS::h_cust_svc objects
-(see L<FS::h_cust_svc>).
+(see L<FS::h_cust_svc>). If MODE is 'I' (for 'invoice'), services with the
+I<pkg_svc.hidden> flag will be omitted.
=cut
sub h_cust_svc {
my $self = shift;
-
- $self->_sort_cust_svc(
+ my ($end, $start, $mode) = @_;
+ my @cust_svc = $self->_sort_cust_svc(
[ qsearch( 'h_cust_svc',
- { 'pkgnum' => $self->pkgnum, },
- FS::h_cust_svc->sql_h_search(@_),
- )
- ]
+ { 'pkgnum' => $self->pkgnum, },
+ FS::h_cust_svc->sql_h_search(@_),
+ ) ]
);
+ if ( $mode eq 'I' ) {
+ my %hidden_svcpart = map { $_->svcpart => $_->hidden } $self->part_svc;
+ return grep { !$hidden_svcpart{$_->svcpart} } @cust_svc;
+ }
+ else {
+ return @cust_svc;
+ }
}
sub _sort_cust_svc {
max( 0, $pkg_svc->quantity - $num_cust_svc );
$part_svc->{'Hash'}{'cust_pkg_svc'} =
$num_cust_svc ? [ $self->cust_svc($part_svc->svcpart) ] : [];
+ $part_svc->{'Hash'}{'hidden'} = $pkg_svc->hidden;
$part_svc;
} $self->part_pkg->pkg_svc;
map { [ $_->label ] } $self->cust_svc;
}
-=item h_labels END_TIMESTAMP [ START_TIMESTAMP ]
+=item h_labels END_TIMESTAMP [ START_TIMESTAMP ] [ MODE ]
Like the labels method, but returns historical information on services that
were active as of END_TIMESTAMP and (optionally) not cancelled before
-START_TIMESTAMP.
+START_TIMESTAMP. If MODE is 'I' (for 'invoice'), services with the
+I<pkg_svc.hidden> flag will be omitted.
Returns a list of lists, calling the label method for all (historical) services
(see L<FS::h_cust_svc>) of this billing item.
sub _upgrade_data { # class method
my ($class, %opts) = @_;
$class->_upgrade_otaker(%opts);
+ my @statements = (
+ # RT#10139, bug resulting in contract_end being set when it shouldn't
+ 'UPDATE cust_pkg SET contract_end = NULL WHERE contract_end = -1',
+ # RT#10830, bad calculation of prorate date near end of year
+ # the date range for bill is December 2009, and we move it forward
+ # one year if it's before the previous bill date (which it should
+ # never be)
+ 'UPDATE cust_pkg SET bill = bill + (365*24*60*60) WHERE bill < last_bill
+ AND bill > 1259654400 AND bill < 1262332800 AND (SELECT plan FROM part_pkg
+ WHERE part_pkg.pkgpart = cust_pkg.pkgpart) = \'prorate\'',
+ );
+ foreach my $sql (@statements) {
+ my $sth = dbh->prepare($sql);
+ $sth->execute or die $sth->errstr;
+ }
}
=back