better display/edit of contacts on customer view, RT#16819
authorIvan Kohler <ivan@freeside.biz>
Sun, 25 Mar 2012 01:47:07 +0000 (18:47 -0700)
committerMark Wells <mark@freeside.biz>
Tue, 27 Mar 2012 06:33:18 +0000 (23:33 -0700)
FS/FS/cust_main.pm
httemplate/edit/cust_main-contacts.html [new file with mode: 0644]
httemplate/edit/process/cust_main-contacts.html [new file with mode: 0644]
httemplate/edit/process/elements/process.html
httemplate/elements/freeside.css
httemplate/view/cust_main.cgi
httemplate/view/cust_main/billing.html
httemplate/view/cust_main/contacts.html
httemplate/view/cust_main/contacts_new.html
httemplate/view/cust_main/misc.html

index 069d5b6..7d1a156 100644 (file)
@@ -8,6 +8,7 @@ use base qw( FS::cust_main::Packages FS::cust_main::Status
              FS::cust_main::Billing_Discount
              FS::otaker_Mixin FS::payinfo_Mixin FS::cust_main_Mixin
              FS::geocode_Mixin
+             FS::o2m_Common
              FS::Record
            );
 use vars qw( $DEBUG $me $conf
diff --git a/httemplate/edit/cust_main-contacts.html b/httemplate/edit/cust_main-contacts.html
new file mode 100644 (file)
index 0000000..bae58bd
--- /dev/null
@@ -0,0 +1,100 @@
+<% include('elements/edit.html',
+     'name_singular'   => 'customer contacts', #yes, we're editing all of them
+     'table'           => 'cust_main',
+     'post_url'       => popurl(1). 'process/cust_main-contacts.html',
+     'labels'          => { 'custnum'     => ' ', #XXX supress this line entirely, its being redundant
+                            'contactnum'  => 'Contact',
+                            #'locationnum' => '&nbsp;',
+                          },
+     'fields'          => [
+       { 'field'             => 'contactnum',
+         'type'              => 'contact',
+         'colspan'           => 6,
+         'm2m_method'        => 'cust_contact',
+         'm2m_dstcol'        => 'contactnum',   
+         'm2_label'          => 'Contact',
+         'm2_error_callback' => $m2_error_callback,
+       },
+     ],
+     #'new_callback'    => $new_callback,
+     #'edit_callback'   => $edit_callback,
+     #'error_callback'  => $error_callback,
+     'agent_virt'      => 1,
+     'menubar'          => [], #remove "view all" link
+
+     #XXX it would be nice if this could instead be after the error but before
+     # the table
+     'html_init'        => include('/elements/small_custview.html',
+                                     $custnum,
+                                     $conf->config('countrydefault') || 'US',
+                                     1, #no balance
+                                  ),
+   )
+%>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+my $conf = new FS::Conf;
+
+my $custnum;
+if ( $cgi->param('error') ) {
+  $custnum = scalar($cgi->param('custnum'));
+
+  die "access denied"
+    unless $curuser->access_right(($custnum ? 'Edit' : 'New'). ' customer'); #contacts?
+
+} elsif ( $cgi->keywords ) { #editing
+  $custnum = ($cgi->keywords)[0];
+
+  die "access denied"
+    unless $curuser->access_right('Edit customer');
+
+} else { #new customer
+
+  #this doesn't really work here, we're an edit only
+  die "guru meditation #32";
+
+  die "access denied"
+    unless $curuser->access_right('New customer');
+
+}
+
+#my $new_callback = sub {
+#  my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_;
+#};
+
+#my $edit_callback = sub {
+# my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_;
+#};
+
+#my $error_callback = sub {
+#  my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_;
+#};
+
+my $m2_error_callback = sub {
+  my($cgi, $object) = @_;
+
+  #process_o2m fields in process/cust_main-contacts.html
+  my @fields = qw( first last title comment );
+  my @gfields = ( '', map "_$_", @fields );
+
+  map {
+        if ( /^contactnum(\d+)$/ ) {
+          my $num = $1;
+          if ( grep $cgi->param("contactnum$num$_"), @gfields ) {
+            my $x = new FS::contact {
+              'contactnum' => scalar($cgi->param("contactnum$num")),
+              map { $_ => scalar($cgi->param("contactnum${num}_$_")) } @fields,
+            };
+            $x;
+          } else {
+            ();
+          }
+        } else {
+          ();
+        }
+      }
+      $cgi->param;
+};
+
+</%init>
diff --git a/httemplate/edit/process/cust_main-contacts.html b/httemplate/edit/process/cust_main-contacts.html
new file mode 100644 (file)
index 0000000..ed874a5
--- /dev/null
@@ -0,0 +1,20 @@
+<% include('elements/process.html',
+     'table'          => 'cust_main',
+     'error_redirect' => popurl(3). 'edit/cust_main-contacts.html?',
+     'agent_virt'     => 1,
+     'skip_process'   => 1, #we don't want to make any changes to cust_main
+     'process_o2m' => {
+       'table'  => 'contact',
+       'fields' => \@contact_fields,
+     },
+     'redirect' => popurl(3). 'view/cust_main.cgi?',
+   )
+%>
+<%init>
+
+my @contact_fields = qw( classnum first last title comment emailaddress );
+foreach my $phone_type ( qsearch({table=>'phone_type', order_by=>'weight'}) ) {
+  push @contact_fields, 'phonetypenum'.$phone_type->phonetypenum;
+}
+
+</%init>
index 636bae0..12b3bd9 100644 (file)
@@ -62,6 +62,10 @@ Example:
                       'fields' => [qw( fieldname fieldname2 )],
                     },
 
+   'skip_process' => 0, #boolean, if set true, will skip the main table
+                        #add/edit processing and only run any linked table
+                        #process_ items
+
    #checks CGI params and whatever else before much else runs
    #return an error string or empty for no error
    'precheck_callback' => sub { my( $cgi ) = @_; },
@@ -204,62 +208,71 @@ my $new;
 my $new_pkey = '';
 foreach my $value ( @values ) {
 
-  $new = $class->new( \%hash );
+  if ($opt{'skip_process'}) {
+
+    $new = $old;
+    $new_pkey = $old_pkey;
+
+  } else {
+
+    $new = $class->new( \%hash );
 
-  $new->$bfield($value) if $bfield;
+    $new->$bfield($value) if $bfield;
 
-  if ($old && exists($opt{'copy_on_empty'})) {
-    foreach my $field (@{$opt{'copy_on_empty'}}) {
-      $new->set($field, $old->get($field))
-        unless scalar($cgi->param($field));
+    if ($old && exists($opt{'copy_on_empty'})) {
+      foreach my $field (@{$opt{'copy_on_empty'}}) {
+        $new->set($field, $old->get($field))
+          unless scalar($cgi->param($field));
+      }
     }
-  }
 
-  if ( $opt{'agent_virt'} ) {
+    if ( $opt{'agent_virt'} ) {
 
-    if ( ! $new->agentnum
-         && (    ! $opt{'agent_null_right'}
-              || ! $curuser->access_right($opt{'agent_null_right'})
-            )
-       )
-    {
+      if ( ! $new->agentnum
+           && (    ! $opt{'agent_null_right'}
+                || ! $curuser->access_right($opt{'agent_null_right'})
+              )
+         )
+      {
 
-      $error ||= 'Select an agent';
+        $error ||= 'Select an agent';
 
-    } else {
+      } else {
 
-      die "illegal agentnum"
-        unless $curuser->agentnums_href->{$new->agentnum}
-            or $curuser->access_right('View customers of all agents')
-            or $opt{'agent_null_right'}
-               && ! $new->agentnum
-               && $curuser->access_right($opt{'agent_null_right'});
+        die "illegal agentnum"
+          unless $curuser->agentnums_href->{$new->agentnum}
+              or $curuser->access_right('View customers of all agents')
+              or $opt{'agent_null_right'}
+                 && ! $new->agentnum
+                 && $curuser->access_right($opt{'agent_null_right'});
 
-    }
+      }
 
-  }
+    }
 
-  $error ||= $new->check;
+    $error ||= $new->check;
 
-  my @args = ();
-  if ( !$error && $opt{'args_callback'} ) {
-    @args = &{ $opt{'args_callback'} }( $cgi, $new );
-  }
+    my @args = ();
+    if ( !$error && $opt{'args_callback'} ) {
+      @args = &{ $opt{'args_callback'} }( $cgi, $new );
+    }
 
-  if ( !$error && $opt{'debug'} ) {
-    warn "$me updating record in $table table using $class class\n";
-    warn Dumper(\%hash);
-    warn "with args: \n". Dumper(\@args) if @args;
-  }
+    if ( !$error && $opt{'debug'} ) {
+      warn "$me updating record in $table table using $class class\n";
+      warn Dumper(\%hash);
+      warn "with args: \n". Dumper(\@args) if @args;
+    }
 
-  if ( !$error ) {
-    if ( $old_pkey ) {
-      $error = $new->replace($old, @args);
-    } else {
-      $error = $new->insert(@args);
+    if ( !$error ) {
+      if ( $old_pkey ) {
+        $error = $new->replace($old, @args);
+      } else {
+        $error = $new->insert(@args);
+      }
+      $new_pkey = $new->getfield($pkey);
     }
-    $new_pkey = $new->getfield($pkey);
-  }
+
+  } #unless $opt{'skip_process'}
 
   if ( !$error && $opt{'process_m2m'} ) {
 
@@ -344,5 +357,4 @@ foreach my $value ( @values ) {
   last if $error;
 
 }
-
 </%init>
index 79c98cd..44a4a3c 100644 (file)
@@ -115,7 +115,7 @@ a.fstab {
          padding-right:12px;*/
          padding-left:4px;
          padding-right:4px;
-         font-size:16px;
+         font-size:18px;
          font-weight:bold;
          text-decoration:none;
          overflow:visible;
@@ -148,7 +148,7 @@ a.fstabselected {
          padding-right:12px;*/
          padding-left:4px;
          padding-right:4px;
-         font-size:16px;
+         font-size:18px;
          font-weight:bold;
          text-decoration:none;
          overflow:visible;
@@ -220,6 +220,32 @@ div.fstabcontainer {
   font-weight:bold;
 }
 
+.fsinnerbox {
+  background-color:#cccccc;
+  padding:2px;
+  -moz-box-shadow:  1px 1px 2px #666666;
+  -webkit-box-shadow:  1px 1px 2px #666666;
+  box-shadow: 1px 1px 2px #666666;
+  filter: progid:DXImageTransform.Microsoft.Shadow(color='#666666', Direction=135, Strength=2);
+}
+
+.fsinnerbox-title {
+  font-size:110%;
+  font-weight:bold;
+  background-color:#cccccc;
+  padding:2px;
+         -moz-border-radius-topleft:8px;
+         -moz-border-radius-topright:8px;
+         -webkit-border-radius-topleft:8px;
+         -webkit-border-radius-topright:8px;
+         border-radius-topleft:8px;
+         border-radius-topright:8px;
+  -moz-box-shadow:  1px 0px 1px #999999;
+  -webkit-box-shadow:  1px 0px 1px #999999;
+  box-shadow: 1px 0px 1px #999999;
+  filter: progid:DXImageTransform.Microsoft.Shadow(color='#999999', Direction=90, Strength=1);
+}
+
 .background {
   background-color:#f8f8f8;
 }
index dcadf99..fda4db0 100755 (executable)
@@ -150,6 +150,11 @@ function areyousure(href, message) {
 
   </TD>
 </TR>
+<TR>
+  <TD COLSPAN = 2>
+    <& cust_main/contacts_new.html, $cust_main &>
+  </TD>
+</TR>
 </TABLE>
 
 % }
index f1add6f..522c6db 100644 (file)
@@ -1,4 +1,4 @@
-<% mt('Billing information') |h %> 
+<FONT CLASS="fsinnerbox-title"><% mt('Billing information') |h %></FONT>
 %# If we can't see the unencrypted card, then bill now is an exercise in
 %# frustration (without some sort of job queue magic to send it to a secure
 %# machine, anyway)
@@ -6,14 +6,14 @@
 %      && ! $cust_main->is_encrypted($cust_main->payinfo)
 %   ) { 
 %#  (<A HREF="<% $p %>misc/bill.cgi?<% $cust_main->custnum %>"><% mt('Bill now') |h %></A>)
-  (<& /elements/bill.html,
-                custnum   => $cust_main->custnum,
-                label     => emt('Bill now'),
-                url       => $p.'view/cust_main.cgi?'.$cust_main->custnum,
-   &>)
+  <& /elements/bill.html,
+       custnum   => $cust_main->custnum,
+       label     => emt('Bill now'),
+       url       => $p.'view/cust_main.cgi?'.$cust_main->custnum,
+  &>
 % } 
 
-<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+<TABLE CLASS="fsinnerbox">
 
 %( my $balance = $cust_main->balance )
 %  =~ s/^(\-?)(.*)$/<FONT SIZE=+1>$1<\/FONT>$money_char$2/;
 % }
 
 
-</TABLE></TD></TR></TABLE>
+</TABLE>
 <%once>
 
 my $paystate_label = FS::Msgcat::_gettext('paystate');
index 68e3b17..b3e52b5 100644 (file)
@@ -5,8 +5,8 @@
 % foreach my $which ( '', 'ship_' ) {
 %   my $pre = $cust_main->get("${which}last") ? $which : '';
 
-<% $which{$which} %> <% mt('address') |h %>
-<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
+<FONT CLASS="fsinnerbox-title"><% $which{$which} %> <% mt('address') |h %></FONT>
+<TABLE CLASS="fsinnerbox">
 <TR>
   <TD ALIGN="right"><% mt('Contact name') |h %></TD>
   <TD COLSPAN=5 BGCOLOR="#ffffff">
     <TD BGCOLOR="#ffffff"><% $cust_main->masked('ss') || '&nbsp' %></TD>
 % } 
 </TR>
+
 % if ( $conf->exists('cust-email-high-visibility') && $which eq '') {
-<TR>
-  <TD ALIGN="right"><% mt('Email invoices') |h %></TD>
-  <TD BGCOLOR="#ffff00">
-    <% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) || $no %>
-  </TD>
-</TR>
+  <TR>
+    <TD ALIGN="right"><% mt('Email invoices') |h %></TD>
+    <TD BGCOLOR="#ffff00">
+      <% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) || $no %>
+    </TD>
+  </TR>
 % }
-<TR>
-  <TD ALIGN="right"><% mt('Company') |h %></TD>
-  <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}company") |h %></TD>
-</TR>
+
+% if ( $cust_main->get("${pre}company") ) {
+  <TR>
+    <TD ALIGN="right"><% mt('Company') |h %></TD>
+    <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}company") |h %></TD>
+  </TR>
+% }
+
 <TR>
   <TD ALIGN="right"><% mt('Address') |h %></TD>
   <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address1") |h %></TD>
   &>
 % }
 
-<TR>
-  <TD ALIGN="right"><% $daytime_label %></TD>
-  <TD COLSPAN=3 BGCOLOR="#ffffff">
-    <& /elements/phonenumber.html,
-                  $cust_main->get("${pre}daytime"),
-                  'callable'=>1,
-                  'calling_list_exempt'=>$cust_main->calling_list_exempt,
-    &>
-  </TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% $night_label %></TD>
-  <TD COLSPAN=3 BGCOLOR="#ffffff">
-    <& /elements/phonenumber.html,
-                  $cust_main->get("${pre}night"),
-                  'callable'=>1,
-                  'calling_list_exempt'=>$cust_main->calling_list_exempt,
-    &>
-  </TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% $mobile_label %></TD>
-  <TD COLSPAN=3 BGCOLOR="#ffffff">
-    <& /elements/phonenumber.html,
-                  $cust_main->get("${pre}mobile"),
-                  'callable'=>1,
-                  'calling_list_exempt'=>$cust_main->calling_list_exempt,
-    &>
-  </TD>
-</TR>
-<TR>
-  <TD ALIGN="right"><% mt('Fax') |h %></TD>
-  <TD COLSPAN=3 BGCOLOR="#ffffff">
-    <% $cust_main->get("${pre}fax") || '&nbsp' %>
-  </TD>
-</TR>
+% foreach my $phone (grep $cust_main->get($pre.$_), qw( daytime night mobile )){
+
+  <TR>
+    <TD ALIGN="right"><% $phone_label{$phone} %></TD>
+    <TD COLSPAN=3 BGCOLOR="#ffffff">
+      <& /elements/phonenumber.html,
+                    $cust_main->get($pre.$phone),
+                    'callable'=>1,
+                    'calling_list_exempt'=>$cust_main->calling_list_exempt,
+      &>
+    </TD>
+  </TR>
+
+% }
+
+% if ( $cust_main->get("${pre}fax") ) {
+  <TR>
+    <TD ALIGN="right"><% mt('Fax') |h %></TD>
+    <TD COLSPAN=3 BGCOLOR="#ffffff">
+      <% $cust_main->get("${pre}fax") || '&nbsp' %>
+    </TD>
+  </TR>
+% }
+
 % if ( $which eq '' && $conf->exists('show_stateid') ) { 
   <TR>
     <TD ALIGN="right"><% $stateid_label %></TD>
     <TD BGCOLOR="#ffffff"><% $cust_main->stateid_state || '&nbsp' %></TD>
   </TR>
 % } 
-</TABLE></TD></TR></TABLE>
+
+</TABLE>
 % if ( $which ne 'ship_' ) {
 <BR>
 % }
 % } 
-<& contacts_new.html, $cust_main &>
 <%once>
 
-my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/
-                      ? 'Day&nbsp;Phone'
-                      : FS::Msgcat::_gettext('daytime');
-my $night_label   = FS::Msgcat::_gettext('night') =~ /^(night)?$/
-                      ? 'Night&nbsp;Phone'
-                      : FS::Msgcat::_gettext('night');
-my $mobile_label = FS::Msgcat::_gettext('mobile') =~ /^(mobile)?$/
-                      ? 'Mobile&nbsp;Phone'
-                      : FS::Msgcat::_gettext('Mobile');
+my %phone_label = (
+
+  'daytime' => ( FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/
+                   ? 'Day&nbsp;Phone'
+                   : FS::Msgcat::_gettext('daytime')
+               ),
+
+  'night'   => ( FS::Msgcat::_gettext('night') =~ /^(night)?$/
+                   ? 'Night&nbsp;Phone'
+                   : FS::Msgcat::_gettext('night')
+               ),
+
+  'mobile'  => ( FS::Msgcat::_gettext('mobile') =~ /^(mobile)?$/
+                   ? 'Mobile&nbsp;Phone'
+                   : FS::Msgcat::_gettext('Mobile')
+               ),
+);
 
 my $stateid_label = FS::Msgcat::_gettext('stateid') =~ /^(stateid)?$/
                       ? 'Driver&rsquo;s&nbsp;License'
index bd812c7..155490f 100644 (file)
@@ -1,17 +1,44 @@
-% if ( @contacts ) {
 <BR>
-Contacts
-<% ntable("#cccccc",2) %>
+<FONT CLASS="fsinnerbox-title">Contacts</FONT>
+<A HREF="<%$p%>/edit/cust_main-contacts.html?<% $cust_main->custnum %>">Edit contacts</A>
+<TABLE CLASS="fsinnerbox">
 %   foreach my $contact ( @contacts ) {
+%     #XXX maybe this should be a table with alternating colors instead
       <TR>
-        <TD ALIGN="right">Contact</TD>
+        <TD ALIGN="right"><% $contact->contact_classname %> Contact</TD>
         <TD BGCOLOR="#FFFFFF"><% $contact->line %></TD>
+
+%       my @contact_email = $contact->contact_email;
+%       if (@contact_email) {
+          <TD ALIGN="right">&nbsp;&nbsp;&nbsp;Email</TD>
+          <TD BGCOLOR="#FFFFFF"><% join(', ', map $_->emailaddress, @contact_email) %></TD>
+%       }
+
+%       foreach my $phone_type (@phone_type) {
+%         my $contact_phone =
+%           qsearchs('contact_phone', {
+%                      'contactnum'   => $contact->contactnum,
+%                      'phonetypenum' => $phone_type->phonetypenum,
+%                   })
+%           or next;
+          <TD ALIGN="right">&nbsp;&nbsp;&nbsp;<% $phone_type->typename %> phone</TD>
+          <TD BGCOLOR="#FFFFFF"><% $contact_phone->phonenum |h %></TD>
+%       }
+
+%       if ( $contact->comment ) {
+          <TD ALIGN="right">&nbsp;&nbsp;&nbsp;Comment</TD>
+          <TD BGCOLOR="#FFFFFF"><% $contact->comment |h %></TD>
+
+%       }
+
       </TR>
 %   }
 </TABLE>
+<%once>
 
-% }
+my @phone_type = qsearch({table=>'phone_type', order_by=>'weight'});
 
+</%once>
 <%init>
 
 my( $cust_main ) = @_;
index 28414ef..2953287 100644 (file)
@@ -1,4 +1,4 @@
-<% ntable("#cccccc") %><TR><TD><% &ntable("#cccccc",2) %>
+<TABLE CLASS="fsinnerbox">
 
 <TR>
   <TD ALIGN="right"><% mt('Customer number') |h %></TD>
     <% $cust_main->pvf($_)->widget('HTML', 'view', $cust_main->getfield($_)) %>
 % }
 
-</TABLE></TD></TR></TABLE>
+</TABLE>
 <%init>
 
 my( $cust_main ) = @_;