use FS::cust_location; #
our ($DEBUG, $me) = (0, '[FS::cust_main::Packages]');
+our $skip_label_sort = 0;
=head1 NAME
Optional queue name for ticket additions
+=item invoice_details
+
+Optional arrayref of invoice detail strings to add (creates cust_pkg_detail detailtype 'I')
+
+=item package_comments
+
+Optional arrayref of package comment strings to add (creates cust_pkg_detail detailtype 'C')
+
=back
=cut
map { $_ => $cust_pkg->$_() }
qw( pkgbatch
start_date order_date expire adjourn contract_end
- refnum discountnum waive_setup
+ refnum setup_discountnum recur_discountnum waive_setup
)
});
$error = $self->order_pkg('cust_pkg' => $pkg,
}
}
+ # add details/comments
+ if ($opt->{'invoice_details'}) {
+ $error = $cust_pkg->set_cust_pkg_detail('I', @{$opt->{'invoice_details'}});
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "setting invoice details: $error";
+ }
+ if ($opt->{'package_comments'}) {
+ $error = $cust_pkg->set_cust_pkg_detail('C', @{$opt->{'package_comments'}});
+ }
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return "setting package comments: $error";
+ }
+
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
''; #no error
my $self = shift;
my $extra_qsearch = ref($_[0]) ? shift : { @_ };
- return $self->num_pkgs unless wantarray || keys %$extra_qsearch;
+ return $self->num_pkgs($extra_qsearch) unless wantarray;
my @cust_pkg = ();
if ( $self->{'_pkgnum'} && ! keys %$extra_qsearch ) {
@cust_pkg = $self->_cust_pkg($extra_qsearch);
}
+ local($skip_label_sort) = 1 if $extra_qsearch->{skip_label_sort};
map { $_ } sort sort_packages @cust_pkg;
+
}
=item cust_pkg
sub ncancelled_pkgs {
my $self = shift;
- my $extra_qsearch = ref($_[0]) ? shift : {};
+ my $extra_qsearch = ref($_[0]) ? shift : { @_ };
local($DEBUG) = $FS::cust_main::DEBUG if $FS::cust_main::DEBUG > $DEBUG;
- return $self->num_ncancelled_pkgs unless wantarray;
+ return $self->num_ncancelled_pkgs($extra_qsearch) unless wantarray;
my @cust_pkg = ();
if ( $self->{'_pkgnum'} ) {
$self->custnum. "\n"
if $DEBUG > 1;
- $extra_qsearch->{'extra_sql'} .= ' AND ( cancel IS NULL OR cancel = 0 ) ';
+ $extra_qsearch->{'extra_sql'} .=
+ ' AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 ) ';
@cust_pkg = $self->_cust_pkg($extra_qsearch);
}
+ local($skip_label_sort) = 1 if $extra_qsearch->{skip_label_sort};
sort sort_packages @cust_pkg;
}
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;
- return 0 if $a_num_cust_svc + $b_num_cust_svc > 20; #for perf, just give up
+ return 0 if $skip_label_sort
+ || $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);
grep { $_->susp } $self->ncancelled_pkgs;
}
-=item unflagged_suspended_pkgs
-
-Returns all unflagged suspended packages (see L<FS::cust_pkg>) for this
-customer (thouse packages without the `manual_flag' set).
-
-=cut
-
-sub unflagged_suspended_pkgs {
- my $self = shift;
- return $self->suspended_pkgs
- unless dbdef->table('cust_pkg')->column('manual_flag');
- grep { ! $_->manual_flag } $self->suspended_pkgs;
-}
-
=item unsuspended_pkgs
Returns all unsuspended (and uncancelled) packages (see L<FS::cust_pkg>) for
$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
sub next_bill_date {
my $self = shift;
- min( map $_->get('bill'), grep $_->get('bill'), $self->billing_pkgs );
+
+# super inefficient with lots of packages
+# min( map $_->get('bill'), grep $_->get('bill'), $self->billing_pkgs );
+
+ my $custnum = $self->custnum;
+
+ $self->scalar_sql("
+ SELECT MIN(bill) FROM cust_pkg
+ LEFT JOIN cust_pkg_option AS cust_suspend_bill_option
+ ON ( cust_pkg.pkgnum = cust_suspend_bill_option.pkgnum
+ AND cust_suspend_bill_option.optionname = 'suspend_bill' )
+ LEFT JOIN cust_pkg_option AS cust_no_suspend_bill_option
+ ON ( cust_pkg.pkgnum = cust_no_suspend_bill_option.pkgnum
+ AND cust_no_suspend_bill_option.optionname = 'no_suspend_bill' )
+ LEFT JOIN part_pkg USING (pkgpart)
+ LEFT JOIN part_pkg_option AS part_suspend_bill_option
+ ON ( part_pkg.pkgpart = part_suspend_bill_option.pkgpart
+ AND part_suspend_bill_option.optionname = 'suspend_bill' )
+ WHERE custnum = $custnum
+ AND bill IS NOT NULL AND bill != 0
+ AND ( cancel IS NULL OR cancel = 0 )
+ AND part_pkg.freq != '' AND part_pkg.freq != '0'
+ AND ( ( susp IS NULL OR susp = 0 )
+ OR COALESCE(cust_suspend_bill_option.optionvalue,'0') = '1'
+ OR ( COALESCE(part_suspend_bill_option.optionvalue,'0') = '1'
+ AND COALESCE(cust_no_suspend_bill_option.optionvalue,'0') = '0'
+ )
+ )
+ ");
+
}
=item num_cancelled_pkgs
=cut
sub num_cancelled_pkgs {
- shift->num_pkgs("cust_pkg.cancel IS NOT NULL AND cust_pkg.cancel != 0");
+ my $self = shift;
+ my $opt = shift || {};
+ $opt->{extra_sql} .= ' AND ' if $opt->{extra_sql};
+ $opt->{extra_sql} .= "cust_pkg.cancel IS NOT NULL AND cust_pkg.cancel != 0";
+ $self->num_pkgs($opt);
}
sub num_ncancelled_pkgs {
- shift->num_pkgs("( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )");
+ my $self = shift;
+ my $opt = shift || {};
+ $opt->{extra_sql} .= ' AND ' if $opt->{extra_sql};
+ $opt->{extra_sql} .= "( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )";
+ $self->num_pkgs($opt);
}
sub num_suspended_pkgs {
- shift->num_pkgs(" ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
- AND cust_pkg.susp IS NOT NULL AND cust_pkg.susp != 0 ");
+ my $self = shift;
+ my $opt = shift || {};
+ $opt->{extra_sql} .= ' AND ' if $opt->{extra_sql};
+ $opt->{extra_sql} .= " ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ AND cust_pkg.susp IS NOT NULL AND cust_pkg.susp != 0 ";
+ $self->num_pkgs($opt);
}
sub num_unsuspended_pkgs {
- shift->num_pkgs(" ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
- AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 ) ");
+ my $self = shift;
+ my $opt = shift || {};
+ $opt->{extra_sql} .= ' AND ' if $opt->{extra_sql};
+ $opt->{extra_sql} .= " ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 )
+ AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 )";
+ $self->num_pkgs($opt);
}
sub num_pkgs {
my( $self ) = shift;
- my $sql = scalar(@_) ? shift : '';
+ my $addl_from = '';
+ my $sql = '';
+ if ( @_ ) {
+ if ( ref($_[0]) ) {
+ my $opt = shift;
+ $sql = $opt->{extra_sql} if exists($opt->{extra_sql});
+ $addl_from = $opt->{addl_from} if exists($opt->{addl_from});
+ } else {
+ $sql = shift;
+ }
+ }
$sql = "AND $sql" if $sql && $sql !~ /^\s*$/ && $sql !~ /^\s*AND/i;
my $sth = dbh->prepare(
- "SELECT COUNT(*) FROM cust_pkg WHERE custnum = ? $sql"
+ "SELECT COUNT(*) FROM cust_pkg $addl_from WHERE cust_pkg.custnum = ? $sql"
) or die dbh->errstr;
$sth->execute($self->custnum) or die $sth->errstr;
$sth->fetchrow_arrayref->[0];