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
74 Creates a new PBX tenant. To add the PBX tenant to the database, see
77 Note that this stores the hash reference, not a distinct copy of the hash it
78 points to. You can ask the object for a copy with the I<hash> method.
82 sub table { 'svc_pbx'; }
86 tie my %fields, 'Tie::IxHash',
88 'id' => 'PBX/Tenant ID',
90 'max_extensions' => 'Maximum number of User Extensions',
91 'max_simultaneous' => 'Maximum number of simultaneous users',
96 'name_plural' => 'PBXs',
97 'lcname_plural' => 'PBXs',
98 'longname_plural' => 'PBXs',
99 'sorts' => 'svcnum', # optional sort field (or arrayref of sort fields, main first)
100 'display_weight' => 70,
101 'cancel_weight' => 90,
102 'fields' => \%fields,
106 =item search_sql STRING
108 Class method which returns an SQL fragment to search for the given string.
113 #or something more complicated if necessary
115 # my($class, $string) = @_;
116 # $class->search_sql_field('title', $string);
121 Returns the title field for this PBX tenant.
132 Adds this record to the database. If there is an error, returns the error,
133 otherwise returns false.
135 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
136 defined. An FS::cust_svc record will be created and inserted.
144 $error = $self->SUPER::insert;
145 return $error if $error;
152 Delete this record from the database.
159 local $SIG{HUP} = 'IGNORE';
160 local $SIG{INT} = 'IGNORE';
161 local $SIG{QUIT} = 'IGNORE';
162 local $SIG{TERM} = 'IGNORE';
163 local $SIG{TSTP} = 'IGNORE';
164 local $SIG{PIPE} = 'IGNORE';
166 my $oldAutoCommit = $FS::UID::AutoCommit;
167 local $FS::UID::AutoCommit = 0;
170 foreach my $svc_phone (qsearch('svc_phone', { 'pbxsvc' => $self->svcnum } )) {
171 $svc_phone->pbxsvc('');
172 my $error = $svc_phone->replace;
174 $dbh->rollback if $oldAutoCommit;
179 foreach my $svc_acct (qsearch('svc_acct', { 'pbxsvc' => $self->svcnum } )) {
180 my $error = $svc_acct->delete;
182 $dbh->rollback if $oldAutoCommit;
187 my $error = $self->SUPER::delete;
189 $dbh->rollback if $oldAutoCommit;
193 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
198 =item replace OLD_RECORD
200 Replaces the OLD_RECORD with this one in the database. If there is an error,
201 returns the error, otherwise returns false.
206 # my ( $new, $old ) = ( shift, shift );
209 # $error = $new->SUPER::replace($old);
210 # return $error if $error;
217 Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
221 Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
225 Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
229 Checks all fields to make sure this is a valid PBX tenant. If there is
230 an error, returns the error, otherwise returns false. Called by the insert
238 my $x = $self->setfixed;
239 return $x unless ref($x);
246 sub _check_duplicate {
249 my $conf = new FS::Conf;
253 foreach my $field ('title', 'id') {
254 my $global_unique = $conf->config("global_unique-pbx_$field");
255 # can be 'disabled', 'enabled', or empty.
256 # if empty, check per exports; if not empty or disabled, check
258 next if $global_unique eq 'disabled';
259 my @dup = $self->find_duplicates(
260 ($global_unique ? 'global' : 'export') , $field
263 return "duplicate $field '".$self->getfield($field).
264 "': conflicts with svcnum ".$dup[0]->svcnum;
269 =item psearch_cdrs OPTIONS
271 Returns a paged search (L<FS::PagedSearch>) for Call Detail Records
272 associated with this service. By default, "associated with" means that
273 the "charged_party" field of the CDR matches the "title" field of the
274 service. To access the CDRs themselves, call "->fetch" on the resulting
279 Accepts the following options:
281 =item for_update => 1: SELECT the CDRs "FOR UPDATE".
283 =item status => "" (or "done"): Return only CDRs with that processing status.
285 =item inbound => 1: No-op for svc_pbx CDR processing.
287 =item default_prefix => "XXX": Also accept the phone number of the service prepended
288 with the chosen prefix.
290 =item disable_src => 1: No-op for svc_pbx CDR processing.
292 =item by_svcnum => 1: Select CDRs where the svcnum field matches, instead of
293 title/charged_party. Normally this field is set after processing.
295 =item by_ip_addr => 'src' or 'dst': Select CDRs where the src_ip_addr or
296 dst_ip_addr field matches title. In this case, some special logic is applied
297 to allow title to indicate a range of IP addresses.
299 =item begin, end: Start and end of date range, as unix timestamp.
301 =item cdrtypenum: Only return CDRs with this type.
303 =item calltypenum: Only return CDRs with this call type.
310 my($self, %options) = @_;
314 my @fields = ( 'charged_party' );
315 $hash{'freesidestatus'} = $options{'status'}
316 if exists($options{'status'});
318 if ($options{'cdrtypenum'}) {
319 $hash{'cdrtypenum'} = $options{'cdrtypenum'};
321 if ($options{'calltypenum'}) {
322 $hash{'calltypenum'} = $options{'calltypenum'};
325 my $for_update = $options{'for_update'} ? 'FOR UPDATE' : '';
327 if ( $options{'by_svcnum'} ) {
328 $hash{'svcnum'} = $self->svcnum;
330 elsif ( $options{'by_ip_addr'} =~ /^src|dst$/) {
331 my $field = 'cdr.'.$options{'by_ip_addr'}.'_ip_addr';
332 push @where, FS::cdr->ip_addr_sql($field, $self->title);
336 my $title = $self->title;
338 my $prefix = $options{'default_prefix'};
340 my @orwhere = map " $_ = '$title' ", @fields;
341 push @orwhere, map " $_ = '$prefix$title' ", @fields
343 if ( $prefix =~ /^\+(\d+)$/ ) {
344 push @orwhere, map " $_ = '$1$title' ", @fields
347 push @where, ' ( '. join(' OR ', @orwhere ). ' ) ';
350 if ( $options{'begin'} ) {
351 push @where, 'startdate >= '. $options{'begin'};
353 if ( $options{'end'} ) {
354 push @where, 'startdate < '. $options{'end'};
357 my $extra_sql = ( keys(%hash) ? ' AND ' : ' WHERE ' ). join(' AND ', @where )
363 'extra_sql' => $extra_sql,
364 'order_by' => "ORDER BY startdate $for_update",
368 =item get_cdrs (DEPRECATED)
370 Like psearch_cdrs, but returns all the L<FS::cdr> objects at once, in a
371 single list. Arguments are the same as for psearch_cdrs. This can take
372 an unreasonably large amount of memory and is best avoided.
378 my $psearch = $self->psearch_cdrs($_);
379 qsearch ( $psearch->{query} )
384 qsearch('pbx_extension', { svcnum=>$self->svcnum });
393 L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
394 L<FS::cust_pkg>, schema.html from the base documentation.