Optional link to package location (see L<FS::location>)
+=item order_date
+
+date package was ordered (also remains same on changes)
+
=item start_date
date
}
}
+ $self->order_date(time);
+
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
=item nobill - can be set true to skip billing if it might otherwise be done.
+=item unused_credit - can be set to 1 to credit the remaining time, or 0 to
+not credit it. This must be set (by change()) when changing the package
+to a different pkgpart or location, and probably shouldn't be in any other
+case. If it's not set, the 'unused_credit_cancel' part_pkg option will
+be used.
+
=back
If there is an error, returns the error, otherwise returns false.
if $error;
}
-
my $cancel_time = $options{'time'} || time;
if ( $options{'reason'} ) {
}
my %svc;
- unless ( $date ) {
+ if ( $date ) {
+# copied from below
foreach my $cust_svc (
#schwartz
map { $_->[0] }
map { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; }
qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } )
) {
+ my $error = $cust_svc->cancel( ('date' => $date) );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "Error expiring cust_svc: $error";
+ }
+ }
+ } else { #!date
+ foreach my $cust_svc (
+ #schwartz
+ map { $_->[0] }
+ sort { $a->[1] <=> $b->[1] }
+ map { [ $_, $_->svc_x->table_info->{'cancel_weight'} ]; }
+ qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } )
+ ) {
my $error = $cust_svc->cancel;
if ( $error ) {
return "Error cancelling cust_svc: $error";
}
}
+ } #if $date
- # Add a credit for remaining service
- my $remaining_value = $self->calc_remain(time=>$cancel_time);
- if ( $remaining_value > 0 && !$options{'no_credit'} ) {
+ # Add a credit for remaining service
+ my $last_bill = $self->getfield('last_bill') || 0;
+ my $next_bill = $self->getfield('bill') || 0;
+ my $do_credit;
+ if ( exists($options{'unused_credit'}) ) {
+ $do_credit = $options{'unused_credit'};
+ }
+ else {
+ $do_credit = $self->part_pkg->option('unused_credit_cancel', 1);
+ }
+ if ( $do_credit
+ and $last_bill > 0 # the package has been billed
+ and $next_bill > 0 # the package has a next bill date
+ and $next_bill >= $cancel_time # which is in the future
+ ) {
+ my $remaining_value = $self->calc_remain('time' => $cancel_time);
+ if ( $remaining_value > 0 ) {
+ # && !$options{'no_credit'} ) {
+ # Undocumented, unused option.
+ # part_pkg configuration should decide this anyway.
my $error = $self->cust_main->credit(
$remaining_value,
'Credit for unused time on '. $self->part_pkg->pkg,
return "Error crediting customer \$$remaining_value for unused time on".
$self->part_pkg->pkg. ": $error";
}
- }
- }
+ } #if $remaining_value
+ } #if $do_credit
my %hash = $self->hash;
$date ? ($hash{'expire'} = $date) : ($hash{'cancel'} = $cancel_time);
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 ) {
$opt->{'locationnum'} = $opt->{'cust_location'}->locationnum;
}
+ my $unused_credit = 0;
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);
}
}
+ # Special case. If the pkgpart is changing, and the customer is
+ # going to be credited for remaining time, don't keep setup, bill,
+ # or last_bill dates, and DO pass the flag to cancel() to credit
+ # the customer.
+ if ( $opt->{'pkgpart'}
+ and $opt->{'pkgpart'} != $self->pkgpart
+ and $self->part_pkg->option('unused_credit_change', 1) ) {
+ $unused_credit = 1;
+ $hash{$_} = '' foreach qw(setup bill last_bill);
+ }
# Create the new package.
my $cust_pkg = new FS::cust_pkg {
}
}
- #Good to go, cancel old package.
- $error = $self->cancel( quiet=>1 );
+ #Good to go, cancel old package. Notify 'cancel' of whether to credit
+ #remaining time.
+ $error = $self->cancel( quiet=>1, unused_credit => $unused_credit );
if ($error) {
$dbh->rollback if $oldAutoCommit;
return $error;
my $part_svc = $_->part_svc;
$part_svc->{'Hash'}{'num_avail'} = #evil encapsulation-breaking
$_->quantity - $self->num_cust_svc($_->svcpart);
+
+ # more evil encapsulation breakage
+ if($part_svc->{'Hash'}{'num_avail'} > 0) {
+ my @exports = $part_svc->part_export_did;
+ $part_svc->{'Hash'}{'can_get_dids'} = scalar(@exports);
+ }
+
$part_svc;
}
$self->part_pkg->pkg_svc;
sub _upgrade_data { # class method
my ($class, %opts) = @_;
$class->_upgrade_otaker(%opts);
- my $sql =('UPDATE cust_pkg SET contract_end = NULL WHERE contract_end = -1');
- my $sth = dbh->prepare($sql);
- $sth->execute or die $sth->errstr;
+ 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