2 use base qw( FS::o2m_Common FS::device_Common FS::svc_External_Common );
6 use FS::Record qw( qsearch qsearchs dbh );
7 use FS::PagedSearch qw( psearch );
15 FS::svc_pbx - Object methods for svc_pbx records
21 $record = new FS::svc_pbx \%hash;
22 $record = new FS::svc_pbx { 'column' => 'value' };
24 $error = $record->insert;
26 $error = $new_record->replace($old_record);
28 $error = $record->delete;
30 $error = $record->check;
32 $error = $record->suspend;
34 $error = $record->unsuspend;
36 $error = $record->cancel;
40 An FS::svc_pbx object represents a PBX tenant. FS::svc_pbx inherits from
41 FS::svc_Common. The following fields are currently supported:
47 Primary key (assigned automatcially for new accounts)
51 (Unique?) number of external record
59 Maximum number of extensions
61 =item max_simultaneous
63 Maximum number of simultaneous users
73 Creates a new PBX tenant. To add the PBX tenant to the database, see
76 Note that this stores the hash reference, not a distinct copy of the hash it
77 points to. You can ask the object for a copy with the I<hash> method.
81 sub table { 'svc_pbx'; }
85 tie my %fields, 'Tie::IxHash',
87 'id' => 'PBX/Tenant ID',
89 'max_extensions' => 'Maximum number of User Extensions',
90 'max_simultaneous' => 'Maximum number of simultaneous users',
95 'name_plural' => 'PBXs',
96 'lcname_plural' => 'PBXs',
97 'longname_plural' => 'PBXs',
98 'sorts' => 'svcnum', # optional sort field (or arrayref of sort fields, main first)
99 'display_weight' => 70,
100 'cancel_weight' => 90,
101 'fields' => \%fields,
105 =item search_sql STRING
107 Class method which returns an SQL fragment to search for the given string.
112 #or something more complicated if necessary
114 # my($class, $string) = @_;
115 # $class->search_sql_field('title', $string);
120 Returns the title field for this PBX tenant.
131 Adds this record to the database. If there is an error, returns the error,
132 otherwise returns false.
134 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
135 defined. An FS::cust_svc record will be created and inserted.
143 $error = $self->SUPER::insert;
144 return $error if $error;
151 Delete this record from the database.
158 local $SIG{HUP} = 'IGNORE';
159 local $SIG{INT} = 'IGNORE';
160 local $SIG{QUIT} = 'IGNORE';
161 local $SIG{TERM} = 'IGNORE';
162 local $SIG{TSTP} = 'IGNORE';
163 local $SIG{PIPE} = 'IGNORE';
165 my $oldAutoCommit = $FS::UID::AutoCommit;
166 local $FS::UID::AutoCommit = 0;
169 foreach my $svc_phone (qsearch('svc_phone', { 'pbxsvc' => $self->svcnum } )) {
170 $svc_phone->pbxsvc('');
171 my $error = $svc_phone->replace;
173 $dbh->rollback if $oldAutoCommit;
178 foreach my $svc_acct (qsearch('svc_acct', { 'pbxsvc' => $self->svcnum } )) {
179 my $error = $svc_acct->delete;
181 $dbh->rollback if $oldAutoCommit;
186 my $error = $self->SUPER::delete;
188 $dbh->rollback if $oldAutoCommit;
192 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
197 =item replace OLD_RECORD
199 Replaces the OLD_RECORD with this one in the database. If there is an error,
200 returns the error, otherwise returns false.
205 # my ( $new, $old ) = ( shift, shift );
208 # $error = $new->SUPER::replace($old);
209 # return $error if $error;
216 Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
220 Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
224 Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
228 Checks all fields to make sure this is a valid PBX tenant. If there is
229 an error, returns the error, otherwise returns false. Called by the insert
237 my $x = $self->setfixed;
238 return $x unless ref($x);
245 sub _check_duplicate {
248 my $conf = new FS::Conf;
252 foreach my $field ('title', 'id') {
253 my $global_unique = $conf->config("global_unique-pbx_$field");
254 # can be 'disabled', 'enabled', or empty.
255 # if empty, check per exports; if not empty or disabled, check
257 next if $global_unique eq 'disabled';
258 my @dup = $self->find_duplicates(
259 ($global_unique ? 'global' : 'export') , $field
262 return "duplicate $field '".$self->getfield($field).
263 "': conflicts with svcnum ".$dup[0]->svcnum;
268 =item psearch_cdrs OPTIONS
270 Returns a paged search (L<FS::PagedSearch>) for Call Detail Records
271 associated with this service. By default, "associated with" means that
272 the "charged_party" field of the CDR matches the "title" field of the
273 service. To access the CDRs themselves, call "->fetch" on the resulting
278 Accepts the following options:
280 =item for_update => 1: SELECT the CDRs "FOR UPDATE".
282 =item status => "" (or "done"): Return only CDRs with that processing status.
284 =item inbound => 1: No-op for svc_pbx CDR processing.
286 =item default_prefix => "XXX": Also accept the phone number of the service prepended
287 with the chosen prefix.
289 =item disable_src => 1: No-op for svc_pbx CDR processing.
291 =item by_svcnum => 1: Select CDRs where the svcnum field matches, instead of
292 title/charged_party. Normally this field is set after processing.
294 =item by_ip_addr => 'src' or 'dst': Select CDRs where the src_ip_addr or
295 dst_ip_addr field matches title. In this case, some special logic is applied
296 to allow title to indicate a range of IP addresses.
298 =item begin, end: Start and end of date range, as unix timestamp.
300 =item cdrtypenum: Only return CDRs with this type.
302 =item calltypenum: Only return CDRs with this call type.
309 my($self, %options) = @_;
313 my @fields = ( 'charged_party' );
314 $hash{'freesidestatus'} = $options{'status'}
315 if exists($options{'status'});
317 if ($options{'cdrtypenum'}) {
318 $hash{'cdrtypenum'} = $options{'cdrtypenum'};
320 if ($options{'calltypenum'}) {
321 $hash{'calltypenum'} = $options{'calltypenum'};
324 my $for_update = $options{'for_update'} ? 'FOR UPDATE' : '';
326 if ( $options{'by_svcnum'} ) {
327 $hash{'svcnum'} = $self->svcnum;
329 elsif ( $options{'by_ip_addr'} =~ /^src|dst$/) {
330 my $field = 'cdr.'.$options{'by_ip_addr'}.'_ip_addr';
331 push @where, FS::cdr->ip_addr_sql($field, $self->title);
335 my $title = $self->title;
337 my $prefix = $options{'default_prefix'};
339 my @orwhere = map " $_ = '$title' ", @fields;
340 push @orwhere, map " $_ = '$prefix$title' ", @fields
342 if ( $prefix =~ /^\+(\d+)$/ ) {
343 push @orwhere, map " $_ = '$1$title' ", @fields
346 push @where, ' ( '. join(' OR ', @orwhere ). ' ) ';
349 if ( $options{'begin'} ) {
350 push @where, 'startdate >= '. $options{'begin'};
352 if ( $options{'end'} ) {
353 push @where, 'startdate < '. $options{'end'};
356 my $extra_sql = ( keys(%hash) ? ' AND ' : ' WHERE ' ). join(' AND ', @where )
362 'extra_sql' => $extra_sql,
363 'order_by' => "ORDER BY startdate $for_update",
367 =item get_cdrs (DEPRECATED)
369 Like psearch_cdrs, but returns all the L<FS::cdr> objects at once, in a
370 single list. Arguments are the same as for psearch_cdrs. This can take
371 an unreasonably large amount of memory and is best avoided.
377 my $psearch = $self->psearch_cdrs($_);
378 qsearch ( $psearch->{query} )
387 L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
388 L<FS::cust_pkg>, schema.html from the base documentation.