2 use base qw( FS::o2m_Common FS::svc_External_Common );
6 use FS::Record qw( qsearch qsearchs dbh );
7 use FS::PagedSearch qw( psearch );
12 use FS::pbx_extension;
16 FS::svc_pbx - Object methods for svc_pbx records
22 $record = new FS::svc_pbx \%hash;
23 $record = new FS::svc_pbx { 'column' => 'value' };
25 $error = $record->insert;
27 $error = $new_record->replace($old_record);
29 $error = $record->delete;
31 $error = $record->check;
33 $error = $record->suspend;
35 $error = $record->unsuspend;
37 $error = $record->cancel;
41 An FS::svc_pbx object represents a PBX tenant. FS::svc_pbx inherits from
42 FS::svc_Common. The following fields are currently supported:
48 Primary key (assigned automatcially for new accounts)
52 (Unique?) number of external record
60 Maximum number of extensions
62 =item max_simultaneous
64 Maximum number of simultaneous users
68 The IP address of this PBX, if that's relevant. This must be a valid IP
69 address (or blank), but it's not checked for block assignment or uniqueness.
79 Creates a new PBX tenant. To add the PBX tenant to the database, see
82 Note that this stores the hash reference, not a distinct copy of the hash it
83 points to. You can ask the object for a copy with the I<hash> method.
87 sub table { 'svc_pbx'; }
91 tie my %fields, 'Tie::IxHash',
93 'id' => 'PBX/Tenant ID',
94 'uuid' => 'External UUID',
96 'max_extensions' => 'Maximum number of User Extensions',
97 'max_simultaneous' => 'Maximum number of simultaneous users',
98 'ip_addr' => 'IP address',
103 'name_plural' => 'PBXs',
104 'lcname_plural' => 'PBXs',
105 'longname_plural' => 'PBXs',
106 'sorts' => 'svcnum', # optional sort field (or arrayref of sort fields, main first)
107 'display_weight' => 70,
108 'cancel_weight' => 90,
109 'fields' => \%fields,
113 =item search_sql STRING
115 Class method which returns an SQL fragment to search for the given string.
120 #or something more complicated if necessary
122 # my($class, $string) = @_;
123 # $class->search_sql_field('title', $string);
128 Returns the title field for this PBX tenant.
139 Adds this record to the database. If there is an error, returns the error,
140 otherwise returns false.
142 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
143 defined. An FS::cust_svc record will be created and inserted.
151 $error = $self->SUPER::insert;
152 return $error if $error;
159 Delete this record from the database.
166 local $SIG{HUP} = 'IGNORE';
167 local $SIG{INT} = 'IGNORE';
168 local $SIG{QUIT} = 'IGNORE';
169 local $SIG{TERM} = 'IGNORE';
170 local $SIG{TSTP} = 'IGNORE';
171 local $SIG{PIPE} = 'IGNORE';
173 my $oldAutoCommit = $FS::UID::AutoCommit;
174 local $FS::UID::AutoCommit = 0;
177 foreach my $svc_phone (qsearch('svc_phone', { 'pbxsvc' => $self->svcnum } )) {
178 $svc_phone->pbxsvc('');
179 my $error = $svc_phone->replace;
181 $dbh->rollback if $oldAutoCommit;
186 foreach my $svc_acct (qsearch('svc_acct', { 'pbxsvc' => $self->svcnum } )) {
187 my $error = $svc_acct->delete;
189 $dbh->rollback if $oldAutoCommit;
194 my $error = $self->SUPER::delete;
196 $dbh->rollback if $oldAutoCommit;
200 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
205 =item replace OLD_RECORD
207 Replaces the OLD_RECORD with this one in the database. If there is an error,
208 returns the error, otherwise returns false.
213 # my ( $new, $old ) = ( shift, shift );
216 # $error = $new->SUPER::replace($old);
217 # return $error if $error;
224 Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
228 Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
232 Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
236 Checks all fields to make sure this is a valid PBX tenant. If there is
237 an error, returns the error, otherwise returns false. Called by the insert
245 my $x = $self->setfixed;
246 return $x unless ref($x);
250 $self->ut_ipn('ip_addr')
251 || $self->SUPER::check;
254 sub _check_duplicate {
257 my $conf = new FS::Conf;
261 foreach my $field ('title', 'id') {
262 my $global_unique = $conf->config("global_unique-pbx_$field");
263 # can be 'disabled', 'enabled', or empty.
264 # if empty, check per exports; if not empty or disabled, check
266 next if $global_unique eq 'disabled';
267 my @dup = $self->find_duplicates(
268 ($global_unique ? 'global' : 'export') , $field
271 return "duplicate $field '".$self->getfield($field).
272 "': conflicts with svcnum ".$dup[0]->svcnum;
277 =item psearch_cdrs OPTIONS
279 Returns a paged search (L<FS::PagedSearch>) for Call Detail Records
280 associated with this service. By default, "associated with" means that
281 the "charged_party" field of the CDR matches the "title" field of the
282 service. To access the CDRs themselves, call "->fetch" on the resulting
287 Accepts the following options:
289 =item for_update => 1: SELECT the CDRs "FOR UPDATE".
291 =item status => "" (or "done"): Return only CDRs with that processing status.
293 =item inbound => 1: No-op for svc_pbx CDR processing.
295 =item default_prefix => "XXX": Also accept the phone number of the service prepended
296 with the chosen prefix.
298 =item disable_src => 1: No-op for svc_pbx CDR processing.
300 =item by_svcnum => 1: Select CDRs where the svcnum field matches, instead of
301 title/charged_party. Normally this field is set after processing.
303 =item by_ip_addr => 'src' or 'dst': Select CDRs where the src_ip_addr or
304 dst_ip_addr field matches title. In this case, some special logic is applied
305 to allow title to indicate a range of IP addresses.
307 =item begin, end: Start and end of date range, as unix timestamp.
309 =item cdrtypenum: Only return CDRs with this type.
311 =item calltypenum: Only return CDRs with this call type.
318 my($self, %options) = @_;
322 my @fields = ( 'charged_party' );
323 $hash{'freesidestatus'} = $options{'status'}
324 if exists($options{'status'});
326 if ($options{'cdrtypenum'}) {
327 $hash{'cdrtypenum'} = $options{'cdrtypenum'};
329 if ($options{'calltypenum'}) {
330 $hash{'calltypenum'} = $options{'calltypenum'};
333 my $for_update = $options{'for_update'} ? 'FOR UPDATE' : '';
335 if ( $options{'by_svcnum'} ) {
336 $hash{'svcnum'} = $self->svcnum;
338 elsif ( $options{'by_ip_addr'} =~ /^src|dst$/) {
339 my $field = 'cdr.'.$options{'by_ip_addr'}.'_ip_addr';
340 push @where, FS::cdr->ip_addr_sql($field, $self->title);
344 my $title = $self->title;
346 my $prefix = $options{'default_prefix'};
348 my @orwhere = map " $_ = '$title' ", @fields;
349 push @orwhere, map " $_ = '$prefix$title' ", @fields
351 if ( $prefix =~ /^\+(\d+)$/ ) {
352 push @orwhere, map " $_ = '$1$title' ", @fields
355 push @where, ' ( '. join(' OR ', @orwhere ). ' ) ';
358 if ( $options{'begin'} ) {
359 push @where, 'startdate >= '. $options{'begin'};
361 if ( $options{'end'} ) {
362 push @where, 'startdate < '. $options{'end'};
365 my $extra_sql = ( keys(%hash) ? ' AND ' : ' WHERE ' ). join(' AND ', @where )
371 'extra_sql' => $extra_sql,
372 'order_by' => "ORDER BY startdate $for_update",
376 =item get_cdrs (DEPRECATED)
378 Like psearch_cdrs, but returns all the L<FS::cdr> objects at once, in a
379 single list. Arguments are the same as for psearch_cdrs. This can take
380 an unreasonably large amount of memory and is best avoided.
386 my $psearch = $self->psearch_cdrs($_);
387 qsearch ( $psearch->{query} )
392 Takes the same options as psearch_cdrs, but returns a single row containing
393 "count" (the number of CDRs) and the sums of the following fields: duration,
394 billsec, rated_price, rated_seconds, rated_minutes.
396 Note that if any calls are not rated, their rated_* fields will be null.
397 If you want to use those fields, pass the 'status' option to limit to
398 calls that have been rated. This is intentional; please don't "fix" it.
404 my $psearch = $self->psearch_cdrs(@_);
405 $psearch->{query}->{'select'} = join(',',
407 map { "SUM($_) AS $_" }
408 qw(duration billsec rated_price rated_seconds rated_minutes)
411 $psearch->{query}->{'extra_sql'} =~ s/ ORDER BY.*$//;
412 qsearchs ( $psearch->{query} );
420 qsearch('pbx_extension', { svcnum=>$self->svcnum });
429 L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
430 L<FS::cust_pkg>, schema.html from the base documentation.