use FS::agent_type;
use FS::type_pkgs;
use FS::part_pkg_option;
+use FS::part_pkg_fcc_option;
use FS::pkg_class;
use FS::agent;
use FS::part_pkg_msgcat;
=item delay_start - Number of days to delay package start, by default
+=item start_on_hold - 'Y' to suspend this package immediately when it is
+ordered. The package will not start billing or have a setup fee charged
+until it is manually unsuspended.
+
=back
=head1 METHODS
If I<pkg_svc> is set to a hashref with svcparts as keys and quantities as
values, appropriate FS::pkg_svc records will be inserted. I<hidden_svc> can
be set to a hashref of svcparts and flag values ('Y' or '') to set the
-'hidden' field in these records.
+'hidden' field in these records, and I<provision_hold> can be set similarly
+for the 'provision_hold' field in these records.
If I<primary_svc> is set to the svcpart of the primary service, the appropriate
FS::pkg_svc record will be updated.
warn " inserting pkg_svc records" if $DEBUG;
my $pkg_svc = $options{'pkg_svc'} || {};
my $hidden_svc = $options{'hidden_svc'} || {};
+ my $provision_hold = $options{'provision_hold'} || {};
foreach my $part_svc ( qsearch('part_svc', {} ) ) {
my $quantity = $pkg_svc->{$part_svc->svcpart} || 0;
my $primary_svc =
'quantity' => $quantity,
'primary_svc' => $primary_svc,
'hidden' => $hidden_svc->{$part_svc->svcpart},
+ 'provision_hold' => $provision_hold->{$part_svc->svcpart},
} );
my $error = $pkg_svc->insert;
if ( $error ) {
}
}
+ if ( $options{fcc_options} ) {
+ warn " updating fcc options " if $DEBUG;
+ $self->set_fcc_options( $options{fcc_options} );
+ }
+
warn " committing transaction" if $DEBUG and $oldAutoCommit;
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
Replaces OLD_RECORD with this one in the database. If there is an error,
returns the error, otherwise returns false.
-Currently available options are: I<pkg_svc>, I<hidden_svc>, I<primary_svc>
-and I<options>
+Currently available options are: I<pkg_svc>, I<hidden_svc>, I<primary_svc>,
+I<provision_hold> and I<options>
If I<pkg_svc> is set to a hashref with svcparts as keys and quantities as
values, the appropriate FS::pkg_svc records will be replaced. I<hidden_svc>
can be set to a hashref of svcparts and flag values ('Y' or '') to set the
-'hidden' field in these records.
+'hidden' field in these records. I<provision_hold> can be set
+to a hashref of svcparts and flag values ('Y' or '') to set the field
+in those records.
If I<primary_svc> is set to the svcpart of the primary service, the appropriate
FS::pkg_svc record will be updated.
warn " replacing pkg_svc records" if $DEBUG;
my $pkg_svc = $options->{'pkg_svc'};
my $hidden_svc = $options->{'hidden_svc'} || {};
+ my $provision_hold = $options->{'provision_hold'} || {};
if ( $pkg_svc ) { # if it wasn't passed, don't change existing pkg_svcs
foreach my $part_svc ( qsearch('part_svc', {} ) ) {
my $quantity = $pkg_svc->{$part_svc->svcpart} || 0;
my $hidden = $hidden_svc->{$part_svc->svcpart} || '';
+ my $provision_hold = $provision_hold->{$part_svc->svcpart} || '';
my $primary_svc =
( defined($options->{'primary_svc'}) && $options->{'primary_svc'}
&& $options->{'primary_svc'} == $part_svc->svcpart
my $old_quantity = 0;
my $old_primary_svc = '';
my $old_hidden = '';
+ my $old_provision_hold = '';
if ( $old_pkg_svc ) {
$old_quantity = $old_pkg_svc->quantity;
$old_primary_svc = $old_pkg_svc->primary_svc
if $old_pkg_svc->dbdef_table->column('primary_svc'); # is this needed?
$old_hidden = $old_pkg_svc->hidden;
+ $old_provision_hold = $old_pkg_svc->provision_hold;
}
next unless $old_quantity != $quantity ||
$old_primary_svc ne $primary_svc ||
- $old_hidden ne $hidden;
+ $old_hidden ne $hidden ||
+ $old_provision_hold ne $provision_hold;
my $new_pkg_svc = new FS::pkg_svc( {
'pkgsvcnum' => ( $old_pkg_svc ? $old_pkg_svc->pkgsvcnum : '' ),
'quantity' => $quantity,
'primary_svc' => $primary_svc,
'hidden' => $hidden,
+ 'provision_hold' => $provision_hold,
} );
my $error = $old_pkg_svc
? $new_pkg_svc->replace($old_pkg_svc)
}
}
+ if ( $options->{fcc_options} ) {
+ warn " updating fcc options " if $DEBUG;
+ $new->set_fcc_options( $options->{fcc_options} );
+ }
+
warn " committing transaction" if $DEBUG and $oldAutoCommit;
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
|| $self->ut_textn('comment')
|| $self->ut_textn('promo_code')
|| $self->ut_alphan('plan')
- || $self->ut_enum('setuptax', [ '', 'Y' ] )
- || $self->ut_enum('recurtax', [ '', 'Y' ] )
+ || $self->ut_flag('setuptax')
+ || $self->ut_flag('recurtax')
|| $self->ut_textn('taxclass')
- || $self->ut_enum('disabled', [ '', 'Y' ] )
- || $self->ut_enum('custom', [ '', 'Y' ] )
- || $self->ut_enum('no_auto', [ '', 'Y' ])
- || $self->ut_enum('recur_show_zero', [ '', 'Y' ])
- || $self->ut_enum('setup_show_zero', [ '', 'Y' ])
+ || $self->ut_flag('disabled')
+ || $self->ut_flag('custom')
+ || $self->ut_flag('no_auto')
+ || $self->ut_flag('recur_show_zero')
+ || $self->ut_flag('setup_show_zero')
+ || $self->ut_flag('start_on_hold')
#|| $self->ut_moneyn('setup_cost')
#|| $self->ut_moneyn('recur_cost')
|| $self->ut_floatn('setup_cost')
join("\n", @error);
}
+=item set_fcc_options HASHREF
+
+Sets the FCC options on this package definition to the values specified
+in HASHREF.
+
+=cut
+
+sub set_fcc_options {
+ my $self = shift;
+ my $pkgpart = $self->pkgpart;
+ my $options;
+ if (ref $_[0]) {
+ $options = shift;
+ } else {
+ $options = { @_ };
+ }
+
+ my %existing_num = map { $_->fccoptionname => $_->num }
+ qsearch('part_pkg_fcc_option', { pkgpart => $pkgpart });
+
+ local $FS::Record::nowarn_identical = 1;
+ # set up params for process_o2m
+ my $i = 0;
+ my $params = {};
+ foreach my $name (keys %$options ) {
+ $params->{ "num$i" } = $existing_num{$name} || '';
+ $params->{ "num$i".'_fccoptionname' } = $name;
+ $params->{ "num$i".'_optionvalue' } = $options->{$name};
+ $i++;
+ }
+
+ $self->process_o2m(
+ table => 'part_pkg_fcc_option',
+ fields => [qw( fccoptionname optionvalue )],
+ params => $params,
+ );
+}
+
=item pkg_locale LOCALE
Returns a customer-viewable string representing this package for the given
sub can_discount { 0; }
# whether the plan allows changing the start date
-sub can_start_date { 1; }
+sub can_start_date {
+ my $self = shift;
+ $self->start_on_hold ? 0 : 1;
+}
# the delay start date if present
sub delay_start_date {
sub option {
my( $self, $opt, $ornull ) = @_;
+
+ #cache: was pulled up in the original part_pkg query
+ if ( $opt =~ /^(setup|recur)_fee$/ && defined($self->hashref->{"_$opt"}) ) {
+ return $self->hashref->{"_$opt"};
+ }
+
cluck "$self -> option: searching for $opt"
if $DEBUG;
my $part_pkg_option =
optionname => $opt,
} );
return $part_pkg_option->optionvalue if $part_pkg_option;
+
my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }
split("\n", $self->get('plandata') );
return $plandata{$opt} if exists $plandata{$opt};
cluck "WARNING: (pkgpart ". $self->pkgpart. ") Package def option $opt ".
"not found in options or plandata!\n"
unless $ornull;
+
'';
}
+=item fcc_option OPTIONNAME
+
+Returns the FCC 477 report option value for the given name, or the empty
+string.
+
+=cut
+
+sub fcc_option {
+ my ($self, $name) = @_;
+ my $part_pkg_fcc_option =
+ qsearchs('part_pkg_fcc_option', {
+ pkgpart => $self->pkgpart,
+ fccoptionname => $name,
+ });
+ $part_pkg_fcc_option ? $part_pkg_fcc_option->optionvalue : '';
+}
+
+=item fcc_options
+
+Returns all FCC 477 report options for this package, as a hash-like list.
+
+=cut
+
+sub fcc_options {
+ my $self = shift;
+ map { $_->fccoptionname => $_->optionvalue }
+ qsearch('part_pkg_fcc_option', { pkgpart => $self->pkgpart });
+}
+
=item bill_part_pkg_link
Returns the associated part_pkg_link records (see L<FS::part_pkg_link>).
sub tax_rates {
my $self = shift;
my ($vendor, $geocode, $class) = @_;
+ # if this part_pkg is overridden into a specific taxclass, get that class
my @taxclassnums = map { $_->taxclassnum }
$self->part_pkg_taxoverride($class);
+ # otherwise, get its tax product category
if (!@taxclassnums) {
my $part_pkg_taxproduct = $self->taxproduct($class);
+ # If this isn't defined, then the class has no taxproduct designation,
+ # so return no tax rates.
+ return () if !$part_pkg_taxproduct;
+
+ # convert the taxproduct to the tax classes that might apply to it in
+ # $geocode
@taxclassnums = map { $_->taxclassnum }
grep { $_->taxable eq 'Y' } # why do we need this?
$part_pkg_taxproduct->part_pkg_taxrate($geocode);
}
return unless @taxclassnums;
+ # then look up the actual tax_rate entries
warn "Found taxclassnum values of ". join(',', @taxclassnums) ."\n"
if $DEBUG;
my $extra_sql = "AND taxclassnum IN (". join(',', @taxclassnums) . ")";
my @taxes = qsearch({ 'table' => 'tax_rate',
'hashref' => { 'geocode' => $geocode,
- 'data_vendor' => $vendor },
+ 'data_vendor' => $vendor,
+ 'disabled' => '' },
'extra_sql' => $extra_sql,
});
warn "Found taxes ". join(',', map {$_->taxnum} @taxes) ."\n"
sub recur_cost_permonth {
my($self, $cust_pkg) = @_;
return 0 unless $self->freq =~ /^\d+$/ && $self->freq > 0;
- sprintf('%.2f', $self->recur_cost / $self->freq );
+ sprintf('%.2f', ($self->recur_cost || 0) / $self->freq );
}
=item cust_bill_pkg_recur CUST_PKG
sub setup_margin {
my $self = shift;
- $self->unit_setup(@_) - $self->setup_cost;
+ $self->unit_setup(@_) - ($self->setup_cost || 0);
}
=item recur_margin_permonth
#false laziness w/part_export & cdr
my %info;
foreach my $INC ( @INC ) {
- warn "globbing $INC/FS/part_pkg/*.pm\n" if $DEBUG;
- foreach my $file ( glob("$INC/FS/part_pkg/*.pm") ) {
+ warn "globbing $INC/FS/part_pkg/[a-z]*.pm\n" if $DEBUG;
+ foreach my $file ( glob("$INC/FS/part_pkg/[a-z]*.pm") ) {
warn "attempting to load plan info from $file\n" if $DEBUG;
$file =~ /\/(\w+)\.pm$/ or do {
warn "unrecognized file in $INC/FS/part_pkg/: $file\n";