diff options
Diffstat (limited to 'httemplate/misc')
49 files changed, 1307 insertions, 354 deletions
diff --git a/httemplate/misc/areacodes.cgi b/httemplate/misc/areacodes.cgi index 9d32a3baf..4b31deb00 100644 --- a/httemplate/misc/areacodes.cgi +++ b/httemplate/misc/areacodes.cgi @@ -1,4 +1,4 @@ -<% objToJson(\@areacodes) %> +<% encode_json(\@areacodes) %>\ <%init> my( $state, $svcpart ) = $cgi->param('arg'); diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html index 887b92489..cc1a26a0e 100644 --- a/httemplate/misc/batch-cust_pay.html +++ b/httemplate/misc/batch-cust_pay.html @@ -5,6 +5,15 @@ <& /elements/error.html &> +<STYLE TYPE="text/css"> +.select_invnum { + text-align: right; + width: 220px; +} +.select_invnum * { + font-family: monospace; +} +</STYLE> <SCRIPT TYPE="text/javascript"> function warnUnload() { if(document.getElementById("OneTrueTable").rows.length > 3 && @@ -23,15 +32,21 @@ function add_row_callback(rownum, prefix) { function custnum_update_callback(rownum, prefix) { var custnum = document.getElementById('custnum'+rownum).value; - document.getElementById('enable_app'+rownum).disabled = ( - custnum == 0 || - num_open_invoices[rownum] < 2 - ); + // if there is a custnum and more than one open invoice, enable + // (and check) the box + var show_applications = !(custnum > 0 && num_open_invoices[rownum] > 1); + var enable_app_checkbox = document.getElementById('enable_app'+rownum); + enable_app_checkbox.disabled = show_applications; + % if ( $use_discounts ) { select_discount_term(rownum, prefix); % } } +function invnum_update_callback(rownum, prefix) { + custnum_update_callback(rownum, prefix); +} + function select_discount_term(row, prefix) { var custnum_obj = document.getElementById('custnum'+prefix+row); var select_obj = document.getElementById('discount_term'+prefix+row); @@ -89,6 +104,17 @@ function toggle_application_row(ev, next) { next.call(this, rownum); } ); + } else { + var row = document.getElementById('row'+rownum); + var table_rows = row.parentNode.rows; + for (i = row.sectionRowIndex; i < table_rows.count; i++) { + if ( table_rows[i].id.indexof('row'+rownum+'.') > -1 ) { + table_rows.removeChild(table_rows[i]); + } else { + break; + } + } + lock_payment_row(rownum, false); } } @@ -168,21 +194,23 @@ function choose_app_invnum() { function focus_app_invnum() { % # invoice numbers just display as invoice numbers var rownum = this.getAttribute('rownum'); - var add_opt = function(obj, value) { + var add_opt = function(obj, value, label) { var o = document.createElement('OPTION'); - o.text = value; + o.text = label; o.value = value; obj.add(o); } this.options.length = 0; var this_invoice = this.curr_invoice; if ( this_invoice ) { - add_opt(this, this_invoice.invnum); + add_opt(this, this_invoice.invnum, this_invoice.label); } else { - add_opt(this, ''); + add_opt(this, '', ''); } for ( var x in invoices_for_row[rownum] ) { - add_opt(this, invoices_for_row[rownum][x].invnum); + add_opt(this, + invoices_for_row[rownum][x].invnum, + invoices_for_row[rownum][x].label); } } @@ -198,7 +226,6 @@ function change_app_amount() { && amount_unapplied(rownum) > 0 ) { create_application_row(rownum, parseInt(appnum) + 1); - } } @@ -220,8 +247,7 @@ function create_application_row(rownum, appnum) { select_invnum.setAttribute('appnum', appnum); select_invnum.setAttribute('id', 'invnum'+rownum+'.'+appnum); select_invnum.setAttribute('name', 'invnum'+rownum+'.'+appnum); - select_invnum.style.textAlign = 'right'; - select_invnum.style.width = '50px'; + select_invnum.className = 'select_invnum'; select_invnum.onchange = choose_app_invnum; select_invnum.onfocus = focus_app_invnum; @@ -352,6 +378,7 @@ function preload() { footer_align => \@footer_align, onchange => \@onchange, custnum_update_callback => 'custnum_update_callback', + invnum_update_callback => 'invnum_update_callback', add_row_callback => 'add_row_callback', &> diff --git a/httemplate/misc/cancel-unaudited.cgi b/httemplate/misc/cancel-unaudited.cgi index 4919c6632..4b3084f00 100755 --- a/httemplate/misc/cancel-unaudited.cgi +++ b/httemplate/misc/cancel-unaudited.cgi @@ -15,19 +15,32 @@ my($query) = $cgi->keywords; $query =~ /^(\d+)$/; my $svcnum = $1; -#my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$svcnum}); -#die "Unknown svcnum!" unless $svc_acct; - +my $error = ''; my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum}); -die "Unknown svcnum!" unless $cust_svc; -my $cust_pkg = $cust_svc->cust_pkg; -if ( $cust_pkg ) { - errorpage( 'This account has already been audited. Cancel the '. - qq!<A HREF="${p}view/cust_main.cgi?!. $cust_pkg->custnum. - '#cust_pkg'. $cust_pkg->pkgnum. '">'. - 'package</A> instead.'); -} +if ( $cust_svc ) { + my $cust_pkg = $cust_svc->cust_pkg; + if ( $cust_pkg ) { + errorpage( 'This account has already been audited. Cancel the '. + qq!<A HREF="${p}view/cust_main.cgi?!. $cust_pkg->custnum. + '#cust_pkg'. $cust_pkg->pkgnum. '">'. + 'package</A> instead.'); #' + } -my $error = $cust_svc->cancel; + $error = $cust_svc->cancel; +} else { + # the rare obscure case: svc_x without cust_svc + my $svc_x; + foreach my $svcdb (FS::part_svc->svc_tables) { + $svc_x = qsearchs($svcdb, { 'svcnum' => $svcnum }); + last if $svc_x; + } + if ( $svc_x ) { + $error = $svc_x->return_inventory + || $svc_x->FS::Record::delete; + } else { + # the svcnum really doesn't exist + $error = "svcnum $svcnum not found"; + } +} </%init> diff --git a/httemplate/misc/change_pkg.cgi b/httemplate/misc/change_pkg.cgi index 2ab9329a1..03e336cba 100755 --- a/httemplate/misc/change_pkg.cgi +++ b/httemplate/misc/change_pkg.cgi @@ -32,8 +32,6 @@ <& /elements/standardize_locations.html, 'form' => "OrderPkgForm", - 'onlyship' => 1, - 'no_company' => 1, 'callback' => 'document.OrderPkgForm.submit();', &> diff --git a/httemplate/misc/change_pkg_contact.html b/httemplate/misc/change_pkg_contact.html new file mode 100755 index 000000000..c88140ebf --- /dev/null +++ b/httemplate/misc/change_pkg_contact.html @@ -0,0 +1,70 @@ +<& /elements/header-popup.html, mt("Change Package Contact") &> + +<& /elements/error.html &> + +<FORM ACTION="<% $p %>misc/process/change_pkg_contact.html" METHOD=POST> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> + +<% ntable('#cccccc') %> + + <TR> + <TH ALIGN="right"><% mt('Package') |h %></TH> + <TD COLSPAN=7 BGCOLOR="#dddddd"> + <% $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : '' %><B><% $part_pkg->pkg |h %></B> - <% $part_pkg->comment |h %> + </TD> + </TR> + +% if ( $cust_pkg->contactnum ) { + <TR> + <TH ALIGN="right"><% mt('Current Contact') %></TH> + <TD COLSPAN=7 BGCOLOR="#dddddd"> + <% $cust_pkg->contact_obj->line |h %> + </TD> + </TR> +% } + +<& /elements/tr-select-contact.html, + 'label' => mt('New Contact'), #XXX test + 'cgi' => $cgi, + 'cust_main' => $cust_pkg->cust_main, +&> + +</TABLE> + +<BR> +<INPUT TYPE = "submit" + VALUE = "<% $cust_pkg->contactnum ? mt("Change contact") : mt("Add contact") |h %>" +> + +</FORM> +</BODY> +</HTML> + +<%init> + +my $conf = new FS::Conf; + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Change customer package'); + +my $pkgnum = scalar($cgi->param('pkgnum')); +$pkgnum =~ /^(\d+)$/ or die "illegal pkgnum $pkgnum"; +$pkgnum = $1; + +my $cust_pkg = + qsearchs({ + 'table' => 'cust_pkg', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'pkgnum' => $pkgnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, + }) or die "unknown pkgnum $pkgnum"; + +my $cust_main = $cust_pkg->cust_main + or die "can't get cust_main record for custnum ". $cust_pkg->custnum. + " ( pkgnum ". cust_pkg->pkgnum. ")"; + +my $part_pkg = $cust_pkg->part_pkg; + +</%init> diff --git a/httemplate/misc/choose_tax_location.html b/httemplate/misc/choose_tax_location.html index dce04c77d..23099c421 100644 --- a/httemplate/misc/choose_tax_location.html +++ b/httemplate/misc/choose_tax_location.html @@ -1,6 +1,5 @@ <FORM NAME="choosegeocodeform"> <CENTER><BR><B>Choose tax location</B><BR><BR> -<P>the geocode is:<% $header %></P> <P STYLE="<% $style %>"><% $header %></P> <SELECT NAME='geocodes' ID='geocodes' STYLE="<% $style %>"> @@ -12,7 +11,7 @@ % map { $value{$_} = $location{$_} } qw ( city state ) % if $location{country} eq 'CA'; % -% my $value = encode_entities(objToJson({ %value }) +% my $value = encode_entities(encode_json({ %value }) % ); % my $content = ''; % $content .= $location->$_. ' ' x ( $max{$_} - length($location->$_) ) diff --git a/httemplate/misc/confirm-address_standardize.html b/httemplate/misc/confirm-address_standardize.html new file mode 100644 index 000000000..420e8ea1d --- /dev/null +++ b/httemplate/misc/confirm-address_standardize.html @@ -0,0 +1,131 @@ +<STYLE type="text/css"> +th { line-height: 150% } +</STYLE> +<CENTER><BR><B> +% if ( $new{bill_error} or $new{ship_error} ) { +Address standardization error +% } +% else { +Confirm address standardization +% } + +</B><BR><BR> +<TABLE WIDTH="100%"> +% my @prefixes = (''); +% if ( $old{same} ) { +% @prefixes = ('bill_'); +% } elsif ( $old{billship} ) { +% @prefixes = ('bill_', 'ship_'); +% } +% for my $pre (@prefixes) { +% my $name = $pre eq 'bill_' ? 'billing' : 'service'; +% if ( $new{$pre.'addr_clean'} ) { + <TR> + <TH>Entered <%$name%> address</TH> + <TH>Standardized <%$name%> address</TH> + </TR> + <TR> +% if ( $old{$pre.'company'} ) { + <TR> + <TD><% $old{$pre.'company'} %></TD> + <TD><% $new{$pre.'company'} %></TD> + </TR> +% } + <TR> + <TD><% $old{$pre.'address1'} %></TD> + <TD><% $new{$pre.'address1'} %></TD> + </TR> + <TR> + <TD><% $old{$pre.'address2'} %></TD> + <TD><% $new{$pre.'address2'} %></TD> + </TR> + <TR> + <TD><% $old{$pre.'city'} %>, <% $old{$pre.'state'} %> <% $old{$pre.'zip'} %></TD> + <TD><% $new{$pre.'city'} %>, <% $new{$pre.'state'} %> <% $new{$pre.'zip'} %></TD> + </TR> + +% } # if addr_clean +% elsif ( $new{$pre.'error'} ) { + <TR> + <TH>Entered <%$name%> address</TH> + </TR> +% if ( $old{$pre.'company'} ) { + <TR> + <TD><% $old{$pre.'company'} %></TD> + </TR> +% } + <TR> + <TD><% $old{$pre.'address1'} %></TD> + <TD ROWSPAN=3><FONT COLOR="#ff0000"><B><% $new{$pre.'error'} %></B></FONT></TD> + </TR> + <TR> + <TD><% $old{$pre.'address2'} %></TD> + </TR> + <TR> + <TD><% $old{$pre.'city'} %>, <% $old{$pre.'state'} %> <% $old{$pre.'zip'} %></TD> + </TR> +% } #if error +% } # for $pre + +%# only do this part if address standardization provided a censustract +% my $pre = $old{same} ? 'bill_' : 'ship_'; +% my $censustract = $new{$pre.'censustract'}; +% my $census_error = $new{$pre.'census_error'}; +% if ( $censustract ) { + <TR> + <TH>Entered census tract</TH> + <TH>Calculated census tract</TH> + </TR> + <TR> + <TD><% $old{$pre.'censustract'} %></TD> + <TD> +% if ( $census_error ) { + <FONT COLOR="#ff0000"><% $census_error %></FONT> +% } else { + <% $censustract %> +% } + </TD> + </TR> +% } #if censustract + +% if ( $new{bill_error} or $new{ship_error} ) { + <TR> + <TD ALIGN="center"> + <BUTTON TYPE="button" STYLE="width:205px" onclick="confirm_manual_address();"> + <IMG SRC="<%$p%>images/error.png" ALT=""> Use entered <%$addresses%> + </BUTTON></TD> + <TD ALIGN="center"> + <BUTTON TYPE="button" STYLE="width:205px" onclick="submit_abort();"> + <IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission + </BUTTON></TD> + </TR> +% } +% else { + <TR> + <TD ALIGN="center"> + <BUTTON TYPE="button" STYLE="width:205px" onclick="confirm_manual_address()();"> + <IMG SRC="<%$p%>images/error.png" ALT=""> Use entered <%$addresses%> + </BUTTON></TD> + <TD ALIGN="center"> + <BUTTON TYPE="button" STYLE="width:205px" onclick="replace_address();"> + <IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized <%$addresses%> + </BUTTON></TD> + </TR> + <TR ALIGN="center"><TD COLSPAN=2> + <BUTTON TYPE="button" STYLE="width:205px" onclick="submit_abort();"> + <IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission + </BUTTON> + </TD></TR> +</TABLE> +% } # !error +<%init> + +# slightly weird interface... +my $q = decode_json($cgi->param('q')); +#warn Dumper($q); +my %old = %{ $q->{old} }; +my %new = %{ $q->{new} }; + +my $addresses = $old{billship} ? 'addresses' : 'address'; + +</%init> diff --git a/httemplate/misc/confirm-censustract.html b/httemplate/misc/confirm-censustract.html new file mode 100644 index 000000000..6a11617e7 --- /dev/null +++ b/httemplate/misc/confirm-censustract.html @@ -0,0 +1,79 @@ +<CENTER><BR><B> +% if ( $error ) { +Census tract error +% } +% else { +Confirm census tract +% } +</B><BR> +% my $querystring = "census_year=$year&latitude=".$cache->get('latitude').'&longitude='.$cache->get('longitude'); +<A HREF="http://maps.ffiec.gov/FFIECMapper/TGMapSrv.aspx?<% $querystring %>" + TARGET="_blank">Map service module location</A><BR> +% $querystring = "census_year=$year&zip_code=".$cache->get('zip'); +<A HREF="http://maps.ffiec.gov/FFIECMapper/TGMapSrv.aspx?<% $querystring %>" + TARGET="_blank">Map zip code center</A><BR> +<BR> +<TABLE> + <TR> + <TH style="width:50%">Entered census tract</TH> + <TH style="width:50%">Calculated census tract</TH> + </TR> + <TR> + <TD><% $old_tract %></TD> +% if ( $error ) { + <TD><FONT COLOR="#ff0000"><% $error %></FONT></TD> +% } else { + <TD><% $new_tract %></TD> +% } + </TR> + <TR> + <TD ALIGN="center"> + <BUTTON TYPE="button" + onclick="set_censustract('<% $old_tract %>', '<% $year %>')"> + <IMG SRC="<%$p%>images/error.png" ALT=""> Use entered census tract + </BUTTON> + </TD> + <TD ALIGN="center"> + <BUTTON TYPE="button" + onclick="set_censustract('<% $new_tract %>', '<% $year %>')"> + <IMG SRC="<%$p%>images/tick.png" ALT=""> Use calculated census tract + </BUTTON> + </TD> + </TR> + <TR> + <TD COLSPAN=2 ALIGN="center"> + <BUTTON TYPE="button" onclick="submit_abort()"> + <IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission + </BUTTON> + </TD> + </TR> +</TABLE></CENTER> +<%init> + +local $SIG{__DIE__}; #disable Mason error trap + +my $DEBUG = 0; + +my $conf = new FS::Conf; + +warn $cgi->param('q') if $DEBUG; + +my $q = decode_json($cgi->param('q')) + or die "bad argument '".$cgi->param('q')."'"; + +my $pre = $q->{'same'} ? 'bill_' : 'ship_'; +my %location = ( + map { $_ => $q->{$pre.$_} } + qw( company address1 address2 city state zip country latitude longitude ) +); + +my $old_tract = $q->{$pre.'censustract'}; +my $cache = eval { FS::GeocodeCache->new(%location) }; +$cache->set_censustract; +my $year = FS::Conf->new->config('census_year'); +my $new_tract = $cache->get('censustract'); +my $error = $cache->get('censustract_error'); + +warn Dumper($cache) if $DEBUG; + +</%init> diff --git a/httemplate/misc/cust-part_pkg.cgi b/httemplate/misc/cust-part_pkg.cgi index a277ba407..43b92297e 100644 --- a/httemplate/misc/cust-part_pkg.cgi +++ b/httemplate/misc/cust-part_pkg.cgi @@ -1,4 +1,4 @@ -<% objToJson( \@return ) %> +<% encode_json( \@return ) %>\ <%init> my( $custnum, $prospectnum, $classnum ) = $cgi->param('arg'); diff --git a/httemplate/misc/cust_main-merge.html b/httemplate/misc/cust_main-merge.html index 4decbef7a..3b4425fc8 100755 --- a/httemplate/misc/cust_main-merge.html +++ b/httemplate/misc/cust_main-merge.html @@ -31,7 +31,17 @@ if ( $cgi->param('new_custnum') =~ /^(\d+)$/ ) { } ); die "No customer # $custnum" unless $cust_main; - $error = $cust_main->merge($new_custnum); + if ( $cgi->param('merge') eq 'Y' ) { + + #old-style merge: everything + delete old customer + $error = $cust_main->merge($new_custnum); + + } else { + + #new-style attach: move packages 3.0 style, that's it + $error = $cust_main->attach_pkgs($new_custnum); + + } } else { $error = 'Select a customer to merge into'; diff --git a/httemplate/misc/cust_main_note-import.cgi b/httemplate/misc/cust_main_note-import.cgi index 72ac556fd..186289517 100644 --- a/httemplate/misc/cust_main_note-import.cgi +++ b/httemplate/misc/cust_main_note-import.cgi @@ -164,7 +164,7 @@ <OPTION VALUE="">---</OPTION> % my $i=0; % foreach (@cust_main) { - <OPTION <% $i ? '' : 'SELECTED' %> VALUE="<% $_->custnum %>"><% $_->name %></OPTION> + <OPTION <% $i ? '' : 'SELECTED' %> VALUE="<% $_->custnum %>"><% $_->name |h %></OPTION> % $i++; % } </SELECT> @@ -172,15 +172,15 @@ 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 : '' %>"> + <INPUT TYPE="hidden" NAME="name<% $row %>" ID="name<% $row %>" VALUE="<% $i ? $cust_main[0]->name : '' |h %>"> </TD> <TD> - <% $first %> - <INPUT TYPE="hidden" NAME="first<% $row %>" VALUE="<% $first %>"> + <% $first |h %> + <INPUT TYPE="hidden" NAME="first<% $row %>" VALUE="<% $first |h %>"> </TD> <TD> - <% $last %> - <INPUT TYPE="hidden" NAME="last<% $row %>" VALUE="<% $last %>"> + <% $last |h %> + <INPUT TYPE="hidden" NAME="last<% $row %>" VALUE="<% $last |h %>"> </TD> <TD> <% $note %> diff --git a/httemplate/misc/delete-customer.cgi b/httemplate/misc/delete-customer.cgi deleted file mode 100755 index 203ed36a5..000000000 --- a/httemplate/misc/delete-customer.cgi +++ /dev/null @@ -1,64 +0,0 @@ -<% include('/elements/header.html', 'Delete customer') %> - -<% include('/elements/error.html') %> - -<FORM ACTION="<% popurl(1) %>process/delete-customer.cgi" METHOD=POST> -<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum |h %>"> - -%if ( qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } ) ) { - Move uncancelled packages to customer number - <INPUT TYPE="text" NAME="new_custnum" VALUE="<% $new_custnum |h %>"><BR><BR> -%} - -This will <B>completely remove</B> all traces of this customer record. This -is <B>not</B> what you want if this is a real customer who has simply -canceled service with you. For that, cancel all of the customer's packages. -(you can optionally hide cancelled customers with the <A HREF="../config/config-view.cgi#hidecancelledcustomers">hidecancelledcustomers</A> configuration option) -<BR> -<BR>Are you <B>absolutely sure</B> you want to delete this customer? -<BR><INPUT TYPE="submit" VALUE="Yes"> -</FORM> - -<% include('/elements/footer.html') %> - -%#Deleting a customer you have financial records on (i.e. credits) is -%#typically considered fraudulant bookkeeping. Remember, deleting -%#customers should ONLY be used for completely bogus records. You should -%#NOT delete real customers who simply discontinue service. -%# -%#For real customers who simply discontinue service, cancel all of the -%#customer's packages. Customers with all cancelled packages are not -%#billed. There is no need to take further action to prevent billing on -%#customers with all cancelled packages. -%# -%#Also see the "hidecancelledcustomers" and "hidecancelledpackages" -%#configuration options, which will allow you to surpress the display of -%#cancelled customers and packages, respectively. - -<%init> - -my $conf = new FS::Conf; -die "Customer deletions not enabled in configuration" - unless $conf->exists('deletecustomers'); - -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Delete customer'); - -my($custnum, $new_custnum); -if ( $cgi->param('error') ) { - $custnum = $cgi->param('custnum'); - $new_custnum = $cgi->param('new_custnum'); -} else { - my($query) = $cgi->keywords; - $query =~ /^(\d+)$/ or die "Illegal query: $query"; - $custnum = $1; - $new_custnum = ''; -} -my $cust_main = qsearchs( { - 'table' => 'cust_main', - 'hashref' => { 'custnum' => $custnum }, - 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql, -} ) - or die 'Unknown custnum'; - -</%init> diff --git a/httemplate/misc/delete-note.html b/httemplate/misc/delete-note.html new file mode 100644 index 000000000..436326ff1 --- /dev/null +++ b/httemplate/misc/delete-note.html @@ -0,0 +1,11 @@ +<%init> +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit customer note'); + +my ($notenum) = $cgi->keywords; +$notenum =~ /^\d+$/ or die "bad notenum '$notenum'"; +my $note = FS::cust_main_note->by_key($notenum) + or die "notenum '$notenum' not found"; +$note->delete; +</%init> +<% $cgi->redirect($p.'view/cust_main.cgi?'.$note->custnum) %> diff --git a/httemplate/misc/detach_pkg.html b/httemplate/misc/detach_pkg.html new file mode 100755 index 000000000..64b3e6e3f --- /dev/null +++ b/httemplate/misc/detach_pkg.html @@ -0,0 +1,104 @@ +<& /elements/header-popup.html, mt("Detach Package to New Customer") &> + +<SCRIPT TYPE="text/javascript" SRC="../elements/order_pkg.js"></SCRIPT> + +<& /elements/error.html &> + +<FORM NAME="OrderPkgForm" ACTION="<% $p %>edit/process/detach-cust_pkg.html" METHOD=POST> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +% foreach my $f (qw( agentnum refnum )) { + <INPUT TYPE="hidden" NAME="<% $f %>" VALUE="<% $cust_main->$f() %>"> +% } +<INPUT TYPE="hidden" NAME="referral_custnum" VALUE="<% $cust_main->custnum %>"> +% foreach my $f (FS::cust_main->location_fields) { + <INPUT TYPE="hidden" NAME="<% $f %>" VALUE="<% $loc->$f() |h %>"> +% } + +<% ntable('#cccccc') %> + + <TR> + <TH ALIGN="right"><% mt('Package') |h %></TH> + <TD COLSPAN=7 BGCOLOR="#dddddd"> + <% $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : '' %><B><% $part_pkg->pkg |h %></B> - <% $part_pkg->comment |h %> + </TD> + </TR> + +% #always should be present for detaching, yes? #if ( $cust_pkg->contactnum ) { +% my $cust_contact = $cust_pkg->contact_obj; + + <INPUT TYPE="hidden" NAME="first" VALUE="<% $cust_contact->get('first') |h %>"> + <INPUT TYPE="hidden" NAME="last" VALUE="<% $cust_contact->get('last') |h %>"> + + <TR> + <TH ALIGN="right"><% mt('Name') %></TH> + <TD COLSPAN=7 BGCOLOR="#dddddd"> + <% $cust_pkg->contact_obj->line |h %> + </TD> + </TR> +% #} + + <TR> + <TH ALIGN="right" VALIGN="top"><% mt('Address') %></TH> + <TD COLSPAN=7 BGCOLOR="#dddddd"> + + <% $loc->location_label( 'join_string' => '<BR>', + 'double_space' => ' ', + 'escape_function' => \&encode_entities, + 'countrydefault' => $countrydefault, + ) + %> + </TD> + </TR> + +</TABLE> + +%#XXX payment info +%#XXX should be sticky on errors... +<& /edit/cust_main/billing.html, FS::cust_main->new({}), + invoicing_list => [], + +&> + +<BR> +<BR> +<INPUT NAME = "submitButton" + TYPE = "submit" + VALUE = "<% mt("Detach package") |h %>" +> + +%#and a cancel button? or is the popup close sufficient? + +</FORM> +</BODY> +</HTML> + +<%init> + +my $conf = new FS::Conf; +my $countrydefault = $conf->config('countrydefault') || 'US'; + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right('Change customer package'); + +my $pkgnum = scalar($cgi->param('pkgnum')); +$pkgnum =~ /^(\d+)$/ or die "illegal pkgnum $pkgnum"; +$pkgnum = $1; + +my $cust_pkg = + qsearchs({ + 'table' => 'cust_pkg', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'pkgnum' => $pkgnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, + }) or die "unknown pkgnum $pkgnum"; + +my $loc = $cust_pkg->cust_location_or_main; + +my $cust_main = $cust_pkg->cust_main + or die "can't get cust_main record for custnum ". $cust_pkg->custnum. + " ( pkgnum ". cust_pkg->pkgnum. ")"; + +my $part_pkg = $cust_pkg->part_pkg; + +</%init> diff --git a/httemplate/misc/did_order_provision.html b/httemplate/misc/did_order_provision.html index 1df9444ab..8739c1619 100644 --- a/httemplate/misc/did_order_provision.html +++ b/httemplate/misc/did_order_provision.html @@ -21,7 +21,7 @@ % my $avail = keys(%$cust_pkg_phone); % $anyavail = 1 if $avail; <TR> - <TD><% $cust_main->name %></TD> + <TD><% $cust_main->name |h %></TD> <TD> % if ( !$avail ) { No suitable packages exist for this customer. diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html index d26e40298..ad67b8d7e 100644 --- a/httemplate/misc/email-customers.html +++ b/httemplate/misc/email-customers.html @@ -104,13 +104,19 @@ Template: ) %><BR> <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%" id="table_no_template"> - <% include('/elements/tr-input-text.html', - 'field' => 'from', - 'label' => 'From:', - 'size' => 50, - ) - %> - + <& /elements/tr-td-label.html, 'label' => 'From:' &> + <TD><& /elements/input-text.html, + 'field' => 'from_name', + 'value' => $conf->config('company_name'), #? + 'size' => 20, + &> <\ + <& /elements/input-text.html, + 'field' => 'from_addr', + 'type' => 'email', # HTML5, woot + 'value' => $conf->config('invoice_from'), + 'size' => 20, + &>></TD> + <% include('/elements/tr-input-text.html', 'field' => 'subject', 'label' => 'Subject:', @@ -120,9 +126,11 @@ Template: <TR> <TD ALIGN="right" VALIGN="top" STYLE="padding-top:3px">Message: </TD> - <TD><& '/elements/htmlarea.html', - 'field' => 'html_body', - 'width' => 600 &></TD> + <TD><& /elements/htmlarea.html, + 'field' => 'html_body', + 'width' => 763, + &> + </TD> </TR> </TABLE> @@ -149,6 +157,7 @@ Template: die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices'); +my $conf = FS::Conf->new; my $table = $cgi->param('table') or die "'table' required"; my %search; if ( $cgi->param('search') ) { @@ -167,7 +176,15 @@ else { my $title = 'Send customer notices'; my $num_cust; -my $from = $cgi->param('from') || ''; +my $from = ''; +if ( $cgi->param('from') ) { + $from = $cgi->param('from'); +} elsif ( $cgi->param('from_name') ) { + $from = ($cgi->param('from_name') . ' <' . $cgi->param('from_addr') . '>'); +} elsif ( $cgi->param('from_addr') ) { + $from = $cgi->param('from_addr'); +} + my $subject = $cgi->param('subject') || ''; my $html_body = $cgi->param('html_body') || ''; diff --git a/httemplate/misc/exchanges.cgi b/httemplate/misc/exchanges.cgi index 8a67f7bab..0de4ace25 100644 --- a/httemplate/misc/exchanges.cgi +++ b/httemplate/misc/exchanges.cgi @@ -1,4 +1,4 @@ -<% objToJson(\@exchanges) %> +<% encode_json(\@exchanges) %>\ <%init> my( $areacode, $svcpart ) = $cgi->param('arg'); diff --git a/httemplate/misc/location.cgi b/httemplate/misc/location.cgi index 188c5c3df..fab61dd01 100644 --- a/httemplate/misc/location.cgi +++ b/httemplate/misc/location.cgi @@ -1,4 +1,4 @@ -<% objToJson(\%hash) %> +<% encode_json(\%hash) %>\ <%init> my $locationnum = $cgi->param('arg'); @@ -24,8 +24,9 @@ my $cust_location = qsearchs({ my %hash = (); %hash = map { $_ => $cust_location->$_() } - qw( address1 address2 city county state zip country - location_kind location_type location_number ) + ( FS::cust_main->location_fields, + qw( location_kind location_type location_number ) + ) if $cust_location; </%init> diff --git a/httemplate/misc/macinventory.cgi b/httemplate/misc/macinventory.cgi index b07da9726..cec0e3121 100644 --- a/httemplate/misc/macinventory.cgi +++ b/httemplate/misc/macinventory.cgi @@ -1,4 +1,4 @@ -<% objToJson(\@macs) %> +<% encode_json(\@macs) %>\ <%init> # XXX: this should be agent-virtualized / limited @@ -13,13 +13,8 @@ die "unknown devicepart $devicepart" unless $part_device; my $inventory_class = $part_device->inventory_class; die "devicepart $devicepart has no inventory" unless $inventory_class; -my @inventory_item = +my @macs = + map $_->item, qsearch('inventory_item', { 'classnum' => $inventory_class->classnum } ); -my @macs; - -foreach my $inventory_item ( @inventory_item ) { - push @macs, $inventory_item->item; -} - </%init> diff --git a/httemplate/misc/maestro-customer_status-test.html b/httemplate/misc/maestro-customer_status-test.html deleted file mode 100644 index 006492919..000000000 --- a/httemplate/misc/maestro-customer_status-test.html +++ /dev/null @@ -1,34 +0,0 @@ -<% include('/elements/header.html', { - 'title' => "Customer $custnum status", - }) %> - -<% include('/elements/small_custview.html', $custnum, '', 1) %> -<BR> - -<table style="border:1px solid #000000"> -% foreach my $key (keys %$return) { -% my $value = $return->{$key}; -% $value = join(', ', @$value) if ref($value) eq 'ARRAY'; - <TR> - <TD ALIGN="right"><% $key %>:</TD> - <TD><B><% $value %></B></TD> - </TR> -% } -</table> - -<% include('/elements/footer.html') %> -<%init> - -my $return; - -my($custnum, $svcnum) = $cgi->keywords; -if ( $custnum =~ /^(\d+)$/ ) { - - use FS::Maestro; - $return = FS::Maestro::customer_status($1, $svcnum); - -} else { - $return = { 'error' => 'No custnum' }; -} - -</%init> diff --git a/httemplate/misc/maestro-customer_status.cgi b/httemplate/misc/maestro-customer_status.cgi deleted file mode 100644 index ffeb53c91..000000000 --- a/httemplate/misc/maestro-customer_status.cgi +++ /dev/null @@ -1,16 +0,0 @@ -<% $uri->query %> -<%init> - -my $uri = new URI; - -my($custnum, $svcnum) = $cgi->keywords; -if ( $custnum =~ /^(\d+)$/ ) { - - use FS::Maestro; - $uri->query_form( FS::Maestro::customer_status($1) ); - -} else { - $uri->query_form( { 'error' => 'No custnum' } ); -} - -</%init> diff --git a/httemplate/misc/maestro-customer_status.html b/httemplate/misc/maestro-customer_status.html deleted file mode 100644 index 8acae2b2a..000000000 --- a/httemplate/misc/maestro-customer_status.html +++ /dev/null @@ -1,16 +0,0 @@ -<% objToJson( $return ) %> -<%init> - -my $return; - -my($custnum, $svcnum) = $cgi->keywords; -if ( $custnum =~ /^(\d+)$/ ) { - - use FS::Maestro; - $return = FS::Maestro::customer_status($1, $svcnum); - -} else { - $return = { 'error' => 'No custnum' }; -} - -</%init> diff --git a/httemplate/misc/manage_cust_email.html b/httemplate/misc/manage_cust_email.html new file mode 100644 index 000000000..3ece459bb --- /dev/null +++ b/httemplate/misc/manage_cust_email.html @@ -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/merge_cust.html b/httemplate/misc/merge_cust.html index ad075be2f..c923b7b1f 100644 --- a/httemplate/misc/merge_cust.html +++ b/httemplate/misc/merge_cust.html @@ -1,6 +1,6 @@ -<% include('/elements/header-popup.html', 'Merge customer' ) %> +<& /elements/header-popup.html, 'Merge customer' &> -<% include('/elements/error.html') %> +<& /elements/error.html &> <FORM NAME="cust_merge_popup" ID="cust_merge_popup" ACTION="<% popurl(1) %>cust_main-merge.html" METHOD=POST onSubmit="submit_merge(); return false;"> @@ -35,13 +35,43 @@ function do_submit_merge() { <INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> <TABLE BORDER="0" CELLSPACING="2" STYLE="margin-left:auto; margin-right:auto"> - <% include('/elements/tr-search-cust_main.html', + + <& /elements/tr-search-cust_main.html, 'label' => 'Merge into: ', 'field' => 'new_custnum', 'find_button' => 1, 'curr_value' => scalar($cgi->param('new_custnum')), - ) - %> + &> + +% if ( 0 ) { #we start supporting payment info merge again in some way + +% if ( scalar($cust_main->ncancelled_pkgs) ) { + <TR> + <TD COLSPAN=2> + <& /elements/radio.html, + 'field' => 'merge', + 'value' => '', + 'curr_value' => scalar($cgi->param('merge')), + &> + Merge packages only. + </TD> + </TR> +% } else { +% $cgi->param('merge', 'Y'); +% } + + <TR> + <TD COLSPAN=2> + <& /elements/radio.html, + 'field' => 'merge', + 'value' => 'Y', + 'curr_value' => scalar($cgi->param('merge')), + &> + Merge invoices, payments/credits, notes, tickets and delete<!-- ^Warchive --> this customer. + </TD> + </TR> +% } + </TABLE> <P ALIGN="CENTER"> @@ -54,6 +84,8 @@ function do_submit_merge() { <%init> +my $conf = new FS::Conf; + $cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum'; my $custnum = $1; diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html index c5f4509ab..39734427e 100644 --- a/httemplate/misc/order_pkg.html +++ b/httemplate/misc/order_pkg.html @@ -93,6 +93,12 @@ &> % } +<& /elements/tr-select-contact.html, + 'cgi' => $cgi, + 'cust_main' => $cust_main, + 'prospect_main' => $prospect_main, +&> + % if ( $cgi->param('lock_locationnum') ) { <INPUT TYPE = "hidden" @@ -128,10 +134,9 @@ % unless ( $cgi->param('lock_locationnum') ) { <& /elements/standardize_locations.html, - 'form' => "OrderPkgForm", - 'onlyship' => 1, - 'no_company' => 1, - 'callback' => 'document.OrderPkgForm.submit();', + 'form' => "OrderPkgForm", + 'callback' => 'document.OrderPkgForm.submit();', + 'with_census' => 1, &> % } diff --git a/httemplate/misc/part_export/huawei_hlr-import_sim.html b/httemplate/misc/part_export/huawei_hlr-import_sim.html new file mode 100644 index 000000000..9b87b3d2a --- /dev/null +++ b/httemplate/misc/part_export/huawei_hlr-import_sim.html @@ -0,0 +1,52 @@ +<& /elements/header-popup.html, 'Import SIMs' &> +Import a file containing SIM card properties.<BR> +Each row should contain the following fields, separated by spaces:<BR> +IMSI, ICCID, PIN1, PUK1, PIN2, PUK2, ACC, Ki<BR> +<BR> +<& /elements/form-file_upload.html, + 'name' => 'ImportForm', + 'action' => 'process/huawei_hlr-import_sim.html', + 'num_files' => 1, + 'fields' => [ 'exportnum', 'classnum', 'agentnum', ], + 'message' => 'Inventory import successful', + 'onsubmit' => "document.ImportForm.submitButton.disabled=true;", +&> +<TABLE CLASS="inv" WIDTH="100%"> + <INPUT TYPE="hidden" NAME="exportnum" VALUE="<%$exportnum%>"> + <& /elements/file-upload.html, + 'field' => 'file', + 'label' => 'Filename', + &> + <& /elements/tr-select-agent.html, + 'disable_empty' => 1, + &> + <& /elements/tr-select-table.html, + 'table' => 'inventory_class', + 'name_col' => 'classname', + 'label' => 'Inventory class', + 'disable_empty' => 1, + &> + + <TR> + <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"> + <INPUT TYPE = "submit" + NAME = "submitButton" + ID = "submitButton" + VALUE = "Import file" + > + </TD> + </TR> + +</TABLE> + +</FORM> + +<%init> +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my ($exportnum) = $cgi->keywords; +$exportnum =~ /^\d+$/ or die "bad exportnum '$exportnum'"; +my $part_export = FS::part_export->by_key($exportnum) + or die "export $exportnum not found"; +</%init> diff --git a/httemplate/misc/part_export/process/huawei_hlr-import_sim.html b/httemplate/misc/part_export/process/huawei_hlr-import_sim.html new file mode 100644 index 000000000..d46700d5f --- /dev/null +++ b/httemplate/misc/part_export/process/huawei_hlr-import_sim.html @@ -0,0 +1,10 @@ +<% $server->process %> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $server = new FS::UI::Web::JSRPC + 'FS::part_export::huawei_hlr::process_import_sim', $cgi; + +</%init> diff --git a/httemplate/misc/part_svc-columns.cgi b/httemplate/misc/part_svc-columns.cgi index 060256154..a86164d06 100644 --- a/httemplate/misc/part_svc-columns.cgi +++ b/httemplate/misc/part_svc-columns.cgi @@ -1,4 +1,4 @@ -<% objToJson(\@output) %> +<% encode_json(\@output) %>\ <%init> my $conf = new FS::Conf; diff --git a/httemplate/misc/phonenums.cgi b/httemplate/misc/phonenums.cgi index fd5de2ae6..a048280bb 100644 --- a/httemplate/misc/phonenums.cgi +++ b/httemplate/misc/phonenums.cgi @@ -1,4 +1,4 @@ -<% objToJson(\@phonenums) %> +<% encode_json(\@phonenums) %>\ <%init> my( $exchangestring, $svcpart ) = $cgi->param('arg'); @@ -21,13 +21,13 @@ if ( $exchangestring ) { my %opts = (); if ( $exchangestring eq 'tollfree' ) { $opts{'tollfree'} = 1; - } - #elsif ( $exchangestring =~ /^([\w\s\:\,\(\)\-]+), ([A-Z][A-Z])$/ ) { - elsif ( $exchangestring =~ /^(.+), ([A-Z][A-Z])$/ ) { + } elsif ( $exchangestring =~ /^_REGION (.*)$/ ) { + $opts{'region'} = $1; + #} elsif ( $exchangestring =~ /^([\w\s\:\,\(\)\-]+), ([A-Z][A-Z])$/ ) { + } elsif ( $exchangestring =~ /^(.+), ([A-Z][A-Z])$/ ) { $opts{'ratecenter'} = $1; $opts{'state'} = $2; - } - else { + } else { $exchangestring =~ /\((\d{3})-(\d{3})-XXXX\)\s*$/i or die "unparsable exchange: $exchangestring"; my( $areacode, $exchange ) = ( $1, $2 ); diff --git a/httemplate/misc/process/change-password.html b/httemplate/misc/process/change-password.html new file mode 100644 index 000000000..7cab9c4e3 --- /dev/null +++ b/httemplate/misc/process/change-password.html @@ -0,0 +1,26 @@ +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; + +$cgi->param('svcnum') =~ /^(\d+)$/ or die "illegal svcnum"; +my $svcnum = $1; +my $svc_acct = FS::svc_acct->by_key($svcnum) + or die "svc_acct $svcnum not found"; +my $part_svc = $svc_acct->part_svc; +die "access denied" unless ( + $curuser->access_right('Provision customer service') or + ( $curuser->access_right('Edit password') and + ! $part_svc->restrict_edit_password ) + ); +my $error = $svc_acct->set_password($cgi->param('password')) + || $svc_acct->replace; + +# annoyingly specific to view/svc_acct.cgi, for now... +$cgi->delete('password'); +</%init> +% if ( $error ) { +% $cgi->param('svcnum', $svcnum); +% $cgi->param("changepw${svcnum}_error", $error); +% } else { +% $cgi->query_string($svcnum); +% } +<% $cgi->redirect($fsurl.'view/svc_acct.cgi?'.$cgi->query_string) %> diff --git a/httemplate/misc/process/change_pkg_contact.html b/httemplate/misc/process/change_pkg_contact.html new file mode 100644 index 000000000..2795c1197 --- /dev/null +++ b/httemplate/misc/process/change_pkg_contact.html @@ -0,0 +1,49 @@ +<% header(emt("Package contact $past_method")) %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY> +</HTML> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Change customer package'); + +#untaint pkgnum +my $pkgnum = $cgi->param('pkgnum'); +$pkgnum =~ /^(\d+)$/ or die "Illegal pkgnum"; +$pkgnum = $1; + +my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} ); #needs agent virt + +my $contactnum = $cgi->param('contactnum'); +$contactnum =~ /^(-?\d*)$/ or die "Illegal contactnum"; +$contactnum = $1; + +my $past_method = $cust_pkg->contactnum ? 'changed' : 'added'; + +my $error = ''; + +if ( $contactnum == -1 ) { + + #little false laziness w/edit/process/quick-cust_pkg.cgi, also the whole + # thing should be a single transaction + my $contact = new FS::contact { + 'custnum' => $cust_pkg->custnum, + map { $_ => scalar($cgi->param("contactnum_$_")) } qw( first last ) + }; + $error = $contact->insert; + $cust_pkg->contactnum( $contact->contactnum ); + +} else { + $cust_pkg->contactnum($contactnum); +} + +$error ||= $cust_pkg->replace; + +if ($error) { + $cgi->param('error', $error); + print $cgi->redirect(popurl(2). "change_pkg_contact.html?". $cgi->query_string ); +} + +</%init> diff --git a/httemplate/misc/process/delete-customer.cgi b/httemplate/misc/process/delete-customer.cgi deleted file mode 100755 index 12011311a..000000000 --- a/httemplate/misc/process/delete-customer.cgi +++ /dev/null @@ -1,33 +0,0 @@ -%if ( $error ) { -% $cgi->param('error', $error); -<% $cgi->redirect(popurl(2). "delete-customer.cgi?". $cgi->query_string ) %> -%} elsif ( $new_custnum ) { -<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$new_custnum") %> -%} else { -<% $cgi->redirect(popurl(3)) %> -%} -<%init> - -my $conf = new FS::Conf; -die "Customer deletions not enabled in configuration" - unless $conf->exists('deletecustomers'); - -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Delete customer'); - -$cgi->param('custnum') =~ /^(\d+)$/; -my $custnum = $1; -my $new_custnum; -if ( $cgi->param('new_custnum') ) { - $cgi->param('new_custnum') =~ /^(\d+)$/ - or die "Illegal new customer number: ". $cgi->param('new_custnum'); - $new_custnum = $1; -} else { - $new_custnum = ''; -} -my $cust_main = qsearchs( 'cust_main', { 'custnum' => $custnum } ) - or die "Customer not found: $custnum"; - -my $error = $cust_main->delete('new_custnum' => $new_custnum); - -</%init> diff --git a/httemplate/misc/process/manage_cust_email.html b/httemplate/misc/process/manage_cust_email.html new file mode 100644 index 000000000..5bf1470d1 --- /dev/null +++ b/httemplate/misc/process/manage_cust_email.html @@ -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/process/payment.cgi b/httemplate/misc/process/payment.cgi index 506e26684..981614e76 100644 --- a/httemplate/misc/process/payment.cgi +++ b/httemplate/misc/process/payment.cgi @@ -210,7 +210,15 @@ if ( $cgi->param('save') ) { $new->set( 'paycvv' => ''); } - $new->set( $_ => $cgi->param($_) ) foreach @{$payby2fields{$payby}}; + if ( $payby eq 'CARD' ) { + my $bill_location = FS::cust_location->new; + $bill_location->set( $_ => $cgi->param($_) ) + foreach @{$payby2fields{$payby}}; + $new->set('bill_location' => $bill_location); + # will do nothing if the fields are all unchanged + } else { + $new->set( $_ => $cgi->param($_) ) foreach @{$payby2fields{$payby}}; + } my $error = $new->replace($cust_main); errorpage("payment processed successfully, but error saving info: $error") diff --git a/httemplate/misc/process/void-cust_bill.html b/httemplate/misc/process/void-cust_bill.html index 899901a50..accee27fd 100755 --- a/httemplate/misc/process/void-cust_bill.html +++ b/httemplate/misc/process/void-cust_bill.html @@ -1,6 +1,6 @@ %if ( $error ) { % $cgi->param('error', $error); -<% $cgi->redirect(popurl(1). "void-cust_bill.html?". $cgi->query_string ) %> +<% $cgi->redirect(popurl(2). "void-cust_bill.html?". $cgi->query_string ) %> %} else { <& /elements/header-popup.html, 'Invoice voided' &> <SCRIPT TYPE="text/javascript"> diff --git a/httemplate/misc/regions.cgi b/httemplate/misc/regions.cgi new file mode 100644 index 000000000..31538b08e --- /dev/null +++ b/httemplate/misc/regions.cgi @@ -0,0 +1,26 @@ +<% encode_json(\@regions) %>\ +<%init> + +my( $state, $svcpart ) = $cgi->param('arg'); + +my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } ); +die "unknown svcpart $svcpart" unless $part_svc; + +my @regions = (); +if ( $state ) { + + my @exports = $part_svc->part_export_did; + if ( scalar(@exports) > 1 ) { + die "more than one DID-providing export attached to svcpart $svcpart"; + } elsif ( ! @exports ) { + die "no DID providing export attached to svcpart $svcpart"; + } + my $export = $exports[0]; + + my $something = $export->get_dids('state'=>$state); + + @regions = @{ $something }; + +} + +</%init> diff --git a/httemplate/misc/xmlhttp-address_standardize.html b/httemplate/misc/xmlhttp-address_standardize.html new file mode 100644 index 000000000..618265364 --- /dev/null +++ b/httemplate/misc/xmlhttp-address_standardize.html @@ -0,0 +1,51 @@ +<% encode_json($return) %>\ +<%init> + +local $SIG{__DIE__}; #disable Mason error trap + +my $DEBUG = 0; + +my $conf = new FS::Conf; + +my $sub = $cgi->param('sub'); + +warn $cgi->param('arg') if $DEBUG; + +my %old = %{ decode_json($cgi->param('arg')) } + or die "bad argument '".$cgi->param('arg')."'"; + +my %new; + +my @prefixes = (''); +if ( $old{same} ) { + @prefixes = ('bill_'); +} elsif ( $old{billship} ) { + @prefixes = ('bill_', 'ship_'); +} +my $all_same = 1; +foreach my $pre ( @prefixes ) { + + my $location = { + map { $_ => $old{$pre.$_} } + qw( company address1 address2 city state zip country ) + }; + + my $cache = eval { FS::GeocodeCache->standardize($location) }; + $cache->set_coord; + # don't do set_censustract here, though censustract may be set by now + + foreach ( keys(%$cache) ) { + $new{$pre.$_} = $cache->get($_); + } + + foreach ( qw(address1 address2 city state zip country) ) { + $all_same = 0 if ( $new{$pre.$_} ne $old{$pre.$_} ); + last if !$all_same; + } + + $all_same = 0 if $new{$pre.'error'}; +} + +my $return = { old => \%old, new => \%new, all_same => $all_same }; +warn "result:\n".encode_json($return) if $DEBUG; +</%init> diff --git a/httemplate/misc/xmlhttp-calculate_taxes.html b/httemplate/misc/xmlhttp-calculate_taxes.html index d3dc36acf..ed7bd0173 100644 --- a/httemplate/misc/xmlhttp-calculate_taxes.html +++ b/httemplate/misc/xmlhttp-calculate_taxes.html @@ -1,4 +1,4 @@ -<% objToJson($return) %> +<% encode_json($return) %>\ <%init> my $DEBUG = 0; diff --git a/httemplate/misc/xmlhttp-cust_bill-search.html b/httemplate/misc/xmlhttp-cust_bill-search.html index 46f15d1ab..6082dc771 100644 --- a/httemplate/misc/xmlhttp-cust_bill-search.html +++ b/httemplate/misc/xmlhttp-cust_bill-search.html @@ -1,18 +1,40 @@ -<% encode_json(\@return) %> +<% encode_json(\@return) %>\ <%init> my $curuser = $FS::CurrentUser::CurrentUser; die 'access denied' unless $curuser->access_right('View invoices'); my @return; +my $date_format = FS::Conf->new->config('date_format') || '%m/%d/%Y'; if ( $cgi->param('sub') eq 'custnum_search_open' ) { my $custnum = $cgi->param('arg'); - #warn "searching invoices for $custnum\n"; - my $cust_main = FS::cust_main->by_key($custnum); - @return = map { - +{ $_->hash, - 'owed' => $_->owed } - } $cust_main->open_cust_bill - if $curuser->agentnums_href->{ $cust_main->agentnum }; + if ( $custnum =~ /^(\d+)$/ ) { +#warn "searching invoices for $custnum\n"; + my $cust_main = FS::cust_main->by_key($custnum); + if ( $curuser->agentnums_href->{ $cust_main->agentnum } ) { + my @open_bills = $cust_main->open_cust_bill; + my $invnum_len; + my $owed_len; + my $date_len; + foreach my $cust_bill (@open_bills) { + my $invnum = $cust_bill->invnum; + my $owed = $cust_bill->owed; + my $date = time2str($date_format, $cust_bill->_date); + $invnum_len = length($invnum) if length($invnum) > $invnum_len; + $owed_len = length($owed) if length($owed) > $owed_len; + $date_len = length($date) if length($date) > $date_len; + + push @return, { $cust_bill->hash, + 'owed' => $owed, + 'date' => $date }; + } + my $format = '%' . $invnum_len . 'd - %' . $date_len . 's - '. + (FS::Conf->new->config('money_char') || '$') . + '%' . $owed_len . '.2f'; + foreach (@return) { + $_->{label} = sprintf($format, $_->{invnum}, $_->{date}, $_->{owed}); + } + } #if agentnum + } #if $custnum } </%init> diff --git a/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html new file mode 100644 index 000000000..c0db3e2c4 --- /dev/null +++ b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html @@ -0,0 +1,123 @@ +<% encode_json($return) %>\ +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" unless $curuser->access_right('Credit line items'); + +my $DEBUG = 0; + +my $conf = new FS::Conf; + +my $sub = $cgi->param('sub'); + +my $return = {}; + +if ( $sub eq 'calculate_taxes' ) { + + { + + my %arg = $cgi->param('arg'); + $return = \%arg; + warn join('', map "$_: $arg{$_}\n", keys %arg ) + if $DEBUG; + + #some false laziness w/cust_credit::credit_lineitems + + my $cust_main = qsearchs({ + 'table' => 'cust_main', + 'hashref' => { 'custnum' => $arg{custnum} }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, + }) or die 'unknown customer'; + + my @billpkgnums = split(',', $arg{billpkgnums}); + my @setuprecurs = split(',', $arg{setuprecurs}); + my @amounts = split(',', $arg{amounts}); + + my @cust_bill_pkg = (); + my $taxlisthash = {}; + while ( @billpkgnums ) { + my $billpkgnum = shift @billpkgnums; + my $setuprecur = shift @setuprecurs; + my $amount = shift @amounts; + + my $cust_bill_pkg = qsearchs({ + 'table' => 'cust_bill_pkg', + 'hashref' => { 'billpkgnum' => $billpkgnum }, + 'addl_from' => 'LEFT JOIN cust_bill USING (invnum)', + 'extra_sql' => 'AND custnum = '. $cust_main->custnum, + }) or die "unknown billpkgnum $billpkgnum"; + + #shouldn't be passed# next if $cust_bill_pkg->pkgnum == 0; + + if ( $setuprecur eq 'setup' ) { + $cust_bill_pkg->setup($amount); + $cust_bill_pkg->recur(0); + $cust_bill_pkg->unitrecur(0); + $cust_bill_pkg->type(''); + } else { + $cust_bill_pkg->recur($amount); + $cust_bill_pkg->setup(0); + $cust_bill_pkg->unitsetup(0); + } + + push @cust_bill_pkg, $cust_bill_pkg; + + my $part_pkg = $cust_bill_pkg->part_pkg; + $cust_main->_handle_taxes( $part_pkg, + $taxlisthash, + $cust_bill_pkg, + $cust_bill_pkg->cust_pkg, + $cust_bill_pkg->cust_bill->_date, + $cust_bill_pkg->cust_pkg->pkgpart, + ); + + } + + if ( @cust_bill_pkg ) { + + my $listref_or_error = + $cust_main->calculate_taxes( \@cust_bill_pkg, $taxlisthash, $cust_bill_pkg[0]->cust_bill->_date ); + + unless ( ref( $listref_or_error ) ) { + $return->{error} = $listref_or_error; + last; + } + + my @taxlines = (); + my $taxtotal = 0; + $return->{taxlines} = \@taxlines; + foreach my $taxline ( @$listref_or_error ) { + my $amount = $taxline->setup; + my $desc = $taxline->desc; + foreach my $location ( @{$taxline->cust_bill_pkg_tax_location}, @{$taxline->cust_bill_pkg_tax_rate_location} ) { + my $taxlocnum = $location->locationnum || ''; + my $taxratelocnum = $location->taxratelocationnum || ''; + $location->cust_bill_pkg_desc($taxline->desc); #ugh @ that kludge + $taxtotal += $location->amount; + push @taxlines, + #[ $location->desc, $taxline->setup, $taxlocnum, $taxratelocnum ]; + [ $location->desc, $location->amount, $taxlocnum, $taxratelocnum ]; + $amount -= $location->amount; + } + if ($amount > 0) { + $taxtotal += $amount; + push @taxlines, + [ $taxline->itemdesc. ' (default)', sprintf('%.2f', $amount), '', '' ]; + } + } + + $return->{taxlines} = \@taxlines; + $return->{taxtotal} = sprintf('%.2f', $taxtotal); + + } else { + + $return->{taxlines} = []; + $return->{taxtotal} = '0.00'; + + } + + } + +} + +</%init> diff --git a/httemplate/misc/xmlhttp-cust_main-address_standardize.html b/httemplate/misc/xmlhttp-cust_main-address_standardize.html deleted file mode 100644 index d0627cd59..000000000 --- a/httemplate/misc/xmlhttp-cust_main-address_standardize.html +++ /dev/null @@ -1,93 +0,0 @@ -<% objToJson($return) %> -<%init> - -my $DEBUG = 0; - -my $conf = new FS::Conf; - -my $sub = $cgi->param('sub'); - -my $return = {}; - -if ( $sub eq 'address_standardize' ) { - - my %arg = $cgi->param('arg'); - $return = \%arg; - warn join('', map "$_: $arg{$_}\n", keys %arg ) - if $DEBUG; - - my $userid = $conf->config('usps_webtools-userid'); - my $password = $conf->config('usps_webtools-password'); - - if ( length($userid) && length($password) ) { - - my $verifier = Business::US::USPS::WebTools::AddressStandardization->new( { - UserID => $userid, #$ENV{USPS_WEBTOOLS_USERID}, - Password => $password, #$ENV{USPS_WEBTOOLS_PASSWORD}, - #Testing => 1, - } ); - - foreach my $pre ( '', 'ship_' ) { - next unless ($pre || !$arg{onlyship}); - - my($zip5, $zip4) = split('-',$arg{$pre.'zip'}); - - my %usps_args = ( - FirmName => $arg{$pre.'company'}, - Address2 => $arg{$pre.'address1'}, - Address1 => $arg{$pre.'address2'}, - City => $arg{$pre.'city'}, - State => $arg{$pre.'state'}, - Zip5 => $zip5, - Zip4 => $zip4, - ); - warn join('', map "$_: $usps_args{$_}\n", keys %usps_args ) - if $DEBUG; - - my $hash = $verifier->verify_address( %usps_args ); - - warn $verifier->response - if $DEBUG; - - unless ( $verifier->is_error ) { - - my $zip = $hash->{Zip5}; - $zip .= '-'. $hash->{Zip4} if $hash->{Zip4} =~ /\d/; - - $return = { - %$return, - "new_$pre".'company' => $hash->{FirmName}, - "new_$pre".'address1' => $hash->{Address2}, - "new_$pre".'address2' => $hash->{Address1}, - "new_$pre".'city' => $hash->{City}, - "new_$pre".'state' => $hash->{State}, - "new_$pre".'zip' => $zip, - }; - - my @fields = (qw( company address1 address2 city state zip )); #hmm - - my $changed = - scalar( grep { $return->{$pre.$_} ne $return->{"new_$pre$_"} } - @fields - ) - ? 1 : 0; - - $return->{$pre.'address_standardized'} = $changed; - - } else { - - $return->{$pre.'error'} = "USPS WebTools error: ". - $verifier->{error}{description}; - - - } - - } - - } - - $return; - -} - -</%init> diff --git a/httemplate/misc/xmlhttp-cust_main-censustract.html b/httemplate/misc/xmlhttp-cust_main-censustract.html index 4b00898da..4c708a4c4 100644 --- a/httemplate/misc/xmlhttp-cust_main-censustract.html +++ b/httemplate/misc/xmlhttp-cust_main-censustract.html @@ -1,4 +1,4 @@ -<% objToJson($return) %> +<% encode_json($return) %>\ <%init> my %arg = $cgi->param('arg'); diff --git a/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi index b524e69fc..36b18b455 100644 --- a/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi +++ b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi @@ -16,7 +16,7 @@ % } % } % -<% objToJson($return) %> +<% encode_json($return) %>\ % } <%init> diff --git a/httemplate/misc/xmlhttp-cust_main-duplicates.html b/httemplate/misc/xmlhttp-cust_main-duplicates.html index 6654b3e39..7ee00af66 100644 --- a/httemplate/misc/xmlhttp-cust_main-duplicates.html +++ b/httemplate/misc/xmlhttp-cust_main-duplicates.html @@ -8,9 +8,9 @@ Choose an existing customer <TR> <TD ALIGN="right" VALIGN="top"><B><% $custnum %>: </B></TD> <TD ALIGN="left"> - <% $_->name %>—<B><FONT COLOR="#<%$_->statuscolor%>"><%$_->ucfirst_cust_status%></FONT></B><BR> -<% $_->address1 %><BR> -<% $_->city %>, <% $_->state %> <% $_->zip %> + <% $_->name |h %>—<B><FONT COLOR="#<%$_->statuscolor%>"><%$_->ucfirst_cust_status%></FONT></B><BR> +<% $_->address1 |h %><BR> +<% $_->city |h %>, <% $_->state %> <% $_->zip %> </TD> <TD ALIGN="center"> <INPUT TYPE="radio" NAME="dup_custnum" VALUE="<%$custnum%>"> diff --git a/httemplate/misc/xmlhttp-cust_main-email_search.html b/httemplate/misc/xmlhttp-cust_main-email_search.html new file mode 100644 index 000000000..0d830826c --- /dev/null +++ b/httemplate/misc/xmlhttp-cust_main-email_search.html @@ -0,0 +1,29 @@ +<% encode_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> diff --git a/httemplate/misc/xmlhttp-cust_main-search.cgi b/httemplate/misc/xmlhttp-cust_main-search.cgi index acf7e70e2..73c9ff8ec 100644 --- a/httemplate/misc/xmlhttp-cust_main-search.cgi +++ b/httemplate/misc/xmlhttp-cust_main-search.cgi @@ -5,7 +5,7 @@ % # cust_main-agent_custid-format') eq 'ww?d+' % $return = findbycustnum_or_agent_custid($1); % } -<% objToJson($return) %> +<% encode_json($return) %>\ % } elsif ( $sub eq 'smart_search' ) { % % my $string = $cgi->param('arg'); @@ -22,14 +22,14 @@ % @cust_main % ]; % -<% objToJson($return) %> +<% encode_json($return) %>\ % } elsif ( $sub eq 'invnum_search' ) { % % my $string = $cgi->param('arg'); % if ( $string =~ /^(\d+)$/ ) { % my $inv = qsearchs('cust_bill', { 'invnum' => $1 }); % my $return = $inv ? findbycustnum($inv->custnum) : []; -<% objToJson($return) %> +<% encode_json($return) %>\ % } else { #return nothing [] % } @@ -47,7 +47,7 @@ % city => $_->city, % }; % } -<% objToJson($return) %> +<% encode_json($return) %>\ % } <%init> diff --git a/httemplate/misc/xmlhttp-mib-browse.html b/httemplate/misc/xmlhttp-mib-browse.html new file mode 100644 index 000000000..f3084ff6f --- /dev/null +++ b/httemplate/misc/xmlhttp-mib-browse.html @@ -0,0 +1,161 @@ +%#<% Data::Format::HTML->new->format($index{by_path}) %> +% my $json = "JSON"->new->canonical; +<% $json->encode($result) %> +<%init> +#<%once> #enable me in production +use SNMP; +SNMP::initMib(); +my $mib = \%SNMP::MIB; + +# make an index of the leaf nodes +my %index = ( + by_objectID => {}, # {.1.3.6.1.2.1.1.1} + by_fullname => {}, # {iso.org.dod.internet.mgmt.mib-2.system.sysDescr} + by_path => {}, # {iso}{org}{dod}{internet}{mgmt}{mib-2}{system}{sysDescr} + module => {}, #{SNMPv2-MIB}{by_path}{iso}{org}... + #{SNMPv2-MIB}{by_fullname}{iso.org...} +); + +my %name_of_oid = (); # '.1.3.6.1' => 'iso.org.dod.internet' + +# build up path names +my $fullname; +$fullname = sub { + my $oid = shift; + return $name_of_oid{$oid} if exists $name_of_oid{$oid}; + + my $object = $mib->{$oid}; + my $myname = '.' . $object->{label}; + # cut off the last element and recurse + $oid =~ /^(\.[\d\.]+)?(\.\d+)$/; + if ( length($1) ) { + $myname = $fullname->($1) . $myname; + } + return $name_of_oid{$oid} = $myname +}; + +my @oids = keys(%$mib); # dotted numeric OIDs +foreach my $oid (@oids) { + my $object = {}; + %$object = %{ $mib->{$oid} }; # untie it + # and remove references + delete $object->{parent}; + delete $object->{children}; + delete $object->{nextNode}; + $index{by_objectID}{$oid} = $object; + my $myname = $fullname->($oid); + $object->{fullname} = $myname; + $index{by_fullname}{$myname} = $object; + my $moduleID = $object->{moduleID}; + $index{module}{$moduleID} ||= { by_fullname => {}, by_path => {} }; + $index{module}{$moduleID}{by_fullname}{$myname} = $object; +} +my @names = sort {$a cmp $b} keys %{ $index{by_fullname} }; +foreach my $myname (@names) { + my $obj = $index{by_fullname}{$myname}; + my $moduleID = $obj->{moduleID}; + my @parts = split('\.', $myname); + shift @parts; # always starts with an empty string + for ($index{by_path}, $index{module}{$moduleID}{by_path}) { + my $subindex = $_; + for my $this_part (@parts) { + $subindex = $subindex->{$this_part} ||= {}; + } + # $subindex now = $index{by_path}{foo}{bar}{baz}. + # set {''} = the object with that name. + # and set object $index{by_path}{foo}{bar}{baz}{''} = + # the object named .foo.bar.baz + $subindex->{''} = $obj; + } +} + +#</%once> +#<%init> +# no ACL for this +my $sub = $cgi->param('sub'); +my $result = {}; +if ( $sub eq 'search' ) { + warn "search: ".$cgi->param('arg')."\n"; + my ($module, $string) = split(':', $cgi->param('arg'), 2); + my $idx; # the branch of the index to use for this search + if ( $module eq 'ANY' ) { + $idx = \%index; + } elsif (exists($index{module}{$module}) ) { + $idx = $index{module}{$module}; + } else { + warn "unknown MIB moduleID: $module\n"; + $idx = {}; # will return nothing, because you've somehow sent a bad moduleID + } + if ( exists($index{by_fullname}{$string}) ) { + warn "exact match\n"; + # don't make this module-selective--if the path matches an existing + # object, return that object + %$result = %{ $index{by_fullname}{$string} }; # put the object info in $result + #warn Dumper $result; + } + my @choices; # menu options to return + if ( $string =~ /^[\.\d]+$/ ) { + # then this is a numeric path + # ignore the module filter, and return everything starting with $string + if ( $string =~ /^\./ ) { + @choices = grep /^\Q$string\E/, keys %{$index{by_objectID}}; + } else { + # or everything containing it + @choices = grep /\Q$string\E/, keys %{$index{by_objectID}}; + } + @choices = map { $index{by_objectID}{$_}->{fullname} } @choices; + } elsif ( $string eq '' or $string =~ /^\./ ) { + # then this is an absolute path + my @parts = split('\.', $string); + shift @parts; + my $subindex = $idx->{by_path}; + my $path = ''; + @choices = keys %$subindex; + # walk all the specified path parts + foreach my $this_part (@parts) { + # stop before walking off the map + last if !exists($subindex->{$this_part}); + $subindex = $subindex->{$this_part}; + $path .= '.' . $this_part; + @choices = grep {$_} keys %$subindex; + } + # skip uninteresting nodes: those that aren't accessible nodes (have no + # data type), and have only one path forward + while ( scalar(@choices) == 1 + and (!exists $subindex->{''} or $subindex->{''}->{type} eq '') ) { + + $subindex = $subindex->{ $choices[0] }; + $path .= '.' . $choices[0]; + @choices = grep {$_} keys %$subindex; + + } + + # if we are on an existing node, and the entered path didn't exactly + # match another node, return the current node as the result + if (!keys %$result and exists($subindex->{''})) { + %$result = %{ $subindex->{''} }; + } + # prepend the path up to this point + foreach (@choices) { + $_ = $path.'.'.$_; + # also label accessible nodes for the UI + if ( exists($subindex->{$_}{''}) and $subindex->{$_}{''}{'type'} ) { + $_ .= '-'; + } + } + # also include one level above the originally requested path, + # for tree-like navigation + if ( $string =~ /^(.+)\.[^\.]+/ ) { + unshift @choices, $1; + } + } else { + # then this is a full-text search + warn "/$string/\n"; + @choices = grep /\Q$string\E/i, keys(%{ $idx->{by_fullname} }); + } + @choices = sort @choices; + $result->{choices} = \@choices; +} elsif ( $sub eq 'get_module_list' ) { + $result = { modules => [ sort keys(%{ $index{module} }) ] }; +} +</%init> diff --git a/httemplate/misc/xmlhttp-ping.html b/httemplate/misc/xmlhttp-ping.html index e99303207..01baa3f57 100644 --- a/httemplate/misc/xmlhttp-ping.html +++ b/httemplate/misc/xmlhttp-ping.html @@ -1,4 +1,4 @@ -<% objToJson($return) %> +<% encode_json($return) %>\ <%init> my $conf = new FS::Conf; diff --git a/httemplate/misc/xmlhttp-svc_broadband-search.cgi b/httemplate/misc/xmlhttp-svc_broadband-search.cgi new file mode 100644 index 000000000..578e6140e --- /dev/null +++ b/httemplate/misc/xmlhttp-svc_broadband-search.cgi @@ -0,0 +1,22 @@ +% if ( $sub eq 'smart_search' ) { +% +% my $string = $cgi->param('arg'); +% my @svc_broadband = FS::svc_broadband->smart_search( $string ); +% my $return = [ map { my $cust_pkg = $_->cust_svc->cust_pkg; +% [ $_->svcnum, +% $_->label. ( $cust_pkg +% ? ' ('. $cust_pkg->cust_main->name. ')' +% : '' +% ), +% ]; +% } +% @svc_broadband, +% ]; +% +<% encode_json($return) %>\ +% } +<%init> + +my $sub = $cgi->param('sub'); + +</%init> |