package FS::part_pkg;
use strict;
-use vars qw( @ISA %plans $DEBUG );
+use vars qw( @ISA %plans $DEBUG $setup_hack );
use Carp qw(carp cluck confess);
use Scalar::Util qw( blessed );
use Time::Local qw( timelocal_nocheck );
@ISA = qw( FS::m2m_Common FS::option_Common );
$DEBUG = 0;
+$setup_hack = 0;
=head1 NAME
=item disabled - Disabled flag, empty or `Y'
+=item custom - Custom flag, empty or `Y'
+
+=item setup_cost - for cost tracking
+
+=item recur_cost - for cost tracking
+
=item pay_weight - Weight (relative to credit_weight and other package definitions) that controls payment application to specific line items.
=item credit_weight - Weight (relative to other package definitions) that controls credit application to specific line items.
=item clone
An alternate constructor. Creates a new package definition by duplicating
-an existing definition. A new pkgpart is assigned and `(CUSTOM) ' is prepended
-to the comment field. To add the package definition to the database, see
-L<"insert">.
+an existing definition. A new pkgpart is assigned and the custom flag is
+set to Y. To add the package definition to the database, see L<"insert">.
=cut
my $class = ref($self);
my %hash = $self->hash;
$hash{'pkgpart'} = '';
- $hash{'comment'} = "(CUSTOM) ". $hash{'comment'}
- unless $hash{'comment'} =~ /^\(CUSTOM\) /;
+ $hash{'custom'} = 'Y';
#new FS::part_pkg ( \%hash ); # ?
new $class ( \%hash ); # ?
}
|| $self->ut_enum('recurtax', [ '', 'Y' ] )
|| $self->ut_textn('taxclass')
|| $self->ut_enum('disabled', [ '', 'Y' ] )
+ || $self->ut_enum('custom', [ '', 'Y' ] )
+ #|| $self->ut_moneyn('setup_cost')
+ #|| $self->ut_moneyn('recur_cost')
+ || $self->ut_floatn('setup_cost')
+ || $self->ut_floatn('recur_cost')
|| $self->ut_floatn('pay_weight')
|| $self->ut_floatn('credit_weight')
|| $self->ut_numbern('taxproductnum')
'part_pkg_taxproduct',
'taxproductnum'
)
- || $self->ut_agentnum_acl('agentnum', \@null_agentnum_right)
+ || ( $setup_hack
+ ? $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum' )
+ : $self->ut_agentnum_acl('agentnum', \@null_agentnum_right)
+ )
|| $self->SUPER::check
;
return $error if $error;
'';
}
-=item pkg_comment
+=item pkg_comment [ OPTION => VALUE... ]
Returns an (internal) string representing this package. Currently,
"pkgpart: pkg - comment", is returned. "pkg - comment" may be returned in the
-future, omitting pkgpart.
+future, omitting pkgpart. The comment will have '(CUSTOM) ' prepended if
+custom is Y.
+
+If the option nopkgpart is true then the "pkgpart: ' is omitted.
=cut
sub pkg_comment {
my $self = shift;
+ my %opt = @_;
#$self->pkg. ' - '. $self->comment;
#$self->pkg. ' ('. $self->comment. ')';
- $self->pkgpart. ': '. $self->pkg. ' - '. $self->comment;
+ my $pre = $opt{nopkgpart} ? '' : $self->pkgpart. ': ';
+ $pre. $self->pkg. ' - '. $self->custom_comment;
+}
+
+sub custom_comment {
+ my $self = shift;
+ ( $self->custom ? '(CUSTOM) ' : '' ). $self->comment;
}
=item pkg_class
associated with this package definition (see L<FS::pkg_svc>). Returns
false if there not a primary service definition or exactly one service
definition with quantity 1, or if SVCDB is specified and does not match the
-svcdb of the service definition,
+svcdb of the service definition. SVCDB can be specified as a scalar table
+name, such as 'svc_acct', or as an arrayref of possible table names.
=cut
sub svcpart {
+ my $pkg_svc = shift->_primary_pkg_svc(@_);
+ $pkg_svc ? $pkg_svc->svcpart : '';
+}
+
+=item part_svc [ SVCDB ]
+
+Like the B<svcpart> method, but returns the FS::part_svc object (see
+L<FS::part_svc>).
+
+=cut
+
+sub part_svc {
+ my $pkg_svc = shift->_primary_pkg_svc(@_);
+ $pkg_svc ? $pkg_svc->part_svc : '';
+}
+
+sub _primary_pkg_svc {
my $self = shift;
- my $svcdb = scalar(@_) ? shift : '';
+
+ my $svcdb = scalar(@_) ? shift : [];
+ $svcdb = ref($svcdb) ? $svcdb : [ $svcdb ];
+ my %svcdb = map { $_=>1 } @$svcdb;
+
my @svcdb_pkg_svc =
- grep { ( $svcdb eq $_->part_svc->svcdb || !$svcdb ) } $self->pkg_svc;
+ grep { !scalar(@$svcdb) || $svcdb{ $_->part_svc->svcdb } }
+ $self->pkg_svc;
+
my @pkg_svc = grep { $_->primary_svc =~ /^Y/i } @svcdb_pkg_svc;
@pkg_svc = grep {$_->quantity == 1 } @svcdb_pkg_svc
unless @pkg_svc;
return '' if scalar(@pkg_svc) != 1;
- $pkg_svc[0]->svcpart;
+ $pkg_svc[0];
}
=item svcpart_unique_svcdb SVCDB
-Returns the svcpart of the a service definition (see L<FS::part_svc>) matching
+Returns the svcpart of a service definition (see L<FS::part_svc>) matching
SVCDB associated with this package definition (see L<FS::pkg_svc>). Returns
false if there not a primary service definition for SVCDB or there are multiple
service definitions for SVCDB.
sub calc_cancel { 0; }
sub calc_units { 0; }
+#fallback for everything except bulk.pm
+sub hide_svc_detail { 0; }
+
=item format OPTION DATA
Returns data formatted according to the function 'format' described
}
+ # now upgrade to the explicit custom flag
+
+ @part_pkg = qsearch({
+ 'table' => 'part_pkg',
+ 'hashref' => { disabled => 'Y', custom => '' },
+ 'extra_sql' => "AND comment LIKE '(CUSTOM) %'",
+ });
+
+ foreach my $part_pkg (@part_pkg) {
+ my $new = new FS::part_pkg { $part_pkg->hash };
+ $new->custom('Y');
+ my $comment = $part_pkg->comment;
+ $comment =~ s/^\(CUSTOM\) //;
+ $new->comment($comment);
+
+ my $pkg_svc = { map { $_->svcpart => $_->quantity } $part_pkg->pkg_svc };
+ my $primary = $part_pkg->svcpart;
+ my $options = { $part_pkg->options };
+
+ my $error = $new->replace( $part_pkg,
+ 'pkg_svc' => $pkg_svc,
+ 'primary_svc' => $primary,
+ 'options' => $options,
+ );
+ die $error if $error;
+ }
+
}
=item curuser_pkgs_sql
=cut
sub curuser_pkgs_sql {
- #my($class) = shift;
+ my $class = shift;
+
+ $class->_pkgs_sql( $FS::CurrentUser::CurrentUser->agentnums );
+
+}
- my $agentnums = join(',', $FS::CurrentUser::CurrentUser->agentnums);
+=item agent_pkgs_sql AGENT | AGENTNUM, ...
+
+Returns an SQL fragment for searching for packages the provided agent or agents
+can use, either via part_pkg.agentnum directly, or via agent type (see
+L<FS::type_pkgs>).
+
+=cut
+
+sub agent_pkgs_sql {
+ my $class = shift; #i'm a class method, not a sub (the question is... why??)
+ my @agentnums = map { ref($_) ? $_->agentnum : $_ } @_;
+
+ $class->_pkgs_sql(@agentnums); #is this why
+
+}
+
+sub _pkgs_sql {
+ my( $class, @agentnums ) = @_;
+ my $agentnums = join(',', @agentnums);
"
(
part_pkg_taxrate is Pg specific
+replace should be smarter about managing the related tables (options, pkg_svc)
+
=head1 SEE ALSO
L<FS::Record>, L<FS::cust_pkg>, L<FS::type_pkgs>, L<FS::pkg_svc>, L<Safe>.