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;
138 Delete this record from the database.
140 If this is a supplemental package link, deleting it will set pkglinknum = null
141 for any related packages, and set those packages to expire on their next bill
146 my $cancel_reason_text = 'Supplemental package removed';
147 my $cancel_reason_type = 'Cancel Reason';
150 my $oldAutoCommit = $FS::UID::AutoCommit;
151 local $FS::UID::AutoCommit = 0;
156 if ( $self->link_type eq 'supp' ) {
157 my $error = $self->remove_linked;
159 $dbh->rollback if $oldAutoCommit;
164 my $error = $self->SUPER::delete(@_);
166 $dbh->rollback if $oldAutoCommit;
175 Removes any supplemental packages that were created by this link, by canceling
176 them and setting their pkglinknum to null. This should be done in preparation
177 for removing the link itself.
183 my $pkglinknum = $self->pkglinknum;
186 # find linked packages
187 my @pkgs = qsearch('cust_pkg', { pkglinknum => $pkglinknum });
188 warn "expiring ".scalar(@pkgs).
189 " linked packages from part_pkg_link #$pkglinknum\n";
191 my $reason = FS::reason->new_or_existing(
193 type => $cancel_reason_type,
194 reason => $cancel_reason_text
197 foreach my $pkg (@pkgs) {
198 $pkg->set('pkglinknum' => '');
199 if ( $pkg->get('cancel') ) {
200 # then just replace it to unlink the package from this object
201 $error = $pkg->replace;
203 $error = $pkg->cancel(
204 'date' => $pkg->get('bill'), # cancel on next bill, or else now
205 'reason' => $reason->reasonnum,
209 return "$error (scheduling package #".$pkg->pkgnum." for expiration)";
214 =item replace OLD_RECORD
216 Replaces the OLD_RECORD with this one in the database. If there is an error,
217 returns the error, otherwise returns false.
221 # the replace method can be inherited from FS::Record
225 Checks all fields to make sure this is a valid link. If there is
226 an error, returns the error, otherwise returns false. Called by the insert
231 # the check method should currently be supplied - FS::Record contains some
232 # data checking routines
238 $self->ut_numbern('pkglinknum')
239 || $self->ut_foreign_key('src_pkgpart', 'part_pkg', 'pkgpart')
240 || $self->ut_foreign_key('dst_pkgpart', 'part_pkg', 'pkgpart')
241 || $self->ut_enum('link_type', [ 'bill', 'svc', 'supp' ] )
242 || $self->ut_enum('hidden', [ '', 'Y' ] )
244 return $error if $error;
246 if ( $self->link_type eq 'supp' ) {
247 # some sanity checking
248 my $src_pkg = $self->src_pkg;
249 my $dst_pkg = $self->dst_pkg;
250 if ( $src_pkg->freq eq '0' and $dst_pkg->freq ne '0' ) {
251 return "One-time charges can't have supplemental packages."
252 } elsif ( $dst_pkg->freq ne '0' ) {
253 my $ratio = $dst_pkg->freq / $src_pkg->freq;
254 if ($ratio != int($ratio)) {
255 return "Supplemental package period (pkgpart ".$dst_pkg->pkgpart.
256 ") must be an integer multiple of main package period.";
266 Returns the source part_pkg object (see L<FS::part_pkg>).
272 qsearchs('part_pkg', { 'pkgpart' => $self->src_pkgpart } );
277 Returns the source part_pkg object (see L<FS::part_pkg>).
283 qsearchs('part_pkg', { 'pkgpart' => $self->dst_pkgpart } );
292 L<FS::Record>, schema.html from the base documentation.