use FS::Record qw( qsearch qsearchs );
use FS::cust_pkg;
use FS::cust_svc;
+use FS::contact; # for attach_pkgs
+use FS::cust_location; #
$DEBUG = 0;
$me = '[FS::cust_main::Packages]';
specific job completes). This can be used to defer provisioning until some
action completes (such as running the customer's credit card successfully).
+=item noexport
+
+This option is option is deprecated but still works for now (use
+I<depend_jobnum> instead for new code). If I<noexport> is set true, no
+provisioning jobs (exports) are scheduled. (You can schedule them later with
+the B<reexport> method for each cust_pkg object. Using the B<reexport> method
+on the cust_main object is not recommended, as existing services will also be
+reexported.)
+
=item ticket_subject
Optional subject for a ticket created and attached to this customer
join(', ', map { "$_: $opt->{$_}" } keys %$opt ). "\n"
if $DEBUG;
+ local $FS::svc_Common::noexport_hack = 1 if $opt->{'noexport'};
+
my $cust_pkg = $opt->{'cust_pkg'};
my $svcs = $opt->{'svcs'} || [];
} elsif ( $opt->{'cust_location'} ) {
- if ( ! $opt->{'cust_location'}->locationnum ) {
- # not inserted yet
- my $error = $opt->{'cust_location'}->insert;
- if ( $error ) {
- $dbh->rollback if $oldAutoCommit;
- return "inserting cust_location (transaction rolled back): $error";
- }
+ my $error = $opt->{'cust_location'}->find_or_insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "inserting cust_location (transaction rolled back): $error";
}
$cust_pkg->locationnum($opt->{'cust_location'}->locationnum);
'pkglinknum' => $link->pkglinknum,
'custnum' => $self->custnum,
'main_pkgnum' => $cust_pkg->pkgnum,
- 'locationnum' => $cust_pkg->locationnum,
# try to prevent as many surprises as possible
- 'pkgbatch' => $cust_pkg->pkgbatch,
- 'start_date' => $cust_pkg->start_date,
- 'order_date' => $cust_pkg->order_date,
- 'expire' => $cust_pkg->expire,
- 'adjourn' => $cust_pkg->adjourn,
- 'contract_end' => $cust_pkg->contract_end,
- 'refnum' => $cust_pkg->refnum,
- 'discountnum' => $cust_pkg->discountnum,
- 'waive_setup' => $cust_pkg->waive_setup,
'allow_pkgpart' => $opt->{'allow_pkgpart'},
+ map { $_ => $cust_pkg->$_() }
+ qw( pkgbatch
+ start_date order_date expire adjourn contract_end
+ refnum discountnum waive_setup
+ )
});
- $error = $self->order_pkg('cust_pkg' => $pkg);
+ $error = $self->order_pkg('cust_pkg' => $pkg,
+ 'locationnum' => $cust_pkg->locationnum);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return "inserting supplemental package: $error";
This can be used to defer provisioning until some action completes (such
as running the customer's credit card successfully).
-The I<noexport> option is deprecated. If I<noexport> is set true, no
+The I<noexport> option is deprecated but still works for now (use
+I<depend_jobnum> instead for new code). If I<noexport> is set true, no
provisioning jobs (exports) are scheduled. (You can schedule them later with
the B<reexport> method for each cust_pkg object. Using the B<reexport> method
on the cust_main object is not recommended, as existing services will also be
#end of false laziness
+ #pull in contact
+
+ my %contact_hash = ( 'first' => $self->first,
+ 'last' => $self->get('last'),
+ 'custnum' => $new_custnum,
+ 'disabled' => '',
+ );
+
+ my $contact = qsearchs( 'contact', \%contact_hash)
+ || new FS::contact \%contact_hash;
+ unless ( $contact->contactnum ) {
+ my $error = $contact->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
foreach my $cust_pkg ( $self->ncancelled_pkgs ) {
+ my $cust_location = $cust_pkg->cust_location || $self->ship_location;
+ my %loc_hash = $cust_location->hash;
+ $loc_hash{'locationnum'} = '';
+ $loc_hash{'custnum'} = $new_custnum;
+ $loc_hash{'disabled'} = '';
+ my $new_cust_location = qsearchs( 'cust_location', \%loc_hash)
+ || new FS::cust_location \%loc_hash;
+
my $pkg_or_error = $cust_pkg->change( {
- 'keep_dates' => 1,
- 'cust_main' => $new_cust_main,
+ 'keep_dates' => 1,
+ 'cust_main' => $new_cust_main,
+ 'contactnum' => $contact->contactnum,
+ 'cust_location' => $new_cust_location,
} );
my $error = ref($pkg_or_error) ? '' : $pkg_or_error;
return 0 if !$a_num_cust_svc && !$b_num_cust_svc;
return -1 if $a_num_cust_svc && !$b_num_cust_svc;
return 1 if !$a_num_cust_svc && $b_num_cust_svc;
- my @a_cust_svc = $a->cust_svc;
- my @b_cust_svc = $b->cust_svc;
+ return 0 if $a_num_cust_svc + $b_num_cust_svc > 20; #for perf, just give up
+ my @a_cust_svc = $a->cust_svc_unsorted;
+ my @b_cust_svc = $b->cust_svc_unsorted;
return 0 if !scalar(@a_cust_svc) && !scalar(@b_cust_svc);
return -1 if scalar(@a_cust_svc) && !scalar(@b_cust_svc);
return 1 if !scalar(@a_cust_svc) && scalar(@b_cust_svc);
$self->unsuspended_pkgs;
}
+=item ncancelled_active_pkgs
+
+Returns all non-cancelled packages (see L<FS::cust_pkg>) for this customer that
+are active (recurring).
+
+=cut
+
+sub ncancelled_active_pkgs {
+ my $self = shift;
+ grep { my $part_pkg = $_->part_pkg;
+ $part_pkg->freq ne '' && $part_pkg->freq ne '0';
+ }
+ $self->ncancelled_pkgs;
+}
+
=item billing_pkgs
Returns active packages, and also any suspended packages which are set to