Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorIvan Kohler <ivan@freeside.biz>
Tue, 26 Mar 2013 06:17:38 +0000 (23:17 -0700)
committerIvan Kohler <ivan@freeside.biz>
Tue, 26 Mar 2013 06:17:38 +0000 (23:17 -0700)
16 files changed:
FS/FS/Schema.pm
FS/FS/cust_main.pm
FS/FS/cust_main/Search.pm
FS/FS/part_event/Condition/message_email.pm [new file with mode: 0644]
httemplate/edit/cust_main/billing.html
httemplate/elements/change_history_common.html
httemplate/elements/menu.html
httemplate/misc/manage_cust_email.html [new file with mode: 0644]
httemplate/misc/process/manage_cust_email.html [new file with mode: 0644]
httemplate/misc/xmlhttp-cust_main-email_search.html [new file with mode: 0644]
httemplate/pref/pref-process.html
httemplate/pref/pref.html
httemplate/search/cust_main.html
httemplate/search/report_cust_main.html
httemplate/view/cust_main/billing.html
httemplate/view/cust_main/payment_history.html

index 29f8ea4..cd42e4e 100644 (file)
@@ -1080,6 +1080,7 @@ sub tables_hashref {
         'locale', 'varchar', 'NULL', 16, '', '', 
         'calling_list_exempt', 'char', 'NULL', 1, '', '',
         'invoice_noemail', 'char', 'NULL', 1, '', '',
+        'message_noemail', 'char', 'NULL', 1, '', '',
         'bill_locationnum', 'int', 'NULL', '', '', '',
         'ship_locationnum', 'int', 'NULL', '', '', '',
       ],
index 3e5d4c1..2a4602e 100644 (file)
@@ -1778,9 +1778,10 @@ sub check {
     || $self->ut_floatn('credit_limit')
     || $self->ut_numbern('billday')
     || $self->ut_numbern('prorate_day')
-    || $self->ut_enum('edit_subject', [ '', 'Y' ] )
-    || $self->ut_enum('calling_list_exempt', [ '', 'Y' ] )
-    || $self->ut_enum('invoice_noemail', [ '', 'Y' ] )
+    || $self->ut_flag('edit_subject')
+    || $self->ut_flag('calling_list_exempt')
+    || $self->ut_flag('invoice_noemail')
+    || $self->ut_flag('message_noemail')
     || $self->ut_enum('locale', [ '', FS::Locales->locales ])
   ;
 
index 2c7c046..f799b51 100644 (file)
@@ -794,11 +794,19 @@ sub search {
     @tagnums = grep /^(\d+)$/, @tagnums;
 
     if ( @tagnums ) {
+      if ( $params->{'all_tags'} ) {
+        foreach ( @tagnums ) {
+          push @where, 'exists(select 1 from cust_tag where '.
+                       'cust_tag.custnum = cust_main.custnum and tagnum = '.
+                       $_ . ')';
+        }
+      } else { # matching any tag, not all
        my $tags_where = "0 < (select count(1) from cust_tag where " 
                . " cust_tag.custnum = cust_main.custnum and tagnum in ("
                . join(',', @tagnums) . "))";
 
        push @where, $tags_where;
+      }
     }
   }
 
diff --git a/FS/FS/part_event/Condition/message_email.pm b/FS/FS/part_event/Condition/message_email.pm
new file mode 100644 (file)
index 0000000..7cceba6
--- /dev/null
@@ -0,0 +1,22 @@
+package FS::part_event::Condition::message_email;
+use base qw( FS::part_event::Condition );
+use strict;
+
+sub description {
+  'Customer allows email notices'
+}
+
+sub condition {
+  my( $self, $object ) = @_;
+  my $cust_main = $self->cust_main($object);
+
+  $cust_main->message_noemail ? 0 : 1;
+}
+
+sub condition_sql {
+  my( $self, $table ) = @_;
+
+  "cust_main.message_noemail IS NULL"
+}
+
+1;
index 6ba73ad..5a66f0a 100644 (file)
         <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum) 
             ? $r : '' %>Email address(es)
       </TD>
-      <TD WIDTH="408"><INPUT TYPE="text" NAME="invoicing_list" VALUE="<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) %>"></TD>
+      <TD WIDTH="408"><INPUT TYPE="text" NAME="invoicing_list" VALUE="<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) %>">
+      <INPUT TYPE="checkbox" NAME="message_noemail" VALUE="Y" <%
+        ( $cust_main->message_noemail eq 'Y' )
+          ? 'CHECKED'
+          : ''
+        %>> <% emt('Do not send notices') %>
+      </TD>
     </TR>
 % }
 
index 232664e..34ce70b 100644 (file)
   <TH CLASS="grid" BGCOLOR="#cccccc">Description</TH>
 </TR>
 
-% foreach my $item ( sort { $a->history_date <=> $b->history_date
-%                           #|| table order
-%                           || $a->historynum <=> $b->historynum
-%                         }
-%                         @history
-%                  )
-% {
+% foreach my $item ( @history ) {
 %   my $history_other = '';
 %   my $act  = $item->history_action;
 %   if ( $act =~ /^replace/ ) {
@@ -196,4 +190,11 @@ $cust_pkg_date_format .= ' %l:%M:%S%P'
   if $conf->exists('cust_pkg-display_times')
   || $curuser->option('cust_pkg-display_times');
 
+@history = sort { $a->history_date <=> $b->history_date
+                  || $a->historynum <=> $b->historynum } @history;
+
+if ( $curuser->option('history_order') eq 'newest' ) {
+  @history = reverse @history;
+}
+
 </%init>
index 00c43bb..14d36c3 100644 (file)
@@ -464,6 +464,8 @@ $tools_menu{'Job Queue'} =  [ $fsurl.'search/queue.html', 'View pending job queu
   if $curuser->access_right('Job queue');
 $tools_menu{'Ticketing'} = [ \%tools_ticketing, 'Ticketing tools' ]
   if $conf->config('ticket_system');
+$tools_menu{'Customer email settings'} = [ $fsurl.'misc/manage_cust_email.html' ]
+  if $curuser->access_right('Edit customer');
 $tools_menu{'Business card scan'} = [ $fsurl.'edit/prospect_main-upload.html' ]
   if $curuser->access_right('New prospect');
 $tools_menu{'Time Queue'} =  [ $fsurl.'search/report_timeworked.html', 'View pending support time' ]
diff --git a/httemplate/misc/manage_cust_email.html b/httemplate/misc/manage_cust_email.html
new file mode 100644 (file)
index 0000000..3ece459
--- /dev/null
@@ -0,0 +1,106 @@
+<& /elements/header.html, 'Manage customer email settings' &>
+<STYLE TYPE="text/css">
+.hidden { display: none }
+</STYLE>
+<& /elements/xmlhttp.html,
+    url => $p.'misc/xmlhttp-cust_main-email_search.html',
+    subs => ['email_search']
+&>
+<SCRIPT TYPE="text/javascript">
+
+function receive_search(result) {
+  var recs = JSON.parse(result);
+  var tbody = document.getElementById('tbody_results');
+  var j = tbody.rows.length;
+  for(var i = 0; i < j; i++) {
+    tbody.deleteRow(tbody.rows[i]);
+  }
+  if (recs.length > 0) {
+    for(var i = 0; i < recs.length; i++) {
+      var rec = recs[i];
+      var row = tbody.insertRow(i);
+      row.style.backgroundColor = (i % 2 ? '#eeeeee' : '#ffffff');
+
+      var cell = row.insertCell(0); // custnum
+      cell.appendChild( document.createTextNode(rec[0]) );
+      cell = row.insertCell(1);     // customer name
+      cell.appendChild( document.createTextNode(rec[1]) );
+      cell = row.insertCell(2);     // email
+      cell.appendChild( document.createTextNode(rec[2]) );
+
+      cell = row.insertCell(3);     // invoice_email
+      var input = document.createElement('INPUT');
+      input.type = 'hidden';
+      input.name = 'custnum';
+      input.value = rec[0];
+      cell.appendChild(input);
+
+      input = document.createElement('INPUT');
+      input.type = 'checkbox';
+      input.name = 'custnum' + rec[0] + '_invoice_email';
+      input.value = 'Y';
+      input.checked = (rec[3] != 'Y');
+      cell.appendChild(input);
+      cell.style.textAlign = 'center';
+      
+      cell = row.insertCell(4);     // message_email
+      input = document.createElement('INPUT');
+      input.type = 'checkbox';
+      input.name = 'custnum' + rec[0] + '_message_email';
+      input.value = 'Y';
+      input.checked = (rec[4] != 'Y');
+      cell.appendChild(input);
+      cell.style.textAlign = 'center';
+    }
+    document.getElementById('div_found').style.display = '';
+  } else {
+    document.getElementById('div_notfound').style.display = '';
+  }
+}
+
+function start_search() {
+  document.getElementById('div_found').style.display = 'none';
+  document.getElementById('div_notfound').style.display = 'none';
+  var email = document.getElementById('input_email').value;
+  email_search(email, receive_search);
+}
+% if ( $cgi->param('search') ) {
+window.onload = start_search;
+% }
+</SCRIPT>
+<FORM ACTION="<%$p%>misc/process/manage_cust_email.html" METHOD="POST">
+<DIV>
+% if ( $cgi->param('done') ) {
+<P STYLE="font-weight: bold; color: #00ff00">Changes saved.</P>
+% } elsif ( $cgi->param('error') ) {
+<P STYLE="font-weight: bold; color: #ff0000"><% $cgi->param('error') |h %></P>
+% }
+  Email address: 
+  <INPUT TYPE="text" ID="input_email" NAME="search"\
+         VALUE="<% $cgi->param('search') |h %>">
+  <INPUT TYPE="button" onclick="start_search()" VALUE="find">
+</DIV>
+<DIV ID="div_notfound" STYLE="display: none; padding: 1em">
+No matching email addresses found.
+</DIV>
+<DIV ID="div_found" STYLE="display: none">
+<TABLE CLASS="grid" STYLE="border-spacing: 0px">
+  <THEAD>
+    <TR STYLE="background-color: #dddddd">
+      <TH>#</TH>
+      <TH>Customer</TH>
+      <TH>Email</TH>
+      <TH>Send invoices</TH>
+      <TH>Send other notices</TH>
+    </TR>
+  </THEAD>
+  <TBODY ID="tbody_results"></TBODY>
+</TABLE>
+<INPUT TYPE="submit" VALUE="Save changes">
+</FORM>
+<& /elements/footer.html &>
+<%init>
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Edit customer');
+
+</%init>
diff --git a/httemplate/misc/process/manage_cust_email.html b/httemplate/misc/process/manage_cust_email.html
new file mode 100644 (file)
index 0000000..5bf1470
--- /dev/null
@@ -0,0 +1,32 @@
+<% $cgi->redirect($fsurl.'misc/manage_cust_email.html?' .
+                  $cgi->query_string) %>
+<%init>
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Edit customer');
+
+my $error;
+foreach my $custnum ($cgi->param('custnum')) {
+  my $cust = FS::cust_main->by_key($custnum)
+    or die "customer not found: $custnum\n";
+  my $new_invoice_noemail = 
+    $cgi->param('custnum'.$custnum.'_invoice_email') ? '' : 'Y';
+  my $new_message_noemail =
+    $cgi->param('custnum'.$custnum.'_message_email') ? '' : 'Y';
+  if ( $new_invoice_noemail ne $cust->invoice_noemail
+    or $new_message_noemail ne $cust->message_noemail ) {
+
+    $cust->set('invoice_noemail', $new_invoice_noemail);
+    $cust->set('message_noemail', $new_message_noemail);
+    $error ||= $cust->replace;
+
+  }
+  $cgi->delete('custnum'.$custnum.'_invoice_email');
+  $cgi->delete('custnum'.$custnum.'_message_email');
+}
+$cgi->delete('custnum');
+if ( $error ) {
+  $cgi->param('error' => $error); # probably unnecessary...
+} else {
+  $cgi->param('done' => 1) unless $error;
+}
+</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-email_search.html b/httemplate/misc/xmlhttp-cust_main-email_search.html
new file mode 100644 (file)
index 0000000..d8c8ef4
--- /dev/null
@@ -0,0 +1,29 @@
+<% JSON::to_json(\@result) %>\
+<%init>
+die 'access denied'
+  unless $FS::CurrentUser::CurrentUser->access_right('Edit customer');
+
+my $sub = $cgi->param('sub');
+my $email = $cgi->param('arg');
+my @where = (
+  "cust_main_invoice.dest != 'POST'",
+  "cust_main_invoice.dest LIKE ".dbh->quote('%'.$email.'%'),
+  $FS::CurrentUser::CurrentUser->agentnums_sql(table => 'cust_main'),
+);
+my @cust_main = qsearch({
+  'table'     => 'cust_main',
+  'select'    => 'cust_main.*, cust_main_invoice.dest',
+  'addl_from' => 'JOIN cust_main_invoice USING (custnum)',
+  'extra_sql' => 'WHERE '.join(' AND ', @where),
+});
+
+my @result = map {
+  [ $_->custnum,
+    $_->name,
+    $_->dest,
+    $_->invoice_noemail,
+    $_->message_noemail,
+  ]
+} @cust_main;
+
+</%init>
index 84f0832..6b94f71 100644 (file)
@@ -49,6 +49,7 @@ unless ( $error ) { # if ($access_user) {
 
   #XXX autogen
   my @paramlist = qw( locale menu_position default_customer_view 
+                      history_order
                       spreadsheet_format mobile_menu
                       enable_fuzzy_on_exact
                       disable_html_editor disable_enter_submit_onetimecharge
index 9537fed..5babb01 100644 (file)
@@ -75,6 +75,21 @@ Interface
       </SELECT>
     </TD>
   </TR>
+
+% my $history_order = $curuser->option('history_order') || 'oldest';
+  <TR>
+    <TH ALIGN="right">Customer history sort order: </TH>
+    <TD COLSPAN=2>
+      <& /elements/select.html,
+        field       => 'history_order',
+        curr_value  => $history_order,
+        options     => [ 'oldest', 'newest' ],
+        labels      => { 'oldest' => 'Oldest first',
+                         'newest' => 'Newest first',
+                       },
+      &>
+    </TD>
+  </TR>
   
   <TR>
     <TH ALIGN="right">Spreadsheet download format: </TH>
@@ -92,7 +107,7 @@ Interface
   </TR>
 
  <TR>
-    <TH ALIGN="right" COLSPAN=1>Enable approximate customer searching even when an exact match is found: </TH>
+    <TH ALIGN="right" COLSPAN=1>Enable approximate customer searching <BR>even when an exact match is found: </TH>
     <TD ALIGN="left" COLSPAN=2>
       <INPUT TYPE="checkbox" NAME="enable_fuzzy_on_exact" VALUE="1" <% $curuser->option('enable_fuzzy_on_exact') ? 'CHECKED' : '' %>>
     </TD>
index 8b39ea9..af9e959 100755 (executable)
@@ -46,6 +46,7 @@ my @scalars = qw (
   custbatch usernum
   cancelled_pkgs
   cust_fields flattened_pkgs
+  all_tags
 );
 
 for my $param ( @scalars ) {
index acc49ae..13f9396 100755 (executable)
       </TR>
 %   }
 
-    <& /elements/tr-select-cust_tag.html,
-                  'cgi'                 => $cgi,
-                  'is_report'    => 1,
-                  'multiple'     => 1,
-    &>
+      <TR>
+        <TD ALIGN="right">Tags</TD>
+        <TD>
+            <& /elements/select-cust_tag.html,
+                          'cgi'                => $cgi,
+                          'is_report'   => 1,
+                          'multiple'    => 1,
+            &>
+          <DIV STYLE="display:inline-block; vertical-align:baseline">
+            <INPUT TYPE="radio" NAME="all_tags" VALUE="0" CHECKED> Any of these
+            <BR>
+            <INPUT TYPE="radio" NAME="all_tags" VALUE="1"> All of these
+          </DIV>
+        </TD>
+      </TR>
 
     <& /elements/tr-select-payby.html,
                   'payby_type'   => 'cust',
index 5c46803..b863a73 100644 (file)
   <TD ALIGN="right"><% mt('Email address(es)') |h %></TD>
   <TD BGCOLOR="#ffffff">
     <% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) || $no %>
+%   if ( $cust_main->message_noemail ) {
+    <BR>
+    <SPAN STYLE="font-size: small"><% emt('(do not send notices)') %></SPAN>
+%   }
   </TD>
 </TR>
 % }
index 7701cb6..66008ee 100644 (file)
 %my $old_history = 0;
 %my $lastdate = 0;
 %
-%foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) {
+%foreach my $item ( @history ) {
 %
 %  $lastdate = $item->{'date'};
 %
@@ -533,6 +533,13 @@ foreach my $cust_refund ($cust_main->cust_refund) {
 
 }
 
+# sort history
+if ( $curuser->option('history_order') eq 'newest' ) {
+  @history = sort { $b->{date} <=> $a->{date} } @history;
+} else {
+  @history = sort { $a->{date} <=> $b->{date} } @history;
+} # no other sort orders for now
+
 sub translate_payby {
     my ($payby,$payinfo) = (shift,shift);
     my %payby = (