1 package FS::part_pkg_link;
5 use FS::Record qw( qsearchs qsearch dbh );
11 @ISA = qw(FS::Record);
15 FS::part_pkg_link - Object methods for part_pkg_link records
19 use FS::part_pkg_link;
21 $record = new FS::part_pkg_link \%hash;
22 $record = new FS::part_pkg_link { 'column' => 'value' };
24 $error = $record->insert;
26 $error = $new_record->replace($old_record);
28 $error = $record->delete;
30 $error = $record->check;
34 An FS::part_pkg_link object represents an link from one package definition to
35 another. FS::part_pkg_link inherits from FS::Record. The following fields are
46 Source package (see L<FS::part_pkg>)
50 Destination package (see L<FS::part_pkg>)
54 Link type - currently, "bill" (source package bills a line item from target
55 package), or "svc" (source package includes services from target package),
56 or "supp" (ordering source package creates a target package).
60 Flag indicating that this subpackage should be felt, but not seen as an invoice
61 line item when set to 'Y'. Not allowed for "supp" links.
71 Creates a new link. To add the link to the database, see L<"insert">.
73 Note that this stores the hash reference, not a distinct copy of the hash it
74 points to. You can ask the object for a copy with the I<hash> method.
78 # the new method can be inherited from FS::Record, if a table method is defined
80 sub table { 'part_pkg_link'; }
84 Adds this record to the database. If there is an error, returns the error,
85 otherwise returns false.
87 If this is a supplemental package link, inserting it will order the
88 supplemental packages for any main packages that already exist.
93 my $oldAutoCommit = $FS::UID::AutoCommit;
94 local $FS::UID::AutoCommit = 0;
98 my $error = $self->SUPER::insert(@_);
100 $dbh->rollback if $oldAutoCommit;
101 return $error if $error;
104 if ( $self->link_type eq 'supp' ) {
106 my @main_pkgs = qsearch('cust_pkg', {
107 pkgpart => $self->src_pkgpart,
110 foreach my $main_pkg (@main_pkgs) {
111 # duplicates code in FS::cust_pkg::uncancel, sort of
112 my $supp_pkg = FS::cust_pkg->new({
113 'pkgpart' => $self->dst_pkgpart,
114 'pkglinknum' => $self->pkglinknum,
115 'main_pkgnum' => $main_pkg->pkgnum,
116 'order_date' => time,
117 map { $_ => $main_pkg->get($_) }
118 qw( custnum locationnum pkgbatch
119 start_date setup expire adjourn contract_end bill susp
120 refnum discountnum waive_setup quantity
121 recur_show_zero setup_show_zero )
123 $error = $supp_pkg->insert;
125 $dbh->rollback if $oldAutoCommit;
126 return "$error (ordering new supplemental package to pkg#".$main_pkg->pkgnum.")" if $error;
130 return $error if $error;
133 $dbh->commit if $oldAutoCommit;
139 Delete this record from the database.
141 If this is a supplemental package link, deleting it will set pkglinknum = null
142 for any related packages, and set those packages to expire on their next bill
147 my $cancel_reason_text = 'Supplemental package removed';
148 my $cancel_reason_type = 'Cancel Reason';
151 my $oldAutoCommit = $FS::UID::AutoCommit;
152 local $FS::UID::AutoCommit = 0;
157 if ( $self->link_type eq 'supp' ) {
158 my $error = $self->remove_linked;
160 $dbh->rollback if $oldAutoCommit;
165 my $error = $self->SUPER::delete(@_);
167 $dbh->rollback if $oldAutoCommit;
170 $dbh->commit if $oldAutoCommit;
176 Removes any supplemental packages that were created by this link, by canceling
177 them and setting their pkglinknum to null. This should be done in preparation
178 for removing the link itself.
184 my $pkglinknum = $self->pkglinknum;
187 # find linked packages
188 my @pkgs = qsearch('cust_pkg', { pkglinknum => $pkglinknum });
189 warn "expiring ".scalar(@pkgs).
190 " linked packages from part_pkg_link #$pkglinknum\n";
192 my $reason = FS::reason->new_or_existing(
194 type => $cancel_reason_type,
195 reason => $cancel_reason_text
198 foreach my $pkg (@pkgs) {
199 $pkg->set('pkglinknum' => '');
200 if ( $pkg->get('cancel') ) {
201 # then just replace it to unlink the package from this object
202 $error = $pkg->replace;
204 $error = $pkg->cancel(
205 'date' => $pkg->get('bill'), # cancel on next bill, or else now
206 'reason' => $reason->reasonnum,
210 return "$error (scheduling package #".$pkg->pkgnum." for expiration)";
215 =item replace OLD_RECORD
217 Replaces the OLD_RECORD with this one in the database. If there is an error,
218 returns the error, otherwise returns false.
222 # the replace method can be inherited from FS::Record
226 Checks all fields to make sure this is a valid link. If there is
227 an error, returns the error, otherwise returns false. Called by the insert
232 # the check method should currently be supplied - FS::Record contains some
233 # data checking routines
239 $self->ut_numbern('pkglinknum')
240 || $self->ut_foreign_key('src_pkgpart', 'part_pkg', 'pkgpart')
241 || $self->ut_foreign_key('dst_pkgpart', 'part_pkg', 'pkgpart')
242 || $self->ut_enum('link_type', [ 'bill', 'svc', 'supp' ] )
243 || $self->ut_enum('hidden', [ '', 'Y' ] )
245 return $error if $error;
247 if ( $self->link_type eq 'supp' ) {
248 # some sanity checking
249 my $src_pkg = $self->src_pkg;
250 my $dst_pkg = $self->dst_pkg;
251 if ( $src_pkg->freq eq '0' and $dst_pkg->freq ne '0' ) {
252 return "One-time charges can't have supplemental packages."
253 } elsif ( $dst_pkg->freq == 0 ) {
254 return "The billing period of a supplemental package must be a whole number of months.";
255 } elsif ( $src_pkg->freq == 0 ) {
256 return "To have supplemental packages, the billing period of a package must be a whole number of months.";
265 Returns the source part_pkg object (see L<FS::part_pkg>).
271 qsearchs('part_pkg', { 'pkgpart' => $self->src_pkgpart } );
276 Returns the source part_pkg object (see L<FS::part_pkg>).
282 qsearchs('part_pkg', { 'pkgpart' => $self->dst_pkgpart } );
291 L<FS::Record>, schema.html from the base documentation.