Accepts the following options: I<search>, the string to search for. The string
will be searched for as a customer number, phone number, name or company name,
as an exact, or, in some cases, a substring or fuzzy match (see the source code
-for the exact heuristics used).
+for the exact heuristics used); I<no_fuzzy_on_exact>, causes smart_search to
+skip fuzzy matching when an exact match is found.
Any additional options are treated as an additional qualifier on the search
(i.e. I<agentnum>).
my @cust_main = ();
+ my $skip_fuzzy = delete $options{'no_fuzzy_on_exact'};
my $search = delete $options{'search'};
( my $alphanum_search = $search ) =~ s/\W//g;
#always do substring & fuzzy,
#getting complains searches are not returning enough
- #unless ( @cust_main ) { #no exact match, trying substring/fuzzy
+ unless ( @cust_main && $skip_fuzzy ) { #no exact match, trying substring/fuzzy
#still some false laziness w/ search/cust_main.cgi
FS::cust_main->fuzzy_search( { $field => $value }, @fuzopts );
}
- #}
+ }
#eliminate duplicates
my %saw = ();
--- /dev/null
+<% include("/elements/header.html", 'Batch Customer Note Import') %>
+%
+
+<FORM ACTION="process/cust_main_note-import.cgi" METHOD="POST">
+
+
+<SCRIPT TYPE="text/javascript">
+
+ function clearhint_custnum() {
+
+ if ( this.value == 'Not found' ) {
+ this.value = '';
+ this.style.color = '#000000';
+ }
+
+ }
+
+ function search_custnum() {
+
+ this.style.color = '#000000'
+
+ var custnum_obj = this;
+ var searchrow = this.getAttribute('rownum');
+ var custnum = this.value;
+ var name_obj = document.getElementById('name'+searchrow);
+
+ if ( custnum == 'searching...' || custnum == 'Not found' )
+ return;
+
+ var customer_select = document.getElementById('cust_select'+searchrow);
+
+ if ( custnum == '' ) {
+ customer_select.selectedIndex = 0;
+ return;
+ }
+
+ custnum_obj.value = 'searching...';
+ custnum_obj.disabled = true;
+ custnum_obj.style.backgroundColor = '#dddddd';
+
+
+ //alert('search for custnum ' + custnum + ', row#' + searchrow );
+
+ function search_custnum_update(name) {
+
+ var name = eval('(' + name + ')' );
+
+ custnum_obj.disabled = false;
+ custnum_obj.style.backgroundColor = '#ffffff';
+
+ if ( name.length > 0 ) {
+ //alert('custnum found: ' + name);
+ opt(customer_select,custnum,name,'#000000');
+ customer_select.selectedIndex = customer_select.length - 1;
+ custnum_obj.value = custnum;
+ name_obj.value = name;
+ } else {
+ custnum_obj.value = 'Not found';
+ custnum_obj.style.color = '#ff0000';
+ }
+
+ }
+
+ custnum_search( custnum, search_custnum_update );
+
+ }
+
+ function select_customer() {
+
+ var custnum = this.options[this.selectedIndex].value;
+ var name = this.options[this.selectedIndex].text;
+
+ var searchrow = this.getAttribute('rownum');
+ var custnum_obj = document.getElementById('custnum'+searchrow);
+ var name_obj = document.getElementById('name'+searchrow);
+
+ custnum_obj.value = custnum;
+ custnum_obj.style.color = '#000000';
+
+ name_obj.value = name;
+
+ }
+
+ function opt(what,value,text,color) {
+ var optionName = new Option(text, value, false, false);
+ optionName.style.color = color;
+ var length = what.length;
+ what.options[length] = optionName;
+ }
+
+ function previewChanged(what) {
+ var submit_obj = document.getElementById('importsubmit');
+ if (what.checked) {
+ submit_obj.value = 'Preview note import';
+ }else{
+ submit_obj.value = 'Import notes';
+ }
+ }
+
+</SCRIPT>
+
+<% include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-search.cgi',
+ 'subs' => [qw( custnum_search )],
+ )
+%>
+
+% my $fh = $cgi->upload('csvfile');
+% my $csv = new Text::CSV_XS;
+% my $skip_fuzzies = $cgi->param('fuzzies') ? 0 : 1;
+%
+% if ( defined($fh) ) {
+ <TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
+ <TR>
+ <TH>Cust #</TH>
+ <TH>Customer</TH>
+ <TH>Last</TH>
+ <TH>First</TH>
+ <TH>Note to be added</TH>
+ </TR>
+% my $agentnum => scalar($cgi->param('agentnum')),
+% my $line;
+% my $row = 0;
+% while ( defined($line=<$fh>) ) {
+% chomp $line;
+% $line =~ s/^(.*)(#!).*/$1/;
+%
+% $csv->parse($line) or die "can't parse line: " . $csv->error_input();
+% my $custnum = 0;
+% my @values = $csv->fields();
+% my $last = shift @values;
+% if ($last =~ /^\s*(\d+)\s*$/ ) {
+% $custnum = $1;
+% $last = shift @values;
+% }
+% my $first = shift @values;
+% my $note = join ' ', @values;
+% next unless ( $last || $first || $note );
+% my @cust_main = ();
+% warn "searching for: $last, $first" if ($first || $last);
+% if ($custnum) {
+% @cust_main = qsearch('cust_main', { 'custnum' => $custnum });
+% } else {
+% @cust_main = FS::cust_main::smart_search(
+% 'search' => "$last, $first",
+% 'no_fuzzy_on_exact' => $skip_fuzzies,
+% )
+% if ($first || $last);
+% }
+%
+ <TR>
+ <TD>
+ <INPUT TYPE="text" NAME="custnum<% $row %>" ID="custnum<% $row %>" SIZE=8 MAXLENGTH=12 VALUE="<% $cust_main[0] ? $cust_main[0]->custnum : '' %>" rownum="<% $row %>">
+ <SCRIPT TYPE="text/javascript">
+ var custnum_input<% $row %> = document.getElementById("custnum<% $row %>");
+ custnum_input<% $row %>.onfocus = clearhint_custnum;
+ custnum_input<% $row %>.onchange = search_custnum;
+ </SCRIPT>
+ </TD>
+ <TD>
+ <SELECT NAME="cust_select<% $row %>" ID="cust_select<% $row %>" rownum="<% $row %>">
+ <OPTION VALUE="">---</OPTION>
+% my $i=0;
+% foreach (@cust_main) {
+ <OPTION <% $i ? '' : 'SELECTED' %> VALUE="<% $_->custnum %>"><% $_->name %></OPTION>
+% $i++;
+% }
+ </SELECT>
+ <SCRIPT TYPE="text/javascript">
+ var customer_select<% $row %> = document.getElementById("cust_select<% $row %>");
+ customer_select<% $row %>.onchange = select_customer;
+ </SCRIPT>
+ <INPUT TYPE="hidden" NAME="name<% $row %>" ID="name<% $row %>" VALUE="<% $i ? $cust_main[0]->name : '' %>">
+ </TD>
+ <TD>
+ <% $first %>
+ <INPUT TYPE="hidden" NAME="first<% $row %>" VALUE="<% $first %>">
+ </TD>
+ <TD>
+ <% $last %>
+ <INPUT TYPE="hidden" NAME="last<% $row %>" VALUE="<% $last %>">
+ </TD>
+ <TD>
+ <% $note %>
+ <INPUT TYPE="hidden" NAME="note<% $row %>" VALUE="<% $note %>">
+ </TD>
+ </TR>
+% $row++;
+% }
+ </TABLE>
+ <INPUT TYPE="submit" NAME="submit" ID="importsubmit" VALUE="Import notes">
+ <INPUT TYPE="checkbox" NAME="preview" onchange="previewChanged(this);">
+ Preview mode
+% } else {
+ No file supplied
+% }
+
+</FORM>
+</BODY>
+</HTML>
--- /dev/null
+<% include("/elements/header.html", "Batch Customer Note Import $op") %>
+
+The following items <% $op eq 'Preview' ? 'would not be' : 'were not' %> imported. (See below for imported items)
+<PRE>
+% foreach my $row (@uninserted) {
+% $csv->combine( (map{ $row->{$_} } qw(last first note) ),
+% $row->{error} ? ('#!', $row->{error}) : (),
+% );
+<% $csv->string %>
+% }
+</PRE>
+
+The following items <% $op eq 'Preview' ? 'would be' : 'were' %> imported. (See above for unimported items)
+
+<PRE>
+% foreach my $row (@inserted) {
+% $csv->combine( (map{ $row->{$_} } qw(custnum last first note) ),
+% ('#!', $row->{name}),
+% );
+<% $csv->string %>
+% }
+</PRE>
+
+<%init>
+my $date = time;
+my $otaker = $FS::CurrentUser::CurrentUser->username;
+my $csv = new Text::CSV_XS;
+
+my $param = $cgi->Vars;
+
+my $op = $param->{preview} ? "Preview" : "Results";
+
+my @inserted = ();
+my @uninserted = ();
+for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+ if ( $param->{"custnum$row"} ) {
+ my $cust_main_note = new FS::cust_main_note {
+ 'custnum' => $param->{"custnum$row"},
+ '_date' => $date,
+ 'otaker' => $otaker,
+ 'comments' => $param->{"note$row"},
+ };
+ my $error = '';
+ $error = $cust_main_note->insert unless ($op eq "Preview");
+ my $result = { 'custnum' => $param->{"custnum$row"},
+ 'last' => $param->{"last$row"},
+ 'first' => $param->{"first$row"},
+ 'note' => $param->{"note$row"},
+ 'name' => $param->{"name$row"},
+ 'error' => $error,
+ };
+ if ($error) {
+ push @uninserted, $result;
+ }else{
+ push @inserted, $result;
+ }
+ }else{
+ push @uninserted, { 'custnum' => '',
+ 'last' => $param->{"last$row"},
+ 'first' => $param->{"first$row"},
+ 'note' => $param->{"note$row"},
+ 'error' => '',
+ };
+ }
+}
+</%init>