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
67 The IP address of this PBX, if that's relevant. This must be a valid IP
68 address (or blank), but it's not checked for block assignment or uniqueness.
78 Creates a new PBX tenant. To add the PBX tenant to the database, see
81 Note that this stores the hash reference, not a distinct copy of the hash it
82 points to. You can ask the object for a copy with the I<hash> method.
86 sub table { 'svc_pbx'; }
90 tie my %fields, 'Tie::IxHash',
92 'id' => 'PBX/Tenant ID',
93 'uuid' => 'External UUID',
95 'max_extensions' => 'Maximum number of User Extensions',
96 'max_simultaneous' => 'Maximum number of simultaneous users',
97 'ip_addr' => 'IP address',
102 'name_plural' => 'PBXs',
103 'lcname_plural' => 'PBXs',
104 'longname_plural' => 'PBXs',
105 'sorts' => 'svcnum', # optional sort field (or arrayref of sort fields, main first)
106 'display_weight' => 70,
107 'cancel_weight' => 90,
108 'fields' => \%fields,
112 =item search_sql STRING
114 Class method which returns an SQL fragment to search for the given string.
119 #or something more complicated if necessary
121 # my($class, $string) = @_;
122 # $class->search_sql_field('title', $string);
127 Returns the title field for this PBX tenant.
138 Adds this record to the database. If there is an error, returns the error,
139 otherwise returns false.
141 The additional fields pkgnum and svcpart (see L<FS::cust_svc>) should be
142 defined. An FS::cust_svc record will be created and inserted.
146 Delete this record from the database.
153 local $SIG{HUP} = 'IGNORE';
154 local $SIG{INT} = 'IGNORE';
155 local $SIG{QUIT} = 'IGNORE';
156 local $SIG{TERM} = 'IGNORE';
157 local $SIG{TSTP} = 'IGNORE';
158 local $SIG{PIPE} = 'IGNORE';
160 my $oldAutoCommit = $FS::UID::AutoCommit;
161 local $FS::UID::AutoCommit = 0;
164 foreach my $svc_phone (qsearch('svc_phone', { 'pbxsvc' => $self->svcnum } )) {
165 $svc_phone->pbxsvc('');
166 my $error = $svc_phone->replace;
168 $dbh->rollback if $oldAutoCommit;
173 foreach my $svc_acct (qsearch('svc_acct', { 'pbxsvc' => $self->svcnum } )) {
174 my $error = $svc_acct->delete;
176 $dbh->rollback if $oldAutoCommit;
181 my $error = $self->SUPER::delete;
183 $dbh->rollback if $oldAutoCommit;
187 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
192 =item replace OLD_RECORD
194 Replaces the OLD_RECORD with this one in the database. If there is an error,
195 returns the error, otherwise returns false.
199 Called by the suspend method of FS::cust_pkg (see L<FS::cust_pkg>).
203 Called by the unsuspend method of FS::cust_pkg (see L<FS::cust_pkg>).
207 Called by the cancel method of FS::cust_pkg (see L<FS::cust_pkg>).
211 Checks all fields to make sure this is a valid PBX tenant. If there is
212 an error, returns the error, otherwise returns false. Called by the insert
220 my $x = $self->setfixed;
221 return $x unless ref($x);
225 $self->ut_ipn('ip_addr')
226 || $self->SUPER::check;
229 sub _check_duplicate {
232 my $conf = new FS::Conf;
236 foreach my $field ('title', 'id') {
237 my $global_unique = $conf->config("global_unique-pbx_$field");
238 # can be 'disabled', 'enabled', or empty.
239 # if empty, check per exports; if not empty or disabled, check
241 next if $global_unique eq 'disabled';
242 my @dup = $self->find_duplicates(
243 ($global_unique ? 'global' : 'export') , $field
246 return "duplicate $field '".$self->getfield($field).
247 "': conflicts with svcnum ".$dup[0]->svcnum;
252 =item psearch_cdrs OPTIONS
254 Returns a paged search (L<FS::PagedSearch>) for Call Detail Records
255 associated with this service. By default, "associated with" means that
256 the "charged_party" field of the CDR matches the "title" field of the
257 service. To access the CDRs themselves, call "->fetch" on the resulting
262 Accepts the following options:
264 =item for_update => 1: SELECT the CDRs "FOR UPDATE".
266 =item status => "" (or "done"): Return only CDRs with that processing status.
268 =item inbound => 1: No-op for svc_pbx CDR processing.
270 =item default_prefix => "XXX": Also accept the phone number of the service prepended
271 with the chosen prefix.
273 =item disable_src => 1: No-op for svc_pbx CDR processing.
275 =item by_svcnum => 1: Select CDRs where the svcnum field matches, instead of
276 title/charged_party. Normally this field is set after processing.
278 =item by_ip_addr => 'src' or 'dst': Select CDRs where the src_ip_addr or
279 dst_ip_addr field matches title. In this case, some special logic is applied
280 to allow title to indicate a range of IP addresses.
282 =item begin, end: Start and end of date range, as unix timestamp.
284 =item cdrtypenum: Only return CDRs with this type.
286 =item calltypenum: Only return CDRs with this call type.
293 my($self, %options) = @_;
297 my @fields = ( 'charged_party' );
298 $hash{'freesidestatus'} = $options{'status'}
299 if exists($options{'status'});
301 if ($options{'cdrtypenum'}) {
302 $hash{'cdrtypenum'} = $options{'cdrtypenum'};
304 if ($options{'calltypenum'}) {
305 $hash{'calltypenum'} = $options{'calltypenum'};
308 my $for_update = $options{'for_update'} ? 'FOR UPDATE' : '';
310 if ( $options{'by_svcnum'} ) {
311 $hash{'svcnum'} = $self->svcnum;
313 elsif ( $options{'by_ip_addr'} =~ /^src|dst$/) {
314 my $field = 'cdr.'.$options{'by_ip_addr'}.'_ip_addr';
315 push @where, FS::cdr->ip_addr_sql($field, $self->title);
319 my $title = $self->title;
321 my $prefix = $options{'default_prefix'};
323 my @orwhere = map " $_ = '$title' ", @fields;
324 push @orwhere, map " $_ = '$prefix$title' ", @fields
326 if ( $prefix =~ /^\+(\d+)$/ ) {
327 push @orwhere, map " $_ = '$1$title' ", @fields
330 push @where, ' ( '. join(' OR ', @orwhere ). ' ) ';
333 if ( $options{'begin'} ) {
334 push @where, 'startdate >= '. $options{'begin'};
336 if ( $options{'end'} ) {
337 push @where, 'startdate < '. $options{'end'};
340 my $extra_sql = ( keys(%hash) ? ' AND ' : ' WHERE ' ). join(' AND ', @where )
346 'extra_sql' => $extra_sql,
347 'order_by' => "ORDER BY startdate $for_update",
351 =item get_cdrs (DEPRECATED)
353 Like psearch_cdrs, but returns all the L<FS::cdr> objects at once, in a
354 single list. Arguments are the same as for psearch_cdrs. This can take
355 an unreasonably large amount of memory and is best avoided.
361 my $psearch = $self->psearch_cdrs($_);
362 qsearch ( $psearch->{query} )
367 Takes the same options as psearch_cdrs, but returns a single row containing
368 "count" (the number of CDRs) and the sums of the following fields: duration,
369 billsec, rated_price, rated_seconds, rated_minutes.
371 Note that if any calls are not rated, their rated_* fields will be null.
372 If you want to use those fields, pass the 'status' option to limit to
373 calls that have been rated. This is intentional; please don't "fix" it.
379 my $psearch = $self->psearch_cdrs(@_);
380 $psearch->{query}->{'select'} = join(',',
382 map { "SUM($_) AS $_" }
383 qw(duration billsec rated_price rated_seconds rated_minutes)
386 $psearch->{query}->{'extra_sql'} =~ s/ ORDER BY.*$//;
387 qsearchs ( $psearch->{query} );
392 require FS::Misc::FixIPFormat;
393 FS::Misc::FixIPFormat::fix_bad_addresses_in_table(
394 'svc_pbx', 'svcnum', 'ip_addr',
407 L<FS::svc_Common>, L<FS::Record>, L<FS::cust_svc>, L<FS::part_svc>,
408 L<FS::cust_pkg>, schema.html from the base documentation.