local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
+ if ( length($self->classnum) && $self->classnum !~ /^(\d+)$/ ) {
+ my $pkg_class = qsearchs('pkg_class', { 'classname' => $self->classnum } )
+ || new FS::pkg_class { classname => $self->classnum };
+ unless ( $pkg_class->classnum ) {
+ my $error = $pkg_class->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+ $self->classnum( $pkg_class->classnum );
+ }
+
warn " inserting part_pkg record" if $DEBUG;
my $error = $self->SUPER::insert( $options{options} );
if ( $error ) {
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. I<bulk_skip> and I<provision_hold> can be set
-to a hashref of svcparts and flag values ('Y' or '') to set the respective field
-in those records.
+can be set to a hashref of svcparts and flag values ('Y' or '') to set the
+'hidden' field in these records. I<bulk_skip> and I<provision_hold> can be
+set to a hashref of svcparts and flag values ('Y' or '') to set the
+respective 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.
+If I<primary_svc> is set to the svcpart of the primary service, the
+appropriate FS::pkg_svc record will be updated.
-If I<options> is set to a hashref, the appropriate FS::part_pkg_option records
-will be replaced.
+If I<options> is set to a hashref, the appropriate FS::part_pkg_option
+records will be replaced.
If I<part_pkg_currency> is set to a hashref of options (with the keys as
-option_CURRENCY), appropriate FS::part_pkg::currency records will be replaced.
+option_CURRENCY), appropriate FS::part_pkg::currency records will be
+replaced.
=cut
'';
}
+sub validate_number {
+ my ($option, $valref) = @_;
+
+ $$valref = 0 unless $$valref;
+ return "Invalid $option"
+ unless ($$valref) = ($$valref =~ /^\s*(\d+)\s*$/);
+ return '';
+}
+
+sub validate_number_blank {
+ my ($option, $valref) = @_;
+
+ if ($$valref) {
+ return "Invalid $option"
+ unless ($$valref) = ($$valref =~ /^\s*(\d+)\s*$/);
+ }
+ return '';
+}
+
=item check
Checks all fields to make sure this is a valid package definition. If
=item check_options
-For a passed I<$options> hashref, validates any options that
-have 'validate' subroutines defined in the info hash,
-then validates the entire hashref if the price plan has
-its own 'validate' subroutine defined in the info hash
-(I<$options> values might be altered.)
+Pass an I<$options> hashref that contains the values to be
+inserted or updated for any FS::part_pkg::MODULE.pm.
+
+For each key in I<$options>, validates the value by calling
+the 'validate' subroutine defined for that option e.g.
+FS::part_pkg::MODULE::plan_info()->{$KEY}->{validate}. The
+option validation function is only called when the hashkey for
+that option exists in I<$options>.
+
+Then the module validation function is called, from
+FS::part_pkg::MODULE::plan_info()->{validate}
Returns error message, or empty string if valid.
=item calc_setup CUST_PKG START_DATE DETAILS_ARRAYREF OPTIONS_HASHREF
-=item calc_recur CUST_PKG START_DATE DETAILS_ARRAYREF OPTIONS_HASHREF
+=item calc_recur CUST_PKG START_DATE_SCALARREF DETAILS_ARRAYREF OPTIONS_HASHREF
Calculates and returns the setup or recurring fees, respectively, for this
package. Implementation is in the FS::part_pkg:* module specific to this price
This option is filled in by the method rather than controlling its operation.
It is an arrayref. Applicable discounts will be added to the arrayref, as
-L<FS::cust_bill_pkg_discount|FS::cust_bill_pkg_discount records>.
+L<FS::cust_bill_pkg_discount> records.
=item real_pkgpart
-For package add-ons, is the base L<FS::part_pkg|package definition>, otherwise
+For package add-ons, is the base L<FS::part_pkg> package definition, otherwise
no different than pkgpart.
=item precommit_hooks
=back
Note: Don't calculate prices when not actually billing the package. For that,
-see the L</base_setup|base_setup> and L</base_recur|base_recur> methods.
+see the L<FS::cust_pkg/base_setup> and L<FS::cust_pkg/base_recur> methods.
=cut
die $error if $error;
}
}
+
+ # remove custom flag from one-time charge packages that were accidentally
+ # flagged as custom
+ $search = FS::Cursor->new({
+ 'table' => 'part_pkg',
+ 'hashref' => { 'freq' => '0',
+ 'custom' => 'Y',
+ 'family_pkgpart' => { op => '!=', value => '' },
+ },
+ 'addl_from' => ' JOIN
+ (select pkgpart from cust_pkg group by pkgpart having count(*) = 1)
+ AS singular_pkg USING (pkgpart)',
+ });
+ my @fields = grep { $_ ne 'pkgpart'
+ and $_ ne 'custom'
+ and $_ ne 'disabled' } FS::part_pkg->fields;
+ PKGPART: while (my $part_pkg = $search->fetch) {
+ # can't merge the package back into its parent (too late for that)
+ # but we can remove the custom flag if it's not actually customized,
+ # i.e. nothing has been changed.
+
+ my $family_pkgpart = $part_pkg->family_pkgpart;
+ next PKGPART if $family_pkgpart == $part_pkg->pkgpart;
+ my $parent_pkg = FS::part_pkg->by_key($family_pkgpart);
+ foreach my $field (@fields) {
+ if ($part_pkg->get($field) ne $parent_pkg->get($field)) {
+ next PKGPART;
+ }
+ }
+ # options have to be identical too
+ # but links, FCC options, discount plans, and usage packages can't be
+ # changed through the "modify charge" UI, so skip them
+ my %newopt = $part_pkg->options;
+ my %oldopt = $parent_pkg->options;
+ OPTION: foreach my $option (keys %newopt) {
+ if (delete $newopt{$option} ne delete $oldopt{$option}) {
+ next PKGPART;
+ }
+ }
+ if (keys(%newopt) or keys(%oldopt)) {
+ next PKGPART;
+ }
+ # okay, now replace it
+ warn "Removing custom flag from part_pkg#".$part_pkg->pkgpart."\n";
+ $part_pkg->set('custom', '');
+ my $error = $part_pkg->replace;
+ die $error if $error;
+ } # $search->fetch
+
+ return;
}
=item curuser_pkgs_sql
=cut
1;
-