6 use FS::Record qw( qsearch qsearchs dbh );
15 use FS::domain_record;
17 @ISA = qw( FS::Record );
21 my ( $hashref, $cache ) = @_;
22 if ( $hashref->{'username'} ) {
23 $self->{'_svc_acct'} = FS::svc_acct->new($hashref, '');
25 if ( $hashref->{'svc'} ) {
26 $self->{'_svcpart'} = FS::part_svc->new($hashref);
32 FS::cust_svc - Object method for cust_svc objects
38 $record = new FS::cust_svc \%hash
39 $record = new FS::cust_svc { 'column' => 'value' };
41 $error = $record->insert;
43 $error = $new_record->replace($old_record);
45 $error = $record->delete;
47 $error = $record->check;
49 ($label, $value) = $record->label;
53 An FS::cust_svc represents a service. FS::cust_svc inherits from FS::Record.
54 The following fields are currently supported:
58 =item svcnum - primary key (assigned automatically for new services)
60 =item pkgnum - Package (see L<FS::cust_pkg>)
62 =item svcpart - Service definition (see L<FS::part_svc>)
72 Creates a new service. To add the refund to the database, see L<"insert">.
73 Services are normally created by creating FS::svc_ objects (see
74 L<FS::svc_acct>, L<FS::svc_domain>, and L<FS::svc_forward>, among others).
78 sub table { 'cust_svc'; }
82 Adds this service to the database. If there is an error, returns the error,
83 otherwise returns false.
87 Deletes this service from the database. If there is an error, returns the
88 error, otherwise returns false.
90 Called by the cancel method of the package (see L<FS::cust_pkg>).
92 =item replace OLD_RECORD
94 Replaces the OLD_RECORD with this one in the database. If there is an error,
95 returns the error, otherwise returns false.
100 my ( $new, $old ) = ( shift, shift );
102 local $SIG{HUP} = 'IGNORE';
103 local $SIG{INT} = 'IGNORE';
104 local $SIG{QUIT} = 'IGNORE';
105 local $SIG{TERM} = 'IGNORE';
106 local $SIG{TSTP} = 'IGNORE';
107 local $SIG{PIPE} = 'IGNORE';
109 my $oldAutoCommit = $FS::UID::AutoCommit;
110 local $FS::UID::AutoCommit = 0;
113 my $error = $new->SUPER::replace($old);
115 $dbh->rollback if $oldAutoCommit;
116 return $error if $error;
119 if ( $new->svcpart != $old->svcpart ) {
120 my $svc_x = $new->svc_x;
121 my $new_svc_x = ref($svc_x)->new({$svc_x->hash});
122 my $error = $new_svc_x->replace($svc_x);
124 $dbh->rollback if $oldAutoCommit;
125 return $error if $error;
129 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
136 Checks all fields to make sure this is a valid service. If there is an error,
137 returns the error, otehrwise returns false. Called by the insert and
146 $self->ut_numbern('svcnum')
147 || $self->ut_numbern('pkgnum')
148 || $self->ut_number('svcpart')
150 return $error if $error;
152 my $part_svc = qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
153 return "Unknown svcpart" unless $part_svc;
155 if ( $self->pkgnum ) {
156 my $cust_pkg = qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
157 return "Unknown pkgnum" unless $cust_pkg;
158 my $pkg_svc = qsearchs( 'pkg_svc', {
159 'pkgpart' => $cust_pkg->pkgpart,
160 'svcpart' => $self->svcpart,
162 my @cust_svc = qsearch('cust_svc', {
163 'pkgnum' => $self->pkgnum,
164 'svcpart' => $self->svcpart,
166 return "Already ". scalar(@cust_svc). " ". $part_svc->svc.
167 " services for pkgnum ". $self->pkgnum
168 if scalar(@cust_svc) >= $pkg_svc->quantity;
176 Returns the definition for this service, as a FS::part_svc object (see
184 ? $self->{'_svcpart'}
185 : qsearchs( 'part_svc', { 'svcpart' => $self->svcpart } );
190 Returns the definition for this service, as a FS::part_svc object (see
197 qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
202 Returns a list consisting of:
203 - The name of this service (from part_svc)
204 - A meaningful identifier (username, domain, or mail alias)
205 - The table name (i.e. svc_domain) for this service
211 my $svcdb = $self->part_svc->svcdb;
212 my $svc_x = $self->svc_x
213 or die "can't find $svcdb.svcnum ". $self->svcnum;
215 if ( $svcdb eq 'svc_acct' ) {
216 $tag = $svc_x->email;
217 } elsif ( $svcdb eq 'svc_acct_sm' ) {
218 my $domuser = $svc_x->domuser eq '*' ? '(anything)' : $svc_x->domuser;
219 my $svc_domain = qsearchs ( 'svc_domain', { 'svcnum' => $svc_x->domsvc } );
220 my $domain = $svc_domain->domain;
221 $tag = "$domuser\@$domain";
222 } elsif ( $svcdb eq 'svc_forward' ) {
223 my $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->srcsvc } );
224 $tag = $svc_acct->email. '->';
225 if ( $svc_x->dstsvc ) {
226 $svc_acct = qsearchs( 'svc_acct', { 'svcnum' => $svc_x->dstsvc } );
227 $tag .= $svc_acct->email;
231 } elsif ( $svcdb eq 'svc_domain' ) {
232 $tag = $svc_x->getfield('domain');
233 } elsif ( $svcdb eq 'svc_www' ) {
234 my $domain = qsearchs( 'domain_record', { 'recnum' => $svc_x->recnum } );
235 $tag = $domain->reczone;
237 cluck "warning: asked for label of unsupported svcdb; using svcnum";
238 $tag = $svc_x->getfield('svcnum');
240 $self->part_svc->svc, $tag, $svcdb;
245 Returns the FS::svc_XXX object for this service (i.e. an FS::svc_acct object or
246 FS::svc_domain object, etc.)
252 my $svcdb = $self->part_svc->svcdb;
253 if ( $svcdb eq 'svc_acct' && $self->{'_svc_acct'} ) {
254 $self->{'_svc_acct'};
256 qsearchs( $svcdb, { 'svcnum' => $self->svcnum } );
260 =item seconds_since TIMESTAMP
262 See L<FS::svc_acct/seconds_since>. Equivalent to
263 $cust_svc->svc_x->seconds_since, but more efficient. Meaningless for records
264 where B<svcdb> is not "svc_acct".
268 #note: implementation here, POD in FS::svc_acct
270 my($self, $since) = @_;
272 my $sth = $dbh->prepare(' SELECT SUM(logout-login) FROM session
275 AND logout IS NOT NULL'
276 ) or die $dbh->errstr;
277 $sth->execute($self->svcnum, $since) or die $sth->errstr;
278 $sth->fetchrow_arrayref->[0];
285 $Id: cust_svc.pm,v 1.13 2002-04-12 15:14:58 ivan Exp $
289 Behaviour of changing the svcpart of cust_svc records is undefined and should
290 possibly be prohibited, and pkg_svc records are not checked.
292 pkg_svc records are not checked in general (here).
294 Deleting this record doesn't check or delete the svc_* record associated
299 L<FS::Record>, L<FS::cust_pkg>, L<FS::part_svc>, L<FS::pkg_svc>,
300 schema.html from the base documentation