package contacts / "name per packages", RT#22185
authorIvan Kohler <ivan@freeside.biz>
Mon, 15 Apr 2013 00:08:36 +0000 (17:08 -0700)
committerIvan Kohler <ivan@freeside.biz>
Mon, 15 Apr 2013 00:08:36 +0000 (17:08 -0700)
15 files changed:
FS/FS/Schema.pm
FS/FS/contact_Mixin.pm [new file with mode: 0644]
FS/FS/cust_main/Packages.pm
FS/FS/cust_pkg.pm
FS/MANIFEST
FS/t/contact_Mixin.t [new file with mode: 0644]
httemplate/elements/contact.html
httemplate/elements/tr-select-contact.html [new file with mode: 0644]
httemplate/misc/order_pkg.html
httemplate/view/cust_main/packages.html
httemplate/view/cust_main/packages/contact.html [new file with mode: 0644]
httemplate/view/cust_main/packages/location.html
httemplate/view/cust_main/packages/package.html
httemplate/view/cust_main/packages/section.html
httemplate/view/cust_main/packages/status.html

index 924d0d9..bbf3b42 100644 (file)
@@ -1721,6 +1721,7 @@ sub tables_hashref {
         'custnum',             'int',     '', '', '', '', 
         'pkgpart',             'int',     '', '', '', '', 
         'pkgbatch',        'varchar', 'NULL', $char_d, '', '',
+        'contactnum',          'int', 'NULL', '', '', '', 
         'locationnum',         'int', 'NULL', '', '', '',
         'otaker',          'varchar', 'NULL', 32, '', '', 
         'usernum',             'int', 'NULL', '', '', '',
diff --git a/FS/FS/contact_Mixin.pm b/FS/FS/contact_Mixin.pm
new file mode 100644 (file)
index 0000000..33cd350
--- /dev/null
@@ -0,0 +1,19 @@
+package FS::contact_Mixin;
+
+use strict;
+use FS::Record qw( qsearchs );
+use FS::contact;
+
+=item contact
+
+Returns the contact object, if any (see L<FS::contact>).
+
+=cut
+
+sub contact {
+  my $self = shift;
+  return '' unless $self->contactnum;
+  qsearchs( 'contact', { 'contactnum' => $self->contactnum } );
+}
+
+1;
index 29e3bec..f83bce9 100644 (file)
@@ -100,6 +100,28 @@ sub order_pkg {
   local $FS::UID::AutoCommit = 0;
   my $dbh = dbh;
 
+  if ( $opt->{'contactnum'} and $opt->{'contactnum'} != -1 ) {
+
+    $cust_pkg->contactnum($opt->{'contactnum'});
+
+  } elsif ( $opt->{'contact'} ) {
+
+    if ( ! $opt->{'contact'}->contactnum ) {
+      # not inserted yet
+      my $error = $opt->{'contact'}->insert;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "inserting contact (transaction rolled back): $error";
+      }
+    }
+    $cust_pkg->contactnum($opt->{'contact'}->contactnum);
+
+  #} else {
+  #
+  #  $cust_pkg->contactnum();
+
+  }
+
   if ( $opt->{'locationnum'} and $opt->{'locationnum'} != -1 ) {
 
     $cust_pkg->locationnum($opt->{'locationnum'});
index b2cb413..bb1b4e3 100644 (file)
@@ -1,7 +1,8 @@
 package FS::cust_pkg;
 
 use strict;
-use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::location_Mixin
+use base qw( FS::otaker_Mixin FS::cust_main_Mixin
+             FS::contact_Mixin FS::location_Mixin
              FS::m2m_Common FS::option_Common );
 use vars qw($disable_agentcheck $DEBUG $me);
 use Carp qw(cluck);
@@ -17,6 +18,7 @@ use FS::CurrentUser;
 use FS::cust_svc;
 use FS::part_pkg;
 use FS::cust_main;
+use FS::contact;
 use FS::cust_location;
 use FS::pkg_svc;
 use FS::cust_bill_pkg;
@@ -620,6 +622,7 @@ sub check {
     $self->ut_numbern('pkgnum')
     || $self->ut_foreign_key('custnum', 'cust_main', 'custnum')
     || $self->ut_numbern('pkgpart')
+    || $self->ut_foreign_keyn('contactnum',  'contact',       'contactnum' )
     || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum')
     || $self->ut_numbern('start_date')
     || $self->ut_numbern('setup')
index da4832d..9423290 100644 (file)
@@ -490,6 +490,8 @@ FS/phone_type.pm
 t/phone_type.t
 FS/contact_email.pm
 t/contact_email.t
+FS/contact_Mixin.pm
+t/contact_Mixin.t
 FS/prospect_main.pm
 t/prospect_main.t
 FS/o2m_Common.pm
diff --git a/FS/t/contact_Mixin.t b/FS/t/contact_Mixin.t
new file mode 100644 (file)
index 0000000..89dcc37
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::contact_Mixin;
+$loaded=1;
+print "ok 1\n";
index 490ba23..3d51776 100644 (file)
@@ -2,9 +2,9 @@
 
   <INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
 
-  <TABLE>
+  <TABLE STYLE="display:inline">
     <TR>
-%     if ( @contact_class ) {
+%     if ( @contact_class && ! $opt{name_only} ) {
         <TD>
           <SELECT NAME="<%$name%>_classnum" <% $onchange %>>
             <OPTION VALUE="">
@@ -106,6 +106,6 @@ foreach my $phone_type ( qsearch({table=>'phone_type', order_by=>'weight'}) ) {
 
 $label{'comment'} = 'Comment';
 
-my @fields = keys %label;
+my @fields = $opt{'name_only'} ? qw( first last ) : keys %label;
 
 </%init>
diff --git a/httemplate/elements/tr-select-contact.html b/httemplate/elements/tr-select-contact.html
new file mode 100644 (file)
index 0000000..5791178
--- /dev/null
@@ -0,0 +1,204 @@
+<%doc>
+
+Example:
+
+  include('/elements/tr-select-contact.html',
+            'cgi'       => $cgi,
+
+            'cust_main'     => $cust_main,
+            #or
+            'prospect_main' => $prospect_main,
+
+            #optional
+            'empty_label'   => '(default contact)',
+         )
+
+</%doc>
+
+<SCRIPT TYPE="text/javascript">
+
+  function contact_disable(what) {
+%   for (@contact_fields) { 
+      what.form.<%$_%>.disabled = true;
+      var ftype = what.form.<%$_%>.tagName;
+      if( ftype == 'SELECT') changeSelect(what.form.<%$_%>, '');
+      else what.form.<%$_%>.value = '';
+      if( ftype != 'SELECT') what.form.<%$_%>.style.backgroundColor = '#dddddd';
+%   } 
+  }
+
+  function contact_clear(what) {
+%   for (@contact_fields) { 
+      var ftype = what.form.<%$_%>.tagName;
+      if( ftype == 'INPUT' ) what.form.<%$_%>.value = '';
+%   }
+  }
+
+  function contact_enable(what) {
+%   for (@contact_fields) { 
+      what.form.<%$_%>.disabled = false;
+      var ftype = what.form.<%$_%>.tagName;
+      if( ftype != 'SELECT') what.form.<%$_%>.style.backgroundColor = '#ffffff';
+%   } 
+  }
+
+  function contactnum_changed(what) {
+    var contactnum = what.options[what.selectedIndex].value;
+    if ( contactnum == -1 ) { //Add new contact
+      contact_clear(what);
+
+      contact_enable(what);
+      return;
+    }
+
+%   if ( $editable ) {
+      if ( contactnum == 0 ) {
+%   }
+
+%       #sleep/wait until dropdowns are updated?
+        contact_disable(what);
+
+%   if ( $editable ) {
+      } else {
+
+%       #sleep/wait until dropdowns are updated?
+        contact_enable(what);
+
+      }
+%   }
+
+  }
+
+  function changeSelect(what, value) {
+    for ( var i=0; i<what.length; i++) {
+      if ( what.options[i].value == value ) {
+        what.selectedIndex = i;
+      }
+    }
+  }
+
+</SCRIPT>
+
+<TR>
+  <<%$th%> ALIGN="right" VALIGN="top"><% $opt{'label'} || emt('Service contact') %></<%$th%>>
+  <TD VALIGN="top" COLSPAN=7>
+    <SELECT NAME     = "contactnum"
+            ID       = "contactnum"
+            STYLE    = "vertical-align:top;margin:3px"
+            onchange = "contactnum_changed(this);"
+    >
+% if ( $cust_main ) {
+      <OPTION VALUE=""><% $opt{'empty_label'} || '(customer default)' |h %>
+% }
+%
+%     foreach my $contact ( @contact ) {
+        <OPTION VALUE="<% $contact->contactnum %>"
+                <% $contactnum == $contact->contactnum ? 'SELECTED' : '' %>
+        ><% $contact->line |h %>
+%     }
+%     if ( $addnew ) {
+        <OPTION VALUE="-1"
+                <% $contactnum == -1 ? 'SELECTED' : '' %>
+        >New contact
+%     }
+    </SELECT>
+
+<% include('/elements/contact.html',
+             'object'       => $contact,
+             #'onchange' ?  probably not
+             'disabled'     => $disabled,
+             'name_only'    => 1,
+          )
+%>
+
+  </TD>
+</TR>
+
+<SCRIPT TYPE="text/javascript">
+  contactnum_changed(document.getElementById('contactnum'));
+</SCRIPT>
+<%init>
+
+#based on / kinda false laziness w/tr-select-cust_contact.html
+
+my $conf = new FS::Conf;
+
+my %opt = @_;
+my $cgi           = $opt{'cgi'};
+my $cust_pkg      = $opt{'cust_pkg'};
+my $cust_main     = $opt{'cust_main'};
+my $prospect_main = $opt{'prospect_main'};
+die "cust_main or prospect_main required" unless $cust_main or $prospect_main;
+
+my $contactnum = '';
+if ( $cgi->param('error') ) {
+  $cgi->param('contactnum') =~ /^(\-?\d*)$/ or die "illegal contactnum";
+  $contactnum = $1;
+} else {
+  if ( length($opt{'curr_value'}) ) {
+    $contactnum = $opt{'curr_value'};
+  } elsif ($prospect_main) {
+    my @cust_contact = $prospect_main->cust_contact;
+    $contactnum = $cust_contact[0]->contactnum if scalar(@cust_contact)==1;
+  } else { #$cust_main
+    $cgi->param('contactnum') =~ /^(\-?\d*)$/ or die "illegal contactnum";
+    $contactnum = $1;
+  }
+}
+
+##probably could use explicit controls
+#my $editable = $cust_main ? 0 : 1; #could use explicit control
+my $editable = 0;
+my $addnew = $cust_main ? 1 : ( $contactnum>0 ? 0 : 1 );
+
+my @contact_fields = map "contactnum_$_", qw( first last );
+
+my $contact; #the one that shows by default in the contact edit space
+if ( $contactnum && $contactnum > 0 ) {
+  $contact = qsearchs('contact', { 'contactnum' => $contactnum } )
+    or die "unknown contactnum";
+} else {
+  $contact = new FS::contact;
+  if ( $contactnum == -1 ) {
+    $contact->$_( $cgi->param($_) ) foreach @contact_fields; #XXX
+  } elsif ( $cust_pkg && $cust_pkg->contactnum ) {
+    my $pkg_contact = $cust_pkg->contact;
+    $contact->$_( $pkg_contact->$_ ) foreach @contact_fields; #XXX why are we making a new one gagain??
+    $opt{'empty_label'} ||= 'package contact: '.$pkg_contact->line;
+  } elsif ( $cust_main ) {
+    $contact = new FS::contact; #I think
+  }
+}
+
+my $contact_sort = sub {
+     lc($a->last)  cmp lc($b->last)
+  or lc($a->first) cmp lc($b->first)
+};
+
+my @contact;
+push @contact, $cust_main->cust_contact if $cust_main;
+push @contact, $prospect_main->contact if $prospect_main;
+push @contact, $contact
+  if !$cust_main && $contact && $contact->contactnum > 0
+  && ! grep { $_->contactnum == $contact->contactnum } @contact;
+
+@contact = sort $contact_sort grep !$_->disabled, @contact;
+
+$contact = $contact[0]
+  if ( $prospect_main )
+  && !$opt{'is_optional'}
+  && @contact;
+
+my $disabled =
+  ( $contactnum < 0
+    || ( $editable && $contactnum )
+    || ( $prospect_main
+         && !$opt{'is_optional'} && !@contact && $addnew
+       )
+  )
+    ? ''
+    : 'DISABLED';
+
+my $th = $opt{'no_bold'} ? 'TD' : 'TH';
+
+</%init>
index 993ea36..e09ba98 100644 (file)
   &>
 % }
 
+<& /elements/tr-select-contact.html,
+             'cgi'           => $cgi,
+             'cust_main'     => $cust_main,
+             'prospect_main' => $prospect_main,
+&>
+
 % if ( $cgi->param('lock_locationnum') ) {
 
     <INPUT TYPE  = "hidden"
index 24a12cc..546dd89 100755 (executable)
@@ -101,7 +101,7 @@ table.usage {
 
   <TR>
     <TD COLSPAN=2>
-% if ( $conf->exists('cust_pkg-group_by_location') and $show_location ) {
+% if ( $conf->exists('cust_pkg-group_by_location') ) {
 <& locations.html,
     'cust_main'     => $cust_main,
     'packages'      => $packages,
@@ -113,7 +113,6 @@ table.usage {
 <& packages/section.html,
     'cust_main'     => $cust_main,
     'packages'      => $packages,
-    'show_location' => $show_location,
  &>
 </TABLE>
 % }
@@ -140,10 +139,6 @@ my $curuser = $FS::CurrentUser::CurrentUser;
 
 my( $packages, $num_old_packages ) = get_packages($cust_main, $conf);
 
-
-my $show_location = $conf->exists('cust_pkg-always_show_location') 
-  || (grep $_->locationnum ne $cust_main->ship_locationnum, @$packages);
-
 my $countrydefault = scalar($conf->config('countrydefault')) || 'US';
 #subroutines
 
diff --git a/httemplate/view/cust_main/packages/contact.html b/httemplate/view/cust_main/packages/contact.html
new file mode 100644 (file)
index 0000000..a6f8a42
--- /dev/null
@@ -0,0 +1,48 @@
+% if ( $contact ) {
+    <% $contact->line |h %>
+% }
+
+% if ( ! $cust_pkg->get('cancel')
+%      && $FS::CurrentUser::CurrentUser->access_right('Change customer package')
+%    )
+% {
+  <FONT SIZE=-1>
+%#XXX    (&nbsp;<%pkg_change_contact_link($cust_pkg)%>&nbsp;)
+%#  %   if ( $cust_pkg->contactnum ) {
+%#          (&nbsp;<%edit_contact_link($cust_pkg->contactnum)%>&nbsp;)
+%#  %   }
+  </FONT>
+% } 
+
+</TD>
+<%init>
+
+my $conf = new FS::Conf;
+my %opt = @_;
+
+my $cust_pkg       = $opt{'cust_pkg'};
+
+my $contact = $cust_pkg->contact;
+
+sub pkg_change_contact_link {
+  my $cust_pkg = shift;
+  my $pkgpart = $cust_pkg->pkgpart;
+  include( '/elements/popup_link-cust_pkg.html',
+#XXX
+    'action'      => $p. "misc/change_pkg.cgi?contactnum=-1",
+    'label'       => emt('Change contact'),
+    'actionlabel' => emt('Change'),
+    'cust_pkg'    => $cust_pkg,
+  );
+}
+
+sub edit_contact_link {
+  my $contactnum = shift;
+  include( '/elements/popup_link.html',
+    'action'      => $p. "edit/cust_contact.cgi?contactnum=$contactnum",
+    'label'       => emt('Edit contact'),
+    'actionlabel' => emt('Edit'),
+  );
+}
+
+</%init>
index 9569485..f2d3798 100644 (file)
@@ -1,5 +1,3 @@
-<TD CLASS="inv" BGCOLOR="<% $bgcolor %>" WIDTH="20%">
-
 % if ( $default ) {
   <DIV STYLE="font-style: italic; font-size: small">
 % }
   </FONT>
 % } 
 
-</TD>
 <%init>
 
 my $conf = new FS::Conf;
 my %opt = @_;
 
-my $bgcolor        = $opt{'bgcolor'};
 my $cust_pkg       = $opt{'cust_pkg'};
 my $countrydefault = $opt{'countrydefault'} || 'US';
 my $statedefault   = $opt{'statedefault'}
index 0b72d19..520305a 100644 (file)
 %           !$supplemental and
 %           $part_pkg->freq ne '0' ) {
       <TR>
-%       if ( !$opt{'show_location'} ) {
-        <TD><FONT SIZE="-1">
-          (&nbsp;<% pkg_change_location_link($cust_pkg) %>&nbsp;)
-        </FONT></TD>
-%       }
 %       if ( FS::Conf->new->exists('invoice-unitprice') ) {
         <TD><FONT SIZE="-1">
           (&nbsp;<% pkg_change_quantity_link($cust_pkg) %>&nbsp;)
index 8ea7a7d..5f54c0a 100755 (executable)
@@ -3,9 +3,7 @@
 % #my $width = $show_location ? 'WIDTH="25%"' : 'WIDTH="33%"';
   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Package') |h %></TH>
   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Status') |h %></TH>
-%   if ( $show_location ) {
-  <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Location') |h %></TH>
-% }
+  <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Contact/Location') |h %></TH>
   <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Services') |h %></TH>
 </TR>
 
@@ -13,6 +11,7 @@
 %   foreach my $cust_pkg (@$packages) {
     <& .packagerow, $cust_pkg,
         'cust_main' => $opt{'cust_main'},
+        'bgcolor'   => $opt{'bgcolor'},
         %conf_opt
     &>
 %   }
   <!--pkgnum: <% $cust_pkg->pkgnum %>-->
   <TR CLASS="row<%$row % 2%>">
     <& package.html, %iopt &>
-    <& status.html, %iopt &>
-%     if ( $iopt{'show_location'} ) {
-    <& location.html, %iopt &>
-%     }
+    <& status.html,  %iopt &>
+    <TD CLASS="inv" BGCOLOR="<% $iopt{bgcolor} %>" WIDTH="20%" VALIGN="top">
+      <& contact.html, %iopt &>
+      <& location.html, %iopt &>
+    </TD>
     <& services.html, %iopt &>
   </TR>
 % $row++;
@@ -51,7 +51,6 @@ my $conf = new FS::Conf;
 my $curuser = $FS::CurrentUser::CurrentUser;
 
 my $packages = $opt{'packages'};
-my $show_location = $opt{'show_location'};
 
 # Sort order is hardcoded for now, can change this if needed.
 @$packages = sort { 
@@ -86,8 +85,6 @@ my %conf_opt = (
   'maestro-status_test'       => $conf->exists('maestro-status_test'),
   'cust_pkg-large_pkg_size'   => scalar($conf->config('cust_pkg-large_pkg_size')),
 
-  # for packages.html Change location link
-  'show_location'             => $show_location,
 );
 
 </%init>
index 6be0296..9d5a88e 100644 (file)
@@ -1,4 +1,4 @@
-<TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+<TD CLASS="inv" BGCOLOR="<% $bgcolor %>" VALIGN="top">
   <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
 
 %#this should use cust_pkg->status and cust_pkg->statuscolor eventually