unless ( ! $self->getfield('susp') ) {
my %hash = $self->hash;
+ my $inactive = time - $hash{'susp'};
$hash{'susp'} = '';
+ $hash{'bill'} = ( $hash{'bill'} || $hash{'setup'} ) + $inactive
+ if $inactive > 0 && ( $hash{'bill'} || $hash{'setup'} );
my $new = new FS::cust_pkg ( \%hash );
$error = $new->replace($self);
if ( $error ) {
: qsearchs( 'part_pkg', { 'pkgpart' => $self->pkgpart } );
}
-=item cust_svc
+=item calc_setup
+
+Calls the I<calc_setup> of the FS::part_pkg object associated with this billing
+item.
+
+=cut
+
+sub calc_setup {
+ my $self = shift;
+ $self->part_pkg->calc_setup($self, @_);
+}
+
+=item calc_recur
+
+Calls the I<calc_recur> of the FS::part_pkg object associated with this billing
+item.
+
+=cut
+
+sub calc_recur {
+ my $self = shift;
+ $self->part_pkg->calc_recur($self, @_);
+}
+
+=item cust_svc [ SVCPART ]
Returns the services for this package, as FS::cust_svc objects (see
-L<FS::cust_svc>)
+L<FS::cust_svc>). If a svcpart is specified, return only the matching
+services.
=cut
sub cust_svc {
my $self = shift;
- if ( $self->{'_svcnum'} ) {
- values %{ $self->{'_svcnum'}->cache };
- } else {
- qsearch ( 'cust_svc', { 'pkgnum' => $self->pkgnum } );
+
+ if ( @_ ) {
+ return qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum,
+ 'svcpart' => shift, } );
}
+
+ #if ( $self->{'_svcnum'} ) {
+ # values %{ $self->{'_svcnum'}->cache };
+ #} else {
+ map { $_->[0] }
+ sort { $b->[1] cmp $a->[1] or $a->[2] <=> $b->[2] }
+ map {
+ my $pkg_svc = qsearchs( 'pkg_svc', { 'pkgpart' => $self->pkgpart,
+ 'svcpart' => $_->svcpart } );
+ [ $_,
+ $pkg_svc ? $pkg_svc->primary_svc : '',
+ $pkg_svc ? $pkg_svc->quantity : 0,
+ ];
+ }
+ qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } );
+ #}
+
+}
+
+=item num_cust_svc [ SVCPART ]
+
+Returns the number of provisioned services for this package. If a svcpart is
+specified, counts only the matching services.
+
+=cut
+
+sub num_cust_svc {
+ my $self = shift;
+ my $sql = 'SELECT COUNT(*) FROM cust_svc WHERE pkgnum = ?';
+ $sql .= ' AND svcpart = ?' if @_;
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute($self->pkgnum, @_) or die $sth->errstr;
+ $sth->fetchrow_arrayref->[0];
+}
+
+=item available_part_svc
+
+Returns a list FS::part_svc objects representing services included in this
+package but not yet provisioned. Each FS::part_svc object also has an extra
+field, I<num_avail>, which specifies the number of available services.
+
+=cut
+
+sub available_part_svc {
+ my $self = shift;
+ grep { $_->num_avail > 0 }
+ map {
+ my $part_svc = $_->part_svc;
+ $part_svc->{'Hash'}{'num_avail'} = #evil encapsulation-breaking
+ $_->quantity - $self->num_cust_svc($_->svcpart);
+ $part_svc;
+ }
+ $self->part_pkg->pkg_svc;
}
=item labels
my $remaining = 0;
my $dest;
my %target;
- my $pkg_svc;
if (ref ($dest_pkgnum) eq 'FS::cust_pkg') {
$dest = $dest_pkgnum;
return ('Package does not exist: '.$dest_pkgnum) unless $dest;
- foreach $pkg_svc (qsearch('pkg_svc', { pkgpart => $dest->pkgpart })) {
+ foreach my $pkg_svc ( $dest->part_pkg->pkg_svc ) {
$target{$pkg_svc->svcpart} = $pkg_svc->quantity;
}
- my $cust_svc;
-
- foreach $cust_svc ($dest->cust_svc) {
+ foreach my $cust_svc ($dest->cust_svc) {
$target{$cust_svc->svcpart}--;
}
next if exists $svcpart2svcparts{$svcpart};
my $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } );
$svcpart2svcparts{$svcpart} = [
+ map { $_->[0] }
+ sort { $b->[1] cmp $a->[1] or $a->[2] <=> $b->[2] }
+ map {
+ my $pkg_svc = qsearchs( 'pkg_svc', { 'pkgpart' => $dest->pkgpart,
+ 'svcpart' => $_ } );
+ [ $_,
+ $pkg_svc ? $pkg_svc->primary_svc : '',
+ $pkg_svc ? $pkg_svc->quantity : 0,
+ ];
+ }
+
grep { $_ != $svcpart }
- map { $_->svcpart }
- qsearch('part_svc', { 'svcdb' => $part_svc->svcdb } )
+ map { $_->svcpart }
+ qsearch('part_svc', { 'svcdb' => $part_svc->svcdb } )
];
warn "alternates for svcpart $svcpart: ".
join(', ', @{$svcpart2svcparts{$svcpart}}). "\n"
}
}
- foreach $cust_svc ($self->cust_svc) {
+ foreach my $cust_svc ($self->cust_svc) {
if($target{$cust_svc->svcpart} > 0) {
$target{$cust_svc->svcpart}--;
my $new = new FS::cust_svc {
} @{$svcpart2svcparts{$cust_svc->svcpart}};
if ( @alternate ) {
warn "alternate(s) found\n" if $DEBUG;
- my $change_svcpart = $alternate[0]; #arbitrary.
+ my $change_svcpart = $alternate[0];
$target{$change_svcpart}--;
my $new = new FS::cust_svc {
svcnum => $cust_svc->svcnum,