linking DIDs and users to PBXes, RT#7051
authorivan <ivan>
Mon, 25 Jan 2010 22:17:14 +0000 (22:17 +0000)
committerivan <ivan>
Mon, 25 Jan 2010 22:17:14 +0000 (22:17 +0000)
14 files changed:
FS/FS/Schema.pm
FS/FS/svc_Common.pm
FS/FS/svc_acct.pm
FS/FS/svc_pbx.pm
FS/FS/svc_phone.pm
httemplate/edit/elements/edit.html
httemplate/edit/elements/svc_Common.html
httemplate/edit/part_svc.cgi
httemplate/edit/svc_acct.cgi
httemplate/edit/svc_phone.cgi
httemplate/elements/select-svc_pbx.html [new file with mode: 0644]
httemplate/elements/tr-select-svc_pbx.html [new file with mode: 0644]
httemplate/view/svc_acct.cgi
httemplate/view/svc_phone.cgi

index 0ffc5bd..8d35c68 100644 (file)
@@ -1586,14 +1586,15 @@ sub tables_hashref {
         'downbytes_threshold',   'bigint', 'NULL',   '', '', '',
         'totalbytes','bigint', 'NULL',   '', '', '',
         'totalbytes_threshold',   'bigint', 'NULL',   '', '', '',
-        'domsvc',    'int', '',   '', '', '', 
+        'domsvc',    'int',     '', '', '', '', 
+        'pbxsvc',    'int', 'NULL', '', '', '',
         'last_login',  @date_type, '', '', 
         'last_logout', @date_type, '', '', 
       ],
       'primary_key' => 'svcnum',
       #'unique' => [ [ 'username', 'domsvc' ] ],
       'unique' => [],
-      'index' => [ ['username'], ['domsvc'] ],
+      'index' => [ ['username'], ['domsvc'], ['pbxsvc'] ],
     },
 
     'acct_rt_transaction' => {
@@ -2514,10 +2515,11 @@ sub tables_hashref {
         'pin',          'varchar', 'NULL', $char_d, '', '',
         'sip_password', 'varchar', 'NULL', $char_d, '', '',
         'phone_name',   'varchar', 'NULL', $char_d, '', '',
+        'pbxsvc',           'int', 'NULL',      '', '', '',
       ],
       'primary_key' => 'svcnum',
       'unique' => [],
-      'index'  => [ [ 'countrycode', 'phonenum' ] ],
+      'index'  => [ [ 'countrycode', 'phonenum' ], ['pbxsvc'] ],
     },
 
     'phone_device' => {
index a67504a..9b2cf7b 100644 (file)
@@ -586,6 +586,105 @@ sub part_svc {
 
 }
 
+=item svc_pbx
+
+Returns the FS::svc_pbx record for this service, if any (see L<FS::svc_pbx>).
+
+Only makes sense if the service has a pbxsvc field (currently, svc_phone and
+svc_acct).
+
+=cut
+
+# XXX FS::h_svc_{acct,phone} could have a history-aware svc_pbx override
+
+sub svc_pbx {
+  my $self = shift;
+  return '' unless $self->pbxsvc;
+  qsearchs( 'svc_pbx', { 'svcnum' => $self->pbxsvc } );
+}
+
+=item pbx_title
+
+Returns the title of the FS::svc_pbx record associated with this service, if
+any.
+
+Only makes sense if the service has a pbxsvc field (currently, svc_phone and
+svc_acct).
+
+=cut
+
+sub pbx_title {
+  my $self = shift;
+  my $svc_pbx = $self->svc_pbx or return '';
+  $svc_pbx->title;
+}
+
+=item pbx_select_hash %OPTIONS
+
+Can be called as an object method or a class method.
+
+Returns a hash SVCNUM => TITLE ...  representing the PBXes this customer
+that may be associated with this service.
+
+Currently available options are: I<pkgnum> I<svcpart>
+
+Only makes sense if the service has a pbxsvc field (currently, svc_phone and
+svc_acct).
+
+=cut
+
+#false laziness w/svc_acct::domain_select_hash
+sub pbx_select_hash {
+  my ($self, %options) = @_;
+  my %pbxes = ();
+  my $part_svc;
+  my $cust_pkg;
+
+  if (ref($self)) {
+    $part_svc = $self->part_svc;
+    $cust_pkg = $self->cust_svc->cust_pkg
+      if $self->cust_svc;
+  }
+
+  $part_svc = qsearchs('part_svc', { 'svcpart' => $options{svcpart} })
+    if $options{'svcpart'};
+
+  $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $options{pkgnum} })
+    if $options{'pkgnum'};
+
+  if ($part_svc && ( $part_svc->part_svc_column('pbxsvc')->columnflag eq 'S'
+                  || $part_svc->part_svc_column('pbxsvc')->columnflag eq 'F')) {
+    %pbxes = map { $_->svcnum => $_->title }
+             map { qsearchs('svc_pbx', { 'svcnum' => $_ }) }
+             split(',', $part_svc->part_svc_column('pbxsvc')->columnvalue);
+  } elsif ($cust_pkg) { # && !$conf->exists('svc_acct-alldomains') ) {
+    %pbxes = map { $_->svcnum => $_->title }
+             map { qsearchs('svc_pbx', { 'svcnum' => $_->svcnum }) }
+             map { qsearch('cust_svc', { 'pkgnum' => $_->pkgnum } ) }
+             qsearch('cust_pkg', { 'custnum' => $cust_pkg->custnum });
+  } else {
+    #XXX agent-virt
+    warn "hi";
+    %pbxes = map { $_->svcnum => $_->title } qsearch('svc_pbx', {} );
+    warn %pbxes;
+  }
+
+  if ($part_svc && $part_svc->part_svc_column('pbxsvc')->columnflag eq 'D') {
+    my $svc_pbx = qsearchs('svc_pbx',
+      { 'svcnum' => $part_svc->part_svc_column('pbxsvc')->columnvalue } );
+    if ( $svc_pbx ) {
+      $pbxes{$svc_pbx->svcnum}  = $svc_pbx->title;
+    } else {
+      warn "unknown svc_pbx.svcnum for part_svc_column pbxsvc: ".
+           $part_svc->part_svc_column('pbxsvc')->columnvalue;
+
+    }
+  }
+
+  (%pbxes);
+
+}
+
 =item set_auto_inventory
 
 Sets any fields which auto-populate from inventory (see L<FS::part_svc>).
index e5fd582..ca33aa8 100644 (file)
@@ -38,6 +38,7 @@ use FS::part_svc;
 use FS::svc_acct_pop;
 use FS::cust_main_invoice;
 use FS::svc_domain;
+use FS::svc_pbx;
 use FS::raddb;
 use FS::queue;
 use FS::radius_usergroup;
@@ -161,45 +162,71 @@ FS::svc_Common.  The following fields are currently supported:
 
 =over 4
 
-=item svcnum - primary key (assigned automatcially for new accounts)
+=item svcnum
+
+Primary key (assigned automatcially for new accounts)
 
 =item username
 
-=item _password - generated if blank
+=item _password
+
+generated if blank
+
+=item _password_encoding
+
+plain, crypt, ldap (or empty for autodetection)
 
-=item _password_encoding - plain, crypt, ldap (or empty for autodetection)
+=item sec_phrase
 
-=item sec_phrase - security phrase
+security phrase
 
-=item popnum - Point of presence (see L<FS::svc_acct_pop>)
+=item popnum
+
+Point of presence (see L<FS::svc_acct_pop>)
 
 =item uid
 
 =item gid
 
-=item finger - GECOS
+=item finger
+
+GECOS
+
+=item dir
 
-=item dir - set automatically if blank (and uid is not)
+set automatically if blank (and uid is not)
 
 =item shell
 
-=item quota - (unimplementd)
+=item quota
 
-=item slipip - IP address
+=item slipip
 
-=item seconds - 
+IP address
+
+=item seconds
+
+=item upbytes
+
+=item downbyte
+
+=item totalbytes
+
+=item domsvc
 
-=item upbytes - 
+svcnum from svc_domain
 
-=item downbytes - 
+=item pbxsvc
 
-=item totalbytes - 
+Optional svcnum from svc_pbx
 
-=item domsvc - svcnum from svc_domain
+=item radius_I<Radius_Attribute>
 
-=item radius_I<Radius_Attribute> - I<Radius-Attribute> (reply)
+I<Radius-Attribute> (reply)
 
-=item rc_I<Radius_Attribute> - I<Radius-Attribute> (check)
+=item rc_I<Radius_Attribute>
+
+I<Radius-Attribute> (check)
 
 =back
 
@@ -275,6 +302,20 @@ sub table_info {
                          disable_inventory => 1,
 
                        },
+        'domsvc'    => {
+                         label     => 'Domain',
+                         type      => 'select',
+                         select_table => 'svc_domain',
+                         select_key   => 'svcnum',
+                         select_label => 'domain',
+                         disable_inventory => 1,
+
+                       },
+        'pbxsvc'    => { label => 'PBX',
+                         type  => 'select-svc_pbx.html',
+                         disable_inventory => 1,
+                         disable_select => 1, #UI wonky, pry works otherwise
+                       },
         'usergroup' => {
                          label => 'RADIUS groups',
                          type  => 'radius_usergroup_selector',
@@ -1011,7 +1052,8 @@ sub check {
 
   my $error = $self->ut_numbern('svcnum')
               #|| $self->ut_number('domsvc')
-              || $self->ut_foreign_key('domsvc', 'svc_domain', 'svcnum' )
+              || $self->ut_foreign_key( 'domsvc', 'svc_domain', 'svcnum' )
+              || $self->ut_foreign_keyn('pbxsvc', 'svc_pbx',    'svcnum' )
               || $self->ut_textn('sec_phrase')
               || $self->ut_snumbern('seconds')
               || $self->ut_snumbern('upbytes')
index f4ecc04..49509b5 100644 (file)
@@ -37,7 +37,21 @@ FS::svc_Common.  The following fields are currently supported:
 
 =over 4
 
-=item field - description
+=item svcnum
+
+Primary key (assigned automatcially for new accounts)
+
+=item id
+
+(Unique?) number of external record
+
+=item title
+
+PBX name
+
+=item max_extensions
+
+Maximum number of extensions
 
 =back
 
index 88582d3..071b807 100644 (file)
@@ -8,6 +8,7 @@ use FS::Msgcat qw(gettext);
 use FS::svc_Common;
 use FS::part_svc;
 use FS::phone_device;
+use FS::svc_pbx;
 
 @ISA = qw( FS::svc_Common );
 
@@ -67,6 +68,10 @@ Voicemail PIN
 
 =item phone_name
 
+=item pbxsvc
+
+Optional svcnum from svc_pbx
+
 =back
 
 =head1 METHODS
@@ -104,6 +109,11 @@ sub table_info {
                           },
         'sip_password' => 'SIP password',
         'phone_name'   => 'Name',
+        'pbxsvc'       => { label => 'PBX',
+                            type  => 'select-svc_pbx.html',
+                            disable_inventory => 1,
+                            disable_select => 1, #UI wonky, pry works otherwise
+                          },
     },
   };
 }
@@ -258,6 +268,7 @@ sub check {
     || $self->ut_anything('sip_password')
     || $self->ut_numbern('pin')
     || $self->ut_textn('phone_name')
+    || $self->ut_foreign_keyn('pbxsvc', 'svc_pbx', 'svcnum' )
   ;
   return $error if $error;
 
index 43b7afe..4fe32c1 100644 (file)
@@ -40,6 +40,10 @@ Example:
                     'disabled' => 0,
                     'onchange' => 'javascript_function',
 
+                    'include_opt_callback' => sub { #my $ = @_;
+                                                    ( 'option' => 'value', );
+                                                  },
+
                     'm2name_table'   => 'table_name',
                     'm2name_namecol' => 'name_column',
                     #OR#
@@ -294,6 +298,10 @@ Example:
 %     $include_common{'colspan'} = $f->{colspan} if $f->{colspan};
 %   }
 %
+%   if ( $f->{include_opt_callback} ) {
+%     %include_common = ( %include_common, &{ $f->{include_opt_callback} } );
+%   }
+%
 %   my $layer_prefix_on = '';
 %
 %   my $include_sub = sub {
index ef04bd0..31d4739 100644 (file)
@@ -54,6 +54,7 @@
 
                  'field_callback' => sub {
                    my ($cgi, $object, $f) = @_;
+
                    my $columndef = $part_svc->part_svc_column($f->{'field'});
                    my $flag = $columndef->columnflag;
                    if ( $flag eq 'F' ) {
                                       : 'hidden';
                      $f->{'value'} = $columndef->columnvalue;
                    }
+
+                   if ( $f->{'type'} eq 'select-svc_pbx' ) {
+                     $f->{'include_opt_callback'} =
+                       sub { ( 'pkgnum'  => $pkgnum,
+                               'svcpart' => $svcpart,
+                             );
+                           };
+                   }
+
                  },
 
                  'html_init' => sub {
index 20bc277..d74e0f2 100755 (executable)
@@ -289,6 +289,14 @@ that field.
 %          } #endif
 %          $html .= '</SELECT>';
 %
+%        } elsif ( $def->{type} eq 'select-svc_pbx.html' ) {
+%
+%          $html .= include('/elements/select-svc_pbx.html',
+%                             'element_name' => "${layer}__${field}",
+%                             'element_etc'  => $disabled,
+%                             'multiple'     => ($flag eq 'S'),
+%                          );
+%
 %        } elsif ( $def->{type} eq 'radius_usergroup_selector' ) {
 %
 %          #XXX disable the RADIUS usergroup selector?  ugh it sure does need
@@ -304,7 +312,7 @@ that field.
 %
 %        } else {
 %
-%          $html .= '<font color="#ff0000">unknown type'. $def->{type};
+%          $html .= '<font color="#ff0000">unknown type '. $def->{type};
 %
 %        }
 %
index afbd002..2845c83 100755 (executable)
@@ -122,7 +122,14 @@ Service # <% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR>
     </TD>
   </TR>
 % } 
-%
+
+<% include('/elements/tr-select-svc_pbx.html',
+             'curr_value' => $svc_acct->pbxsvc,
+             'part_svc'   => $part_svc,
+             'cust_pkg'   => $cust_pkg,
+          )
+%>
+
 %#pop
 %my $popnum = $svc_acct->popnum || 0;
 %if ( $part_svc->part_svc_column('popnum')->columnflag eq 'F' ) {
index d7629ab..55ee890 100644 (file)
@@ -6,6 +6,10 @@
                                  type  => 'select-did',
                                  label => 'Phone number',
                                },
+                               { field => 'pbxsvc',
+                                 type  => 'select-svc_pbx',
+                                 label => 'PBX',
+                               },
                                'sip_password',
                                'pin',
                                'phone_name',
@@ -16,6 +20,7 @@
                                'sip_password' => 'SIP password',
                                'pin'          => 'Voicemail PIN',
                                'phone_name'   => 'Name',
+                               'pbxsvc'       => 'PBX',
                              },
            )
 %>
diff --git a/httemplate/elements/select-svc_pbx.html b/httemplate/elements/select-svc_pbx.html
new file mode 100644 (file)
index 0000000..19bce96
--- /dev/null
@@ -0,0 +1,57 @@
+<SELECT <% $opt{'multiple'} ? 'MULTIPLE' : 'SIZE=1' %>
+        NAME = "<% $opt{'element_name'} || $opt{'field'} || 'pbxsvc' %>"
+        <% $opt{'element_etc'} %>
+>
+
+% unless ( $opt{'multiple'} || $opt{'disable_empty'} ) {
+    <OPTION VALUE=""><% $opt{'empty_label'} || '' %>
+% }
+
+% foreach my $svcnum (
+%                      sort { $svc_pbx{$a} cmp $svc_pbx{$b} }
+%                      keys %svc_pbx
+%                    ) {
+%   my $svc_pbx = $svc_pbx{$svcnum};
+%   my $selected = ($svcnum == $pbxsvc) ? ' SELECTED' : '';
+
+    <OPTION VALUE="<% $svcnum %>" <% $selected %>><% $svc_pbx{$svcnum} %>
+
+% } 
+
+</SELECT>
+<%init>
+
+# false laziness w/select-svc_acct-domain.html
+
+my %opt = @_;
+
+my $pbxsvc = $opt{'curr_value'};
+my $part_svc = $opt{'part_svc'}
+               || qsearchs('part_svc', { 'svcpart' => $opt{'svcpart'} });
+my $svcpart = $part_svc ? $part_svc->svcpart : '';
+
+#optional
+my $cust_pkg = $opt{'cust_pkg'};
+$cust_pkg ||= qsearchs('cust_pkg', { 'pkgnum' => $opt{'pkgnum'} })
+  if $opt{'pkgnum'};
+
+my $pkgnum = $cust_pkg ? $cust_pkg->pkgnum : '';
+
+my %svc_pbx = ();
+
+if ( $pbxsvc ) {
+  my $svc_pbx = qsearchs('svc_pbx', { 'svcnum' => $pbxsvc } );
+  if ( $svc_pbx ) {
+    $svc_pbx{$svc_pbx->svcnum} = $svc_pbx;
+  } else {
+    warn "unknown svc_pbx.svcnum for svc_acct.pbxsvc: $pbxsvc";
+  }
+}
+
+%svc_pbx = (
+  %svc_pbx,
+  FS::svc_Common->pbx_select_hash( 'svcpart' => $svcpart,
+                                   'pkgnum'  => $pkgnum,
+                                 )
+);
+</%init>
diff --git a/httemplate/elements/tr-select-svc_pbx.html b/httemplate/elements/tr-select-svc_pbx.html
new file mode 100644 (file)
index 0000000..b02bd65
--- /dev/null
@@ -0,0 +1,60 @@
+%if ( $columnflag eq 'F' || !keys(%svc_pbx) ) {
+  <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'pbxsvc' %>" VALUE="<% $pbxsvc %>">
+% } else { 
+
+  <TR>
+    <TD ALIGN="right"><% $opt{'label'} || 'PBX' %></TD>
+    <TD>
+      <% include('/elements/select-svc_pbx.html',
+                   'curr_value' => $pbxsvc,
+                   'part_svc'   => $part_svc,
+                   'cust_pkg'   => $cust_pkg,
+                )
+      %>
+    </TD>
+  </TR>
+% } 
+<%init>
+
+# false laziness w/tr-select-svc_acct-domain.html
+
+my %opt = @_;
+
+my $pbxsvc = $opt{'curr_value'};
+
+#required
+my $part_svc = $opt{'part_svc'}
+               || qsearchs('part_svc', { 'svcpart' => $opt{'svcpart'} });
+my $svcpart =
+  $part_svc ? $part_svc->svcpart : '';
+my $columnflag =
+  $part_svc ? $part_svc->part_svc_column('pbxsvc')->columnflag : '';
+
+#optional
+my $cust_pkg = $opt{'cust_pkg'};
+$cust_pkg ||= qsearchs('cust_pkg', { 'pkgnum' => $opt{'pkgnum'} })
+  if $opt{'pkgnum'};
+
+# false laziness w/select-svc_pbx.html
+
+my $pkgnum = $cust_pkg ? $cust_pkg->pkgnum : '';
+
+my %svc_pbx = ();
+
+if ( $pbxsvc ) {
+  my $svc_pbx = qsearchs('svc_pbx', { 'svcnum' => $pbxsvc } );
+  if ( $svc_pbx ) {
+    $svc_pbx{$svc_pbx->svcnum} = $svc_pbx;
+  } else {
+    warn "unknown svc_pbx.svcnum for svc_acct.pbxsvc: $pbxsvc";
+  }
+}
+
+%svc_pbx = (
+  %svc_pbx,
+  FS::svc_Common->pbx_select_hash( 'svcpart' => $svcpart,
+                                   'pkgnum'  => $pkgnum,
+                                 )
+);
+
+</%init>
index 44a2aa6..7e94df1 100755 (executable)
@@ -157,6 +157,13 @@ Service #<B><% $svcnum %></B>
   <TD BGCOLOR="#ffffff"><% $domain %></TD>
 </TR>
 
+% if ( $svc_acct->pbxsvc ) {
+    <TR>
+      <TD ALIGN="right">PBX</TD>
+      <TD BGCOLOR="#ffffff"><% $svc_acct->pbx_title %></TD>
+    </TR>
+%}
+
 <TR>
   <TD ALIGN="right">Password</TD>
   <TD BGCOLOR="#ffffff">
index c5fce62..59ee2d5 100644 (file)
@@ -3,6 +3,7 @@
               'fields'    => [qw(
                                   countrycode
                                   phonenum
+                                  pbx_title
                                   sip_password
                                   pin
                                   phone_name
@@ -10,6 +11,7 @@
               'labels'    => {
                                'countrycode'  => 'Country code',
                                'phonenum'     => 'Phone number',
+                               'pbx_title'    => 'PBX',
                                'sip_password' => 'SIP password',
                                'pin'          => 'PIN',
                                'phone_name'   => 'Name',