summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2015-05-31 23:24:52 -0700
committerMark Wells <mark@freeside.biz>2015-05-31 23:24:52 -0700
commit6bc36a712285e9616a00ae38b136d7b8cd6cb58f (patch)
treedd1b6d580ef4c28ac634766acfd5e690388b7c77
parent09589754a8926ef064ea1d3d474a0fc3a8590101 (diff)
BroadWorks export, phase 1, #25927
-rw-r--r--FS/FS/Schema.pm1
-rw-r--r--FS/FS/part_device.pm9
-rw-r--r--FS/FS/part_export/broadworks.pm823
-rw-r--r--FS/FS/phone_device.pm9
-rw-r--r--FS/FS/svc_phone.pm2
-rw-r--r--httemplate/browse/part_device.html7
-rw-r--r--httemplate/edit/part_device.html2
-rw-r--r--httemplate/view/svc_phone.cgi2
8 files changed, 847 insertions, 8 deletions
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 5ebab78..2d98963 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -5858,6 +5858,7 @@ sub tables_hashref {
'devicepart', 'serial', '', '', '', '',
'devicename', 'varchar', '', $char_d, '', '',
'inventory_classnum', 'int', 'NULL', '', '', '',
+ 'title', 'varchar', 'NULL', $char_d, '', '',
],
'primary_key' => 'devicepart',
'unique' => [ [ 'devicename' ] ], #?
diff --git a/FS/FS/part_device.pm b/FS/FS/part_device.pm
index 0f840a7..ac98740 100644
--- a/FS/FS/part_device.pm
+++ b/FS/FS/part_device.pm
@@ -38,10 +38,16 @@ primary key
=item devicename
-devicename
+device name (used in Freeside)
=item inventory_classnum
+L<FS::inventory_class> used to track inventory of these devices.
+
+=item title
+
+external device name (for export)
+
=back
=head1 METHODS
@@ -105,6 +111,7 @@ sub check {
$self->ut_numbern('devicepart')
|| $self->ut_text('devicename')
|| $self->ut_foreign_keyn('inventory_classnum', 'inventory_class', 'classnum')
+ || $self->ut_textn('title')
;
return $error if $error;
diff --git a/FS/FS/part_export/broadworks.pm b/FS/FS/part_export/broadworks.pm
new file mode 100644
index 0000000..7a9ed70
--- /dev/null
+++ b/FS/FS/part_export/broadworks.pm
@@ -0,0 +1,823 @@
+package FS::part_export::broadworks;
+
+use base qw( FS::part_export );
+use strict;
+
+use Tie::IxHash;
+use FS::Record qw(dbh qsearch qsearchs);
+use Locale::SubCountry;
+use BroadWorks::OCI;
+
+our $me = '[broadworks]';
+our %client; # exportnum => client object
+our %expire; # exportnum => timestamp on which to refresh the client
+
+tie my %options, 'Tie::IxHash',
+ 'service_provider'=> { label => 'Service Provider ID' },
+ 'admin_user' => { label => 'Administrative user ID' },
+ 'admin_pass' => { label => 'Administrative password' },
+ 'domain' => { label => 'Domain' },
+ 'user_limit' => { label => 'Maximum users per customer',
+ default => 100 },
+ 'debug' => { label => 'Enable debugging',
+ type => 'checkbox',
+ },
+;
+
+# do we need roles for this?
+# no. cust_main -> group, svc_phone -> pilot/single user,
+# phone_device -> access device
+#
+# phase 2: svc_pbx -> trunk group, pbx_extension -> trunk user
+
+our %info = (
+ 'svc' => [qw( svc_phone svc_pbx )], # part_device?
+ 'desc' =>
+ 'Provision phone and PBX services to a Broadworks Application Server',
+ 'options' => \%options,
+ 'notes' => <<'END'
+<P>Export to <b>BroadWorks Application Server</b>.</P>
+<P>In the simple case where one IP phone corresponds to one public phone
+number, this requires a svc_phone definition and a part_device. The "title"
+field ("external name") of the part_device must be one of the access device
+type names recognized by BroadWorks, such as "Polycom Soundpoint IP 550",
+"SNOM 320", or "Generic SIP Phone".</P>
+<P>
+END
+);
+
+sub export_insert {
+ my($self, $svc_x) = (shift, shift);
+
+ my $cust_main = $svc_x->cust_main;
+ my ($groupId, $error) = $self->set_cust_main_Group($cust_main);
+ return $error if $error;
+
+ if ( $svc_x->isa('FS::svc_phone') ) {
+ my $userId;
+ ($userId, $error) = $self->set_svc_phone_User($svc_x, $groupId);
+
+ $error ||= $self->set_sip_authentication($userId, $userId, $svc_x->sip_password);
+
+ return $error if $error;
+
+ } elsif ( $svc_x->isa('FS::svc_pbx') ) {
+ # noop
+ }
+
+ '';
+}
+
+sub export_replace {
+ my($self, $svc_new, $svc_old) = @_;
+
+ my $cust_main = $svc_new->cust_main;
+ my ($groupId, $error) = $self->set_cust_main_Group($cust_main);
+ return $error if $error;
+
+ if ( $svc_new->isa('FS::svc_phone') ) {
+ my $oldUserId = $self->userId($svc_old);
+ my $newUserId = $self->userId($svc_new);
+
+ if ( $oldUserId ne $newUserId ) {
+ my ($success, $message) = $self->request(
+ User => 'UserModifyUserIdRequest',
+ userId => $oldUserId,
+ newUserId => $newUserId
+ );
+ return $message if !$success;
+ }
+
+ if ( $svc_old->phonenum ne $svc_new->phonenum ) {
+ $error ||= $self->release_number($svc_old->phonenum, $groupId);
+ }
+
+ my $userId;
+ ($userId, $error) = $self->set_svc_phone_User($svc_new, $groupId);
+ $error ||= $self->set_sip_authentication($userId, $userId, $svc_new->sip_password);
+
+ if ($error and $oldUserId ne $newUserId) {
+ # rename it back, then
+ my ($success, $message) = $self->request(
+ User => 'UserModifyUserIdRequest',
+ userId => $newUserId,
+ newUserId => $oldUserId
+ );
+ # if it fails, we can't really fix it
+ return "$error; unable to reverse user ID change: $message" if !$success;
+ }
+
+ return $error if $error;
+
+ } elsif ( $svc_new->isa('FS::svc_pbx') ) {
+ # noop
+ }
+
+ '';
+}
+
+sub export_delete {
+ my ($self, $svc_x) = @_;
+
+ my $cust_main = $svc_x->cust_main;
+ my $groupId = $self->groupId($cust_main);
+
+ if ( $svc_x->isa('FS::svc_phone') ) {
+ my $userId = $self->userId($svc_x);
+ my $error = $self->delete_User($userId)
+ || $self->release_number($svc_x->phonenum, $groupId);
+ return $error if $error;
+ } elsif ( $svc_x->isa('FS::svc_pbx') ) {
+ # noop
+ }
+
+ # find whether the customer still has any services on this platform
+ # (other than the one being deleted)
+ my %svcparts = map { $_->svcpart => 1 } $self->export_svc;
+ my $svcparts = join(',', keys %svcparts);
+ my $num_svcs = FS::cust_svc->count(
+ '(select custnum from cust_pkg where cust_pkg.pkgnum = cust_svc.pkgnum) '.
+ ' = ? '.
+ ' AND svcnum != ?'.
+ " AND svcpart IN ($svcparts)",
+ $cust_main->custnum,
+ $svc_x->svcnum
+ );
+
+ if ( $num_svcs == 0 ) {
+ warn "$me removed last service for group $groupId; deleting group.\n";
+ my $error = $self->delete_Group($groupId);
+ warn "$me error deleting group: $error\n" if $error;
+ return "$error (removing customer group)" if $error;
+ }
+
+ '';
+}
+
+sub export_device_insert {
+ my ($self, $svc_x, $device) = @_;
+
+ if ( $device->count('svcnum = ?', $svc_x->svcnum) > 1 ) {
+ return "This service already has a device.";
+ }
+
+ my $cust_main = $svc_x->cust_main;
+ my $groupId = $self->groupId($cust_main);
+
+ my ($deviceName, $error) = $self->set_device_AccessDevice($device, $groupId);
+ return $error if $error;
+
+ if ( $device->isa('FS::phone_device') ) {
+ return $self->set_endpoint( $self->userId($svc_x), $deviceName);
+ } # else pbx_device, extension_device
+
+ '';
+}
+
+sub export_device_replace {
+ my ($self, $svc_x, $new_device, $old_device) = @_;
+ my $cust_main = $svc_x->cust_main;
+ my $groupId = $self->groupId($cust_main);
+
+ my $new_deviceName = $self->deviceName($new_device);
+ my $old_deviceName = $self->deviceName($old_device);
+
+ if ($new_deviceName ne $old_deviceName) {
+
+ # do it in this order to switch the service endpoint over to the new
+ # device.
+ return $self->export_device_insert($svc_x, $new_device)
+ || $self->delete_Device($old_deviceName, $groupId);
+
+ } else { # update in place
+
+ my ($deviceName, $error) = $self->set_device_AccessDevice($new_device, $groupId);
+ return $error if $error;
+
+ }
+}
+
+sub export_device_delete {
+ my ($self, $svc_x, $device) = @_;
+
+ if ( $device->isa('FS::phone_device') ) {
+ my $error = $self->set_endpoint( $self->userId($svc_x), '' );
+ return $error if $error;
+ } # else...
+
+ return $self->delete_Device($self->deviceName($device));
+}
+
+
+=head2 CREATE-OR-UPDATE METHODS
+
+These take a Freeside object that can be exported to the Broadworks system,
+determine if it already has been exported, and if so, update it to match the
+Freeside object. If it's not already there, they create it. They return a list
+of two objects:
+- that object's identifying string or hashref or whatever in Broadworks, and
+- an error message, if creating the object failed.
+
+=over 4
+
+=item set_cust_main_Group CUST_MAIN
+
+Takes a L<FS::cust_main>, creates a Group for the customer, and returns a
+GroupId. If the Group exists, it will be updated with the current customer
+and export settings.
+
+=cut
+
+sub set_cust_main_Group {
+ my $self = shift;
+ my $cust_main = shift;
+ my $location = $cust_main->ship_location;
+
+ my $LSC = Locale::SubCountry->new($location->country)
+ or return(0, "Invalid country code ".$location->country);
+ my $state_name;
+ if ( $LSC->has_sub_countries ) {
+ $state_name = $LSC->full_name( $location->state );
+ }
+
+ my $groupId = $self->groupId($cust_main);
+ my %group_info = (
+ $self->SPID,
+ groupId => $groupId,
+ defaultDomain => $self->option('domain'),
+ userLimit => $self->option('user_limit'),
+ groupName => $cust_main->name_short,
+ callingLineIdName => $cust_main->name_short,
+ contact => {
+ contactName => $cust_main->contact_firstlast,
+ contactNumber => ( $cust_main->daytime
+ || $cust_main->night
+ || $cust_main->mobile
+ || undef
+ ),
+ contactEmail => ( ($cust_main->all_emails)[0] || undef ),
+ },
+ address => {
+ addressLine1 => $location->address1,
+ addressLine2 => ($location->address2 || undef),
+ city => $location->city,
+ stateOrProvince => $state_name,
+ zipOrPostalCode => $location->zip,
+ country => $location->country,
+ },
+ );
+
+ my ($success, $message) = $self->request('Group' => 'GroupGetRequest14sp7',
+ $self->SPID,
+ groupId => $groupId
+ );
+
+ if ($success) { # update it with the curent params
+
+ ($success, $message) =
+ $self->request('Group' => 'GroupModifyRequest', %group_info);
+
+ } elsif ($message =~ /Group not found/) {
+
+ # create a new group
+ ($success, $message) =
+ $self->request('Group' => 'GroupAddRequest', %group_info);
+
+ if ($success) {
+ # tell the group that its users in general are allowed to use
+ # Authentication
+ ($success, $message) = $self->request(
+ 'Group' => 'GroupServiceModifyAuthorizationListRequest',
+ $self->SPID,
+ groupId => $groupId,
+ userServiceAuthorization => {
+ serviceName => 'Authentication',
+ authorizedQuantity => { unlimited => 'true' },
+ },
+ );
+ }
+
+ if ($success) {
+ # tell the group that each new user, specifically, is allowed to
+ # use Authentication
+ ($success, $message) = $self->request(
+ 'Group' => 'GroupNewUserTemplateAssignUserServiceListRequest',
+ $self->SPID,
+ groupId => $groupId,
+ serviceName => 'Authentication',
+ );
+ }
+
+ } # else we somehow failed to fetch the group; throw an error
+
+ if ($success) {
+ return ($groupId, '');
+ } else {
+ return ('', $message);
+ }
+}
+
+=item set_svc_phone_User SVC_PHONE, GROUPID
+
+Creates a User object corresponding to this svc_phone, in the specified
+group. If the User already exists, updates the record with the current
+customer name (or phone name), phone number, and access device.
+
+=cut
+
+sub set_svc_phone_User {
+ my ($self, $svc_phone, $groupId) = @_;
+
+ my $error;
+
+ # make sure the phone number is available
+ $error = $self->assign_number( $svc_phone->phonenum, $groupId );
+
+ my $userId = $self->userId($svc_phone);
+ my $cust_main = $svc_phone->cust_main;
+
+ my ($first, $last);
+ if ($svc_phone->phone_name =~ /,/) {
+ ($last, $first) = split(/,\s*/, $svc_phone->phone_name);
+ } elsif ($svc_phone->phone_name =~ / /) {
+ ($first, $last) = split(/ +/, $svc_phone->phone_name, 2);
+ } else {
+ $first = $cust_main->first;
+ $last = $cust_main->last;
+ }
+
+ my %new_user = (
+ $self->SPID,
+ groupId => $groupId,
+ userId => $userId,
+ lastName => $last,
+ firstName => $first,
+ callingLineIdLastName => $last,
+ callingLineIdFirstName => $first,
+ password => $svc_phone->sip_password,
+ # not supported: nameDialingName; Hiragana names
+ phoneNumber => $svc_phone->phonenum,
+ callingLinePhoneNumber => $svc_phone->phonenum,
+ );
+
+ # does the user exist?
+ my ($success, $message) = $self->request(
+ 'User' => 'UserGetRequest21',
+ userId => $userId
+ );
+
+ if ( $success ) { # modify in place
+
+ ($success, $message) = $self->request(
+ 'User' => 'UserModifyRequest17sp4',
+ %new_user
+ );
+
+ } elsif ( $message =~ /User not found/ ) { # create new
+
+ ($success, $message) = $self->request(
+ 'User' => 'UserAddRequest17sp4',
+ %new_user
+ );
+
+ }
+
+ if ($success) {
+ return ($userId, '');
+ } else {
+ return ('', $message);
+ }
+}
+
+=item set_device_AccessDevice DEVICE, [ GROUPID ]
+
+Creates/updates an Access Device Profile. This is a record for a
+I<specific physical device> that can send/receive calls. (Not to be confused
+with an "Access Device Endpoint", which is a I<port> on such a device.) DEVICE
+can be any record with a foreign key to L<FS::part_device>.
+
+If GROUPID is specified, this device profile will be created at the Group
+level in that group; otherwise it will be a ServiceProvider level record.
+
+=cut
+
+sub set_device_AccessDevice {
+ my $self = shift;
+ my $device = shift;
+ my $groupId = shift;
+
+ my $deviceName = $self->deviceName($device);
+
+ my $svc_x;
+ if ($device->svcnum) {
+ $svc_x = FS::cust_svc->by_key($device->svcnum)->svc_x;
+ } else {
+ $svc_x = FS::svc_phone->new({}); # returns empty for all fields
+ }
+
+ my $part_device = $device->part_device
+ or return ('', "devicepart ".$device->part_device." not defined" );
+
+ # required fields
+ my %new_device = (
+ $self->SPID,
+ deviceName => $deviceName,
+ deviceType => $part_device->title,
+ description => ($svc_x->title # svc_pbx
+ || $part_device->devicename), # others
+ );
+
+ # optional fields
+ $new_device{netAddress} = $svc_x->ip_addr if $svc_x->ip_addr; # svc_pbx only
+ $new_device{macAddress} = $device->mac_addr if $device->mac_addr;
+
+ my %find_device = (
+ $self->SPID,
+ deviceName => $deviceName
+ );
+ my $level = 'ServiceProvider';
+
+ if ( $groupId ) {
+ $level = 'Group';
+ $find_device{groupId} = $new_device{groupId} = $groupId;
+ } else {
+ # shouldn't be used in our current design
+ warn "$me creating access device $deviceName at Service Provider level\n";
+ }
+
+ my ($success, $message) = $self->request(
+ $level, $level.'AccessDeviceGetRequest18sp1',
+ %find_device
+ );
+
+ if ( $success ) { # modify in place
+
+ ($success, $message) = $self->request(
+ $level => $level.'AccessDeviceModifyRequest14',
+ %new_device
+ );
+
+ } elsif ( $message =~ /Access Device not found/ ) { # create new
+
+ ($success, $message) = $self->request(
+ $level => $level.'AccessDeviceAddRequest14',
+ %new_device
+ );
+
+ }
+
+ if ($success) {
+ return ($deviceName, '');
+ } else {
+ return ('', $message);
+ }
+}
+
+=back
+
+=head2 PROVISIONING METHODS
+
+These return an error string on failure, and an empty string on success.
+
+=over 4
+
+=item assign_number NUMBER, GROUPID
+
+Assigns a phone number to a group. If it's assigned to a different group or
+doesn't belong to the service provider, this will fail. If it's already
+assigned to I<this> group, it will do nothing and return success.
+
+=cut
+
+sub assign_number {
+ my ($self, $number, $groupId) = @_;
+ # see if it's already assigned
+ my ($success, $message) = $self->request(
+ Group => 'GroupDnGetAssignmentListRequest18',
+ $self->SPID,
+ groupId => $groupId,
+ searchCriteriaDn => {
+ mode => 'Equal To',
+ value => $number,
+ isCaseInsensitive => 'false',
+ },
+ );
+ return "$message (checking phone number status)" if !$success;
+ my $result = $self->oci_table( $message->{dnTable} );
+ return '' if @$result > 0;
+
+ ($success, $message) = $self->request(
+ Group => 'GroupDnAssignListRequest',
+ $self->SPID,
+ groupId => $groupId,
+ phoneNumber => $number,
+ );
+
+ $success ? '' : $message;
+}
+
+=item release_number NUMBER, GROUPID
+
+Unassigns a phone number from a group. If it's assigned to a user in the
+group then this will fail. If it's not assigned to the group at all, this
+does nothing.
+
+=cut
+
+sub release_number {
+ my ($self, $number, $groupId) = @_;
+ # see if it's already assigned
+ my ($success, $message) = $self->request(
+ Group => 'GroupDnGetAssignmentListRequest18',
+ $self->SPID,
+ groupId => $groupId,
+ searchCriteriaDn => {
+ mode => 'Equal To',
+ value => $number,
+ isCaseInsensitive => 'false',
+ },
+ );
+ return "$message (checking phone number status)" if !$success;
+ my $result = $self->oci_table( $message->{dnTable} );
+ return '' if @$result == 0;
+
+ ($success, $message) = $self->request(
+ Group => 'GroupDnUnassignListRequest',
+ $self->SPID,
+ groupId => $groupId,
+ phoneNumber => $number,
+ );
+
+ $success ? '' : $message;
+}
+
+=item set_endpoint USERID [, DEVICENAME ]
+
+Sets the endpoint for communicating with USERID to DEVICENAME. For now, this
+assumes that all devices are defined at Group level.
+
+If DEVICENAME is null, the user will be set to have no endpoint.
+
+=cut
+
+# we only support linePort = userId, and no numbered ports
+
+sub set_endpoint {
+ my ($self, $userId, $deviceName) = @_;
+
+ my $endpoint;
+ if ( length($deviceName) > 0 ) {
+ $endpoint = {
+ accessDeviceEndpoint => {
+ linePort => $userId,
+ accessDevice => {
+ deviceLevel => 'Group',
+ deviceName => $deviceName,
+ },
+ }
+ };
+ } else {
+ $endpoint = undef;
+ }
+ my ($success, $message) = $self->request(
+ User => 'UserModifyRequest17sp4',
+ userId => $userId,
+ endpoint => $endpoint,
+ );
+
+ $success ? '' : $message;
+}
+
+=item set_sip_authentication USERID, NAME, PASSWORD
+
+Sets the SIP authentication credentials for USERID to (NAME, PASSWORD).
+
+=cut
+
+sub set_sip_authentication {
+ my ($self, $userId, $userName, $password) = @_;
+
+ my ($success, $message) = $self->request(
+ 'Services/ServiceAuthentication' => 'UserAuthenticationModifyRequest',
+ userId => $userId,
+ userName => $userName,
+ newPassword => $password,
+ );
+
+ $success ? '' : $message;
+}
+
+=item delete_group GROUPID
+
+Deletes the group GROUPID.
+
+=cut
+
+sub delete_Group {
+ my ($self, $groupId) = @_;
+
+ my ($success, $message) = $self->request(
+ Group => 'GroupDeleteRequest',
+ $self->SPID,
+ groupId => $groupId
+ );
+ if ( $success or $message =~ /Group not found/ ) {
+ return '';
+ } else {
+ return $message;
+ }
+}
+
+=item delete_User USERID
+
+Deletes the user USERID, and releases its phone number if it has one.
+
+=cut
+
+sub delete_User {
+ my ($self, $userId) = @_;
+
+ my ($success, $message) = $self->request(
+ User => 'UserDeleteRequest',
+ userId => $userId
+ );
+ if ($success or $message =~ /User not found/) {
+ return '';
+ } else {
+ return $message;
+ }
+}
+
+=item delete_Device DEVICENAME[, GROUPID ]
+
+Deletes the access device DEVICENAME (from group GROUPID, or from the service
+provider if there is no GROUPID).
+
+=cut
+
+sub delete_Device {
+ my ($self, $deviceName, $groupId) = @_;
+
+ my ($success, $message);
+ if ( $groupId ) {
+ ($success, $message) = $self->request(
+ Group => 'GroupAccessDeviceDeleteRequest',
+ $self->SPID,
+ groupId => $groupId,
+ deviceName => $deviceName,
+ );
+ } else {
+ ($success, $message) = $self->request(
+ ServiceProvider => 'ServiceProviderAccessDeviceDeleteRequest',
+ $self->SPID,
+ deviceName => $deviceName,
+ );
+ }
+ if ( $success or $message =~ /Access Device not found/ ) {
+ return '';
+ } else {
+ return $message;
+ }
+}
+
+=back
+
+=head2 CONVENIENCE METHODS
+
+=over 4
+
+=item SPID
+
+Returns 'serviceProviderId' => the service_provider option. This is commonly
+needed in request parameters.
+
+=item groupId CUST_MAIN
+
+Returns the groupID that goes with the specified customer.
+
+=item userId SVC_X
+
+Returns the userId (including domain) that should go with the specified
+service.
+
+=item deviceName DEVICE
+
+Returns the access device name that should go with the specified phone_device
+or pbx_device.
+
+=cut
+
+sub SPID {
+ my $self = shift;
+ my $id = $self->option('service_provider') or die 'service provider not set';
+ 'serviceProviderId' => $id
+}
+
+sub groupId {
+ my $self = shift;
+ my $cust_main = shift;
+ 'cust_main#'.$cust_main->custnum;
+}
+
+sub userId {
+ my $self = shift;
+ my $svc = shift;
+ my $userId;
+ if ($svc->phonenum) {
+ $userId = $svc->phonenum;
+ } else { # pbx_extension needs one of these
+ die "can't determine userId for non-svc_phone service";
+ }
+ my $domain = $self->option('domain'); # domsvc?
+ $userId .= '@' . $domain if $domain;
+
+ return $userId;
+}
+
+sub deviceName {
+ my $self = shift;
+ my $device = shift;
+ $device->mac_addr || ($device->table . '#' . $device->devicenum);
+}
+
+=item oci_table HASHREF
+
+Converts the base OCITable type into an arrayref of hashrefs.
+
+=cut
+
+sub oci_table {
+ my $self = shift;
+ my $oci_table = shift;
+ my @colnames = $oci_table->{colHeading};
+ my @data;
+ foreach my $row (@{ $oci_table->{row} }) {
+ my %hash;
+ @hash{@colnames} = @{ $row->{col} };
+ push @data, \%hash;
+ }
+
+ \@data;
+}
+
+#################
+# DID SELECTION #
+#################
+
+
+
+################
+# CALL DETAILS #
+################
+
+=item import_cdrs START, END
+
+Retrieves CDRs for calls in the date range from START to END and inserts them
+as a new CDR batch. On success, returns a new cdr_batch object. On failure,
+returns an error message. If there are no new CDRs, returns nothing.
+
+=cut
+
+##############
+# API ACCESS #
+##############
+
+=item request SCOPE, COMMAND, [ ARGUMENTS... ]
+
+Wrapper for L<BroadWorks::OCI/request>. The client object will be cached.
+Returns two values: a flag, true or false, indicating success of the request,
+and the decoded response message as a hashref.
+
+On failure of the request (or failure to authenticate), the response message
+will be a simple scalar containing the error message.
+
+=cut
+
+sub request {
+ my $self = shift;
+
+ delete $client{$self->exportnum} if $expire{$self->exportnum} < time;
+ my $client = $client{$self->exportnum};
+ if (!$client) {
+ local $@;
+ eval "use BroadWorks::OCI";
+ die "$me $@" if $@;
+
+ Log::Report::dispatcher('PERL', 'default',
+ mode => ($self->option('debug') ? 'DEBUG' : 'NORMAL')
+ );
+
+ $client = BroadWorks::OCI->new(
+ userId => $self->option('admin_user'),
+ password => $self->option('admin_pass'),
+ );
+ my ($success, $message) = $client->login;
+ return ('', $message) if !$success;
+
+ $client{$self->exportnum} = $client; # if login succeeded
+ $expire{$self->exportnum} = time + 120; # hardcoded, yeah
+ }
+ return $client->request(@_);
+}
+
+1;
diff --git a/FS/FS/phone_device.pm b/FS/FS/phone_device.pm
index b891bb2..2afad91 100644
--- a/FS/FS/phone_device.pm
+++ b/FS/FS/phone_device.pm
@@ -90,12 +90,12 @@ sub insert {
my $dbh = dbh;
my $error = $self->SUPER::insert;
+ $error ||= $self->export('device_insert');
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
}
- $self->export('device_insert');
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
@@ -122,9 +122,8 @@ sub delete {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- $self->export('device_delete');
- my $error = $self->SUPER::delete;
+ my $error = $self->export('device_delete') || $self->SUPER::delete;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
@@ -159,13 +158,13 @@ sub replace {
local $FS::UID::AutoCommit = 0;
my $dbh = dbh;
- my $error = $new->SUPER::replace($old);
+ my $error = $new->SUPER::replace($old)
+ || $new->export('device_replace', $old);
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
return $error;
}
- $new->export('device_replace', $old);
$dbh->commit or die $dbh->errstr if $oldAutoCommit;
'';
diff --git a/FS/FS/svc_phone.pm b/FS/FS/svc_phone.pm
index fc90365..cf9d9b4 100644
--- a/FS/FS/svc_phone.pm
+++ b/FS/FS/svc_phone.pm
@@ -276,6 +276,8 @@ sub table { 'svc_phone'; }
sub table_dupcheck_fields { ( 'countrycode', 'phonenum' ); }
+sub device_table { 'phone_device'; }
+
=item search_sql STRING
Class method which returns an SQL fragment to search for the given string.
diff --git a/httemplate/browse/part_device.html b/httemplate/browse/part_device.html
index 69387dd..12c19ed 100644
--- a/httemplate/browse/part_device.html
+++ b/httemplate/browse/part_device.html
@@ -8,7 +8,10 @@
],
'query' => { 'table' => 'part_device', },
'count_query' => 'SELECT COUNT(*) FROM part_device',
- 'header' => [ '#', 'Device type', 'Inventory Class', ],
+ 'header' => [ '#',
+ 'Device type',
+ 'Inventory Class',
+ 'External name', ],
'fields' => [ 'devicepart',
'devicename',
sub {
@@ -18,10 +21,12 @@
if $inventory_class;
'';
},
+ 'title',
],
'links' => [ $link,
$link,
'',
+ '',
],
)
%>
diff --git a/httemplate/edit/part_device.html b/httemplate/edit/part_device.html
index 1317c8d..75e6b0a 100644
--- a/httemplate/edit/part_device.html
+++ b/httemplate/edit/part_device.html
@@ -5,6 +5,7 @@
'devicepart' => 'Part number',
'devicename' => 'Device name',
'inventory_classnum' => 'Inventory class',
+ 'title' => 'External name',
},
'fields' => \@fields,
'viewall_dir' => 'browse',
@@ -34,6 +35,7 @@ foreach my $inventory_class ( @inventory_classes ) {
my @fields;
push @fields, 'devicename',
+ 'title',
{ field => 'inventory_classnum',
type => 'select',
options => \@inventory_classnums,
diff --git a/httemplate/view/svc_phone.cgi b/httemplate/view/svc_phone.cgi
index aca4129..b71e094 100644
--- a/httemplate/view/svc_phone.cgi
+++ b/httemplate/view/svc_phone.cgi
@@ -146,7 +146,7 @@ my $html_foot = sub {
###
$e911.
- $devices.
+ #$devices.
$status.
join(' | ', @links ). '<BR>'.
join(' | ', @ilinks). '<BR>';