diff options
Diffstat (limited to 'httemplate')
107 files changed, 2017 insertions, 665 deletions
diff --git a/httemplate/browse/part_export.cgi b/httemplate/browse/part_export.cgi index 91238a0fd..876633afc 100755 --- a/httemplate/browse/part_export.cgi +++ b/httemplate/browse/part_export.cgi @@ -38,6 +38,21 @@ function part_export_areyousure(href) { <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> <% $part_export->label_html %> (<A HREF="<% $p %>edit/part_export.cgi?<% $part_export->exportnum %>">edit</A> | <A HREF="javascript:part_export_areyousure('<% $p %>misc/delete-part_export.cgi?<% $part_export->exportnum %>')">delete</A>) +% if ( my @actions = $part_export->actions ) { + <P STYLE="position: absolute"> + Management: +% while (@actions) { +% my $label = shift @actions; +% my $path = shift @actions; + <& /elements/popup_link.html, + 'label' => $label, + 'action' => $fsurl.$path.'?'.$part_export->exportnum, + 'actionlabel' => $label, + &><% @actions ? ' | ' : '' %> +% } + </P> +% } #if @actions + </TD> <TD CLASS="inv" BGCOLOR="<% $bgcolor %>"> diff --git a/httemplate/edit/bulk-part_pkg.html b/httemplate/edit/bulk-part_pkg.html index 751bf7e5d..a1c6f0c9b 100644 --- a/httemplate/edit/bulk-part_pkg.html +++ b/httemplate/edit/bulk-part_pkg.html @@ -12,7 +12,7 @@ The following packages will be changed:<BR> % foreach my $pkgpart (sort keys(%part_pkg)) { <INPUT TYPE="hidden" NAME="pkgpart" VALUE="<% $pkgpart %>"> -<% $part_pkg{$pkgpart}->pkg_comment %><BR> +<% $part_pkg{$pkgpart}->pkg_comment |h %><BR> % } </DIV> <BR> diff --git a/httemplate/edit/cust_location.cgi b/httemplate/edit/cust_location.cgi index 80b27c2b3..b90ba66b8 100755 --- a/httemplate/edit/cust_location.cgi +++ b/httemplate/edit/cust_location.cgi @@ -7,20 +7,32 @@ ACTION="<% $p %>edit/process/cust_location.cgi" METHOD=POST> <INPUT TYPE="hidden" NAME="locationnum" VALUE="<% $locationnum %>"> <% ntable('#cccccc') %> -<% include('/elements/location.html', - 'object' => $cust_location, - 'no_asterisks' => 1, - ) %> +<& /elements/location.html, + 'object' => $cust_location, + 'no_asterisks' => 1, + # these are service locations, so they need all this stuff + 'enable_coords' => 1, + 'enable_district' => 1, + 'enable_censustract' => 1, +&> +<& /elements/standardize_locations.html, + 'form' => 'EditLocationForm', + 'callback' => 'document.EditLocationForm.submit();', +&> </TABLE> <BR> <SCRIPT TYPE="text/javascript"> -function areyousure() { - return confirm('Modify this service location?'); +function go() { +% if ( FS::Conf->new->config('address_standardize_method') ) { + standardize_locations(); +% } else { + confirm('Modify this service location?') && + document.EditLocationForm.submit(); +% } } </SCRIPT> -<INPUT TYPE="submit" VALUE="Submit" onclick="return areyousure()"> - +<INPUT TYPE="button" NAME="submitButton" VALUE="Submit" onclick="go()"> </FORM> </BODY> </HTML> diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi index 5671e7a2b..2908848c6 100755 --- a/httemplate/edit/cust_main.cgi +++ b/httemplate/edit/cust_main.cgi @@ -48,7 +48,7 @@ <TD STYLE="width:650px"> %#; padding-right:2px; vertical-align:top"> <FONT CLASS="fsinnerbox-title"><% mt('Billing address') |h %></FONT> - <TABLE CLASS="fsinnerbox"> + <TABLE CLASS="fsinnerbox" WIDTH="100%"> <& cust_main/before_bill_location.html, $cust_main &> <& /elements/location.html, object => $cust_main->bill_location, @@ -62,7 +62,6 @@ <TR><TD STYLE="height:40px"></TD></TR> <TR> <TD STYLE="width:650px"> -%#; padding-left:2px; vertical-align:top"> <FONT CLASS="fsinnerbox-title"><% mt('Service address') |h %></FONT> <INPUT TYPE="checkbox" NAME="same" @@ -72,19 +71,17 @@ VALUE="Y" <% $has_ship_address ? '' : 'CHECKED' %> ><% mt('same as billing address') |h %> - <TABLE CLASS="fsinnerbox" ID="table_ship_location"> - <& /elements/location.html, - object => $cust_main->ship_location, - prefix => 'ship_', - enable_censustract => 1, - enable_district => 1, - enable_coords => 1, - &> - </TABLE> - <TABLE CLASS="fsinnerbox" ID="table_ship_location_blank" - STYLE="display:none"> - <TR><TD></TD></TR> - </TABLE> + <DIV CLASS="fsinnerbox"> + <TABLE ID="table_ship_location" WIDTH="100%"> + <& /elements/location.html, + object => $cust_main->ship_location, + prefix => 'ship_', + enable_censustract => 1, + enable_district => 1, + enable_coords => 1, + &> + </TABLE> + </DIV> </TD> </TR></TABLE> @@ -94,16 +91,11 @@ function samechanged(what) { %# document.getElementById('table_ship_location').style.visibility = %# what.checked ? 'hidden' : 'visible'; var t1 = document.getElementById('table_ship_location'); - var t2 = document.getElementById('table_ship_location_blank'); if ( what.checked ) { - t2.style.width = t1.clientWidth + 'px'; - t2.style.height = t1.clientHeight + 'px'; - t1.style.display = 'none'; - t2.style.display = ''; + t1.style.visibility = 'hidden'; } else { - t2.style.display = 'none'; - t1.style.display = ''; + t1.style.visibility = 'visible' } } //samechanged(document.getElementById('same')); diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html index 6ba73ad68..5a66f0a60 100644 --- a/httemplate/edit/cust_main/billing.html +++ b/httemplate/edit/cust_main/billing.html @@ -521,7 +521,13 @@ <% $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> % } diff --git a/httemplate/edit/cust_pkg_quantity.html b/httemplate/edit/cust_pkg_quantity.html new file mode 100755 index 000000000..ec47ed6cb --- /dev/null +++ b/httemplate/edit/cust_pkg_quantity.html @@ -0,0 +1,49 @@ +<& /elements/header-popup.html, "Change Quantity" &> +<& /elements/error.html &> + +<FORM ACTION="<% $p %>edit/process/cust_pkg_quantity.html" METHOD=POST> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<& /elements/table-grid.html, 'bgcolor' => '#cccccc', 'cellpadding' => 2 &> + + <TR> + <TH ALIGN="right">Current package </TH> + <TD CLASS="grid"> + <% $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : '' %><B><% $part_pkg->pkg |h %></B> - <% $part_pkg->comment |h %> + </TD> + </TR> + +<& /elements/tr-input-text.html, + 'field' => 'quantity', + 'curr_value' => $cust_pkg->quantity, + 'label' => emt('Quantity') +&> + +</TABLE> + +<BR> +<INPUT NAME="submit" TYPE="submit" VALUE="Change"> + +</FORM> +</BODY> +</HTML> + +<%init> + +#some false laziness w/misc/change_pkg.cgi + +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 = FS::cust_pkg->by_key($pkgnum) or die "unknown pkgnum $pkgnum"; + +my $part_pkg = $cust_pkg->part_pkg; + +</%init> diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi index 4dd253be8..2897cf39d 100644 --- a/httemplate/edit/part_export.cgi +++ b/httemplate/edit/part_export.cgi @@ -2,6 +2,34 @@ <% include('/elements/error.html') %> +<SCRIPT TYPE="text/javascript"> + function svc_machine_changed (what, layer) { + if ( what.checked ) { + var machine = document.getElementById(layer + "_machine"); + var part_export_machine = + document.getElementById(layer + "_part_export_machine"); + if ( what.value == 'Y' ) { + machine.disabled = true; + part_export_machine.disabled = false; + } else if ( what.value == 'N' ) { + machine.disabled = false; + part_export_machine.disabled = true; + } + } + } + + function part_export_machine_changed (what, layer) { + var select_default = document.getElementById(layer + '_default_machine'); + var selected = select_default.value; + select_default.options.length = 0; + var choices = what.value.split("\n"); + for (var i = 0; i < choices.length; i++) { + select_default.options[i] = new Option(choices[i]); + } + select_default.value = selected; + } + +</SCRIPT> <FORM NAME="dummy"> <INPUT TYPE="hidden" NAME="exportnum" VALUE="<% $part_export->exportnum %>"> @@ -58,7 +86,6 @@ my $widget = new HTML::Widgets::SelectLayers( 'form_name' => 'dummy', 'form_action' => 'process/part_export.cgi', 'form_text' => [qw( exportnum exportname )], -# 'form_checkbox' => [qw()], 'html_between' => "</TD></TR></TABLE>\n", 'layer_callback' => sub { my $layer = shift; @@ -87,7 +114,8 @@ my $widget = new HTML::Widgets::SelectLayers( if ( $exports->{$layer}{svc_machine} ) { my( $N_CHK, $Y_CHK) = ( 'CHECKED', '' ); my( $machine_DISABLED, $pem_DISABLED) = ( '', 'DISABLED' ); - my $part_export_machine = ''; + my @part_export_machine; + my $default_machine = ''; if ( $cgi->param('svc_machine') eq 'Y' || $machine eq '_SVC_MACHINE' ) @@ -97,38 +125,43 @@ my $widget = new HTML::Widgets::SelectLayers( $machine_DISABLED = 'DISABLED'; $pem_DISABLED = ''; $machine = ''; - $part_export_machine = - $cgi->param('part_export_machine') - || join "\n", + @part_export_machine = $cgi->param('part_export_machine'); + if (!@part_export_machine) { + @part_export_machine = map $_->machine, grep ! $_->disabled, $part_export->part_export_machine; + } + $default_machine = + $cgi->param('default_machine_name') + || $part_export->default_export_machine; } - my $oc = qq(onChange="${layer}_svc_machine_changed(this)"); + my $oc = qq(onChange="svc_machine_changed(this, '$layer')"); $html .= qq[ <INPUT TYPE="radio" NAME="svc_machine" VALUE="N" $N_CHK $oc> <INPUT TYPE="text" NAME="machine" ID="${layer}_machine" VALUE="$machine" $machine_DISABLED> <BR> <INPUT TYPE="radio" NAME="svc_machine" VALUE="Y" $Y_CHK $oc> - Selected in each customer service from these choices - <TEXTAREA NAME="part_export_machine" ID="${layer}_part_export_machine" $pem_DISABLED>$part_export_machine</TEXTAREA> - - <SCRIPT TYPE="text/javascript"> - function ${layer}_svc_machine_changed (what) { - if ( what.checked ) { - var machine = document.getElementById("${layer}_machine"); - var part_export_machine = document.getElementById("${layer}_part_export_machine"); - if ( what.value == 'Y' ) { - machine.disabled = true; - part_export_machine.disabled = false; - } else if ( what.value == 'N' ) { - machine.disabled = false; - part_export_machine.disabled = true; - } - } - } - </SCRIPT> + <DIV STYLE="display:inline-block; vertical-align: top; text-align: right"> + Selected in each customer service from these choices: + <TEXTAREA STYLE="vertical-align: top" NAME="part_export_machine" + ID="${layer}_part_export_machine" + onchange="part_export_machine_changed(this, '$layer')" + $pem_DISABLED>] . + + join("\n", @part_export_machine) . + + qq[</TEXTAREA> + <BR> + Default: + <SELECT NAME="default_machine_name" ID="${layer}_default_machine"> ]; + foreach (@part_export_machine) { + $_ = encode_entities($_); # oh noes, XSS + my $sel = ($default_machine eq $_) ? ' SELECTED' : ''; + $html .= qq!<OPTION VALUE="$_"$sel>$_</OPTION>\n!; + } + $html .= '</DIV></SELECT>' } else { $html .= qq(<INPUT TYPE="text" NAME="machine" VALUE="$machine">). '<INPUT TYPE="hidden" NAME="svc_machine" VALUE=N">'; diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi index 7baf84d11..fadde354e 100755 --- a/httemplate/edit/part_pkg.cgi +++ b/httemplate/edit/part_pkg.cgi @@ -28,7 +28,8 @@ 'labels' => { 'pkgpart' => 'Package Definition', - 'pkg' => 'Package (customer-visible)', + 'pkg' => 'Package', + %locale_field_labels, 'comment' => 'Comment (customer-hidden)', 'classnum' => 'Package class', 'addon_classnum' => 'Restrict additional orders to package class', @@ -80,6 +81,7 @@ size => 40, #32 maxlength => 50, }, + #@locale_fields, {field=>'comment', type=>'text', size=>40 }, #32 { field => 'agentnum', type => 'select-agent', @@ -337,6 +339,22 @@ my $agent_clone_extra_sql = my $conf = new FS::Conf; my $taxproducts = $conf->exists('enable_taxproducts'); +my @locales = grep { ! /^en_/i } $conf->config('available-locales'); #should filter from the default locale lang instead of en_ +my %locale_labels = map { + ( $_ => 'Package -- '. FS::Locales->description($_) ) +} @locales; +@locales = + sort { $locale_labels{$a} cmp $locale_labels{$b} } + @locales; + +my $n = 0; +my %locale_field_labels = ( + map { + ( 'pkgpartmsgnum'. $n++. '_pkg' => $locale_labels{$_} ); + } + @locales +); + my $sth = dbh->prepare("SELECT COUNT(*) FROM part_pkg_report_option". " WHERE disabled IS NULL OR disabled = '' ") or die dbh->errstr; @@ -368,6 +386,42 @@ my $recur_show_zero_disabled = 1; my $pkgpart = ''; +my $splice_locale_fields = sub { + my( $fields, $pkey_value_callback, $pkg_value_callback ) = @_; + + my $n = 0; + my @locale_fields = ( + map { + my $pkey_value= $pkey_value_callback ? &$pkey_value_callback($_) : ''; + my $pkg_value = $pkg_value_callback + ? $pkg_value_callback eq 'cgiparam' + ? $cgi->param('pkgpartmsgnum'. $n. '_pkg') + : &$pkg_value_callback($_) + : ''; + ( + { field => 'pkgpartmsgnum'. $n, + type => 'hidden', + value => $pkey_value, + }, + { field => 'pkgpartmsgnum'. $n. '_locale', + type => 'hidden', + value => $_, + }, + { field => 'pkgpartmsgnum'. $n++. '_pkg', + type => 'text', + size => 40, + #maxlength => 50, + value => $pkg_value, + }, + ); + + } + @locales + ); + splice(@$fields, 7, 0, @locale_fields); #XXX 7 is arbitrary above + +}; + my $error_callback = sub { my($cgi, $object, $fields, $opt ) = @_; @@ -408,6 +462,16 @@ my $error_callback = sub { $pkgpart = $object->pkgpart; + &$splice_locale_fields( + $fields, + sub { + my $locale = shift; + my $part_pkg_msgcat = $object->part_pkg_msgcat($locale); + $part_pkg_msgcat ? $part_pkg_msgcat->pkgpartmsgnum : ''; + }, + 'cgiparam' + ); + }; my $new_hashref_callback = sub { { 'plan' => 'flat' }; }; @@ -473,6 +537,20 @@ my $edit_callback = sub { $pkgpart = $object->pkgpart; + &$splice_locale_fields( + $fields, + sub { + my $locale = shift; + my $part_pkg_msgcat = $object->part_pkg_msgcat($locale); + $part_pkg_msgcat ? $part_pkg_msgcat->pkgpartmsgnum : ''; + }, + sub { + my $locale = shift; + my $part_pkg_msgcat = $object->part_pkg_msgcat($locale); + $part_pkg_msgcat ? $part_pkg_msgcat->pkg : ''; + } + ); + }; my $new_callback = sub { @@ -487,6 +565,8 @@ my $new_callback = sub { $options{'suspend_bill'}=1 if $conf->exists('part_pkg-default_suspend_bill'); + &$splice_locale_fields($fields, '', ''); + }; my $clone_callback = sub { @@ -520,6 +600,16 @@ my $clone_callback = sub { foreach (qw( setup_fee recur_fee disable_line_item_date_ranges )); $recur_disabled = $object->freq ? 0 : 1; + + &$splice_locale_fields( + $fields, + '', + sub { + my $locale = shift; + my $part_pkg_msgcat = $object->part_pkg_msgcat($locale); + $part_pkg_msgcat ? $part_pkg_msgcat->pkg : ''; + } + ); }; my $discount_error_callback = sub { diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html index dfe52f109..a469beb7f 100644 --- a/httemplate/edit/payment_gateway.html +++ b/httemplate/edit/payment_gateway.html @@ -19,7 +19,7 @@ <SCRIPT TYPE="text/javascript"> - var modulesForNamespace = <% to_json(\%modules_for_namespace, {canonical=>1}) %>; + var modulesForNamespace = <% encode_json(\%modules_for_namespace, {canonical=>1}) %>; function changeNamespace(what) { var ns = what.value; var select_module = document.getElementById('gateway_module'); diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html index 2770f3283..77f261d56 100644 --- a/httemplate/edit/process/change-cust_pkg.html +++ b/httemplate/edit/process/change-cust_pkg.html @@ -32,11 +32,11 @@ my %change = map { $_ => scalar($cgi->param($_)) } $change{'keep_dates'} = 1; if ( $cgi->param('locationnum') == -1 ) { - my $cust_location = new FS::cust_location { + my $cust_location = FS::cust_location->new_or_existing({ 'custnum' => $cust_pkg->custnum, map { $_ => scalar($cgi->param($_)) } qw( address1 address2 city county state zip country ) - }; + }); $change{'cust_location'} = $cust_location; } diff --git a/httemplate/edit/process/cust_location.cgi b/httemplate/edit/process/cust_location.cgi index b9f93db8b..56c3968f6 100644 --- a/httemplate/edit/process/cust_location.cgi +++ b/httemplate/edit/process/cust_location.cgi @@ -28,11 +28,10 @@ my $cust_location = qsearchs({ }); die "unknown locationnum $locationnum" unless $cust_location; -my $new = FS::cust_location->new({ +my $new = FS::cust_location->new_or_existing({ custnum => $cust_location->custnum, prospectnum => $cust_location->prospectnum, - map { $_ => scalar($cgi->param($_)) } - qw( address1 address2 city county state zip country ) + map { $_ => scalar($cgi->param($_)) } FS::cust_main->location_fields }); my $error = $cust_location->move_to($new); diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi index 054973f23..c1f815550 100755 --- a/httemplate/edit/process/cust_main.cgi +++ b/httemplate/edit/process/cust_main.cgi @@ -83,10 +83,7 @@ for my $pre (qw(bill ship)) { } $hash{'custnum'} = $cgi->param('custnum'); warn Dumper \%hash if $DEBUG; - # if we can qsearchs it, then it's unchanged, so use that - $locations{$pre} = qsearchs('cust_location', \%hash) - || FS::cust_location->new( \%hash ); - + $locations{$pre} = FS::cust_location->new_or_existing(\%hash); } if ( ($cgi->param('same') || '') eq 'Y' ) { diff --git a/httemplate/edit/process/cust_pkg_quantity.html b/httemplate/edit/process/cust_pkg_quantity.html new file mode 100644 index 000000000..fb2657252 --- /dev/null +++ b/httemplate/edit/process/cust_pkg_quantity.html @@ -0,0 +1,33 @@ +% if ($error) { +% $cgi->param('error', $error); +% $cgi->redirect(popurl(3). 'edit/cust_pkg_quantity.html?'. $cgi->query_string ); +% } else { + + <& /elements/header-popup.html, "Quantity changed" &> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY> + </HTML> + +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Change customer package'); + +my $cust_pkg = qsearchs({ + 'table' => 'cust_pkg', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'pkgnum' => scalar($cgi->param('pkgnum')), }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); +die 'unknown pkgnum' unless $cust_pkg; + +$cgi->param('quantity') =~ /^(\d+)$/; +my $quantity = $1; +my $error = $cust_pkg->set_quantity($1); + +</%init> diff --git a/httemplate/edit/process/part_export.cgi b/httemplate/edit/process/part_export.cgi index bcb9c0df1..e0c470675 100644 --- a/httemplate/edit/process/part_export.cgi +++ b/httemplate/edit/process/part_export.cgi @@ -56,6 +56,7 @@ my $new = new FS::part_export ( { if ( $cgi->param('svc_machine') eq 'Y' ) { $new->machine('_SVC_MACHINE'); $new->part_export_machine_textarea( $cgi->param('part_export_machine') ); + $new->default_machine_name( $cgi->param('default_machine_name') ); } my $error; diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi index 2ac57f90b..932e33b1d 100755 --- a/httemplate/edit/process/part_pkg.cgi +++ b/httemplate/edit/process/part_pkg.cgi @@ -10,6 +10,7 @@ 'precheck_callback' => $precheck_callback, 'args_callback' => $args_callback, 'process_m2m' => \@process_m2m, + 'process_o2m' => \@process_o2m, ) %> <%init> @@ -244,4 +245,11 @@ if ( $cgi->param('pkgpart') || ! $conf->exists('agent_defaultpkg') ) { }; } +my @process_o2m = ( + { + 'table' => 'part_pkg_msgcat', + 'fields' => [qw( locale pkg )], + }, +); + </%init> diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi index 2dadbccdc..0cc17d36b 100644 --- a/httemplate/edit/process/quick-cust_pkg.cgi +++ b/httemplate/edit/process/quick-cust_pkg.cgi @@ -70,6 +70,9 @@ my $quantity = $1 || 1; $cgi->param('refnum') =~ /^(\d*)$/ or die 'illegal refnum '. $cgi->param('refnum'); my $refnum = $1; +$cgi->param('contactnum') =~ /^(\-?\d*)$/ + or die 'illegal contactnum '. $cgi->param('contactnum'); +my $contactnum = $1; $cgi->param('locationnum') =~ /^(\-?\d*)$/ or die 'illegal locationnum '. $cgi->param('locationnum'); my $locationnum = $1; @@ -109,6 +112,7 @@ my %hash = ( : '' ), 'refnum' => $refnum, + 'contactnum' => $contactnum, 'locationnum' => $locationnum, 'discountnum' => $discountnum, #for the create a new discount case @@ -142,11 +146,19 @@ if ( $quotationnum ) { my %opt = ( 'cust_pkg' => $cust_pkg ); + if ( $contactnum == -1 ) { + my $contact = FS::contact->new({ + 'custnum' => scalar($cgi->param('custnum')), + map { $_ => scalar($cgi->param("contactnum_$_")) } qw( first last ) + }); + $opt{'contact'} = $contact; + } + if ( $locationnum == -1 ) { - my $cust_location = new FS::cust_location { + my $cust_location = FS::cust_location->new_or_existing({ map { $_ => scalar($cgi->param($_)) } - qw( custnum address1 address2 city county state zip country geocode ) - }; + ('custnum', FS::cust_main->location_fields) + }); $opt{'cust_location'} = $cust_location; } diff --git a/httemplate/edit/process/svc_phone.html b/httemplate/edit/process/svc_phone.html index 27e975568..9983ea2cb 100644 --- a/httemplate/edit/process/svc_phone.html +++ b/httemplate/edit/process/svc_phone.html @@ -40,10 +40,10 @@ my $args_callback = sub { my %opt = (); if ( $cgi->param('locationnum') == -1 ) { - my $cust_location = new FS::cust_location { + my $cust_location = FS::cust_location->new_or_existing({ map { $_ => scalar($cgi->param($_)) } qw( custnum address1 address2 city county state zip country ) - }; + }); $opt{'cust_location'} = $cust_location; } diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi index 0d4b9897b..1b85460e6 100644 --- a/httemplate/edit/svc_broadband.cgi +++ b/httemplate/edit/svc_broadband.cgi @@ -104,8 +104,12 @@ my @fields = ( { field=>'sectornum', type=>'select-tower_sector', }, { field=>'routernum', type=>'select-router_block_ip' }, { field=>'mac_addr' , type=>'input-mac_addr' }, - qw( latitude longitude altitude vlan_profile - performance_profile authkey plan_id ) + qw( + latitude longitude altitude + radio_serialnum radio_location poe_location rssi suid + ), + { field=>'shared_svcnum', type=>'search-svc_broadband', }, + qw( vlan_profile performance_profile authkey plan_id ), ); if ( $conf->exists('svc_broadband-radius') ) { diff --git a/httemplate/elements/auto-table.html b/httemplate/elements/auto-table.html index 3a3bd405d..5118b91ff 100644 --- a/httemplate/elements/auto-table.html +++ b/httemplate/elements/auto-table.html @@ -50,7 +50,7 @@ var <%$pre%>next_rownum; var <%$pre%>set_rownum; var <%$pre%>addRow; var <%$pre%>deleteRow; -var <%$pre%>fieldorder = <% to_json($fieldorder) %>; +var <%$pre%>fieldorder = <% encode_json($fieldorder) %>; function <%$pre%>possiblyAddRow_factory(obj) { var callback = obj.onchange; @@ -190,7 +190,7 @@ function <%$pre%>init() { <%$pre%>template.appendChild(delete_cell); // preload rows - var rows = <% to_json(\@rows) %>; + var rows = <% encode_json(\@rows) %>; for (var i = 0; i < rows.length; i++) { <%$pre%>addRow(rows[i]); } diff --git a/httemplate/elements/change_history_common.html b/httemplate/elements/change_history_common.html index 232664e39..34ce70b6c 100644 --- a/httemplate/elements/change_history_common.html +++ b/httemplate/elements/change_history_common.html @@ -15,13 +15,7 @@ <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> diff --git a/httemplate/elements/contact.html b/httemplate/elements/contact.html index 490ba2303..3d5177612 100644 --- a/httemplate/elements/contact.html +++ b/httemplate/elements/contact.html @@ -2,9 +2,9 @@ <INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>"> - <TABLE> + <TABLE STYLE="display:inline"> <TR> -% if ( @contact_class ) { +% if ( @contact_class && ! $opt{name_only} ) { <TD> <SELECT NAME="<%$name%>_classnum" <% $onchange %>> <OPTION VALUE=""> @@ -106,6 +106,6 @@ foreach my $phone_type ( qsearch({table=>'phone_type', order_by=>'weight'}) ) { $label{'comment'} = 'Comment'; -my @fields = keys %label; +my @fields = $opt{'name_only'} ? qw( first last ) : keys %label; </%init> diff --git a/httemplate/elements/dashboard-toplist.html b/httemplate/elements/dashboard-toplist.html index f4a372519..b80af7883 100644 --- a/httemplate/elements/dashboard-toplist.html +++ b/httemplate/elements/dashboard-toplist.html @@ -169,7 +169,6 @@ if ( $FS::TicketSystem::system eq 'RT_Internal' ObjectCustomFieldValues.ObjectId = cust_tickets.Id ) GROUP BY cust_tickets.custnum, ObjectCustomFieldValues.Content"; - #warn $sql."\n"; } else { # no custom_priority_field $sql = "SELECT cust_tickets.custnum, @@ -181,10 +180,8 @@ if ( $FS::TicketSystem::system eq 'RT_Internal' my $sth = dbh->prepare($sql) or die dbh->errstr; $sth->execute or die $sth->errstr; while ( my $row = $sth->fetchrow_hashref ) { - #warn to_json($row)."\n"; $num_tickets_by_priority{ $row->{priority} }->{ $row->{custnum} } = $row->{num_tickets}; } } -#warn Dumper \%num_tickets_by_priority; </%init> diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 00c43bb09..5689b12d2 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -294,9 +294,11 @@ tie my %report_ticketing, 'Tie::IxHash', 'Advanced ticket reports' => [ $fsurl.'rt/Search/Build.html?NewQuery=1', 'List tickets by any criteria' ], ; -tie my %report_employees, 'Tie::IxHash', - 'Employee Commission Report' => [ $fsurl.'search/report_employee_commission.html', '' ], - 'Employee Audit Report' => [ $fsurl.'search/report_employee_audit.html', 'Employee audit report' ], +tie my %report_employees, 'Tie::IxHash'; +$report_employees{'Employee Commission Report'} = [ $fsurl.'search/report_employee_commission.html', '' ] + if $curuser->access_right('Employees: Commission Report'); +$report_employees{'Employee Audit Report'} = [ $fsurl.'search/report_employee_audit.html', 'Employee audit report' ] + if $curuser->access_right('Employees: Audit Report'); ; tie my %report_bill_event, 'Tie::IxHash', @@ -397,7 +399,7 @@ $report_menu{'Tickets'} = [ \%report_ticketing, 'Ticket reports' ] if $conf->config('ticket_system') ;#&& FS::TicketSystem->access_right(\%session, 'Something'); $report_menu{'Employees'} = [ \%report_employees, 'Employee reports' ] - if $curuser->access_right('Financial reports'); + if keys %report_employees; $report_menu{'Billing events'} = [ \%report_bill_event, 'Billing events' ] if $curuser->access_right('Billing event reports'); $report_menu{'Financial'} = [ \%report_financial, 'Financial reports' ] @@ -464,6 +466,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/elements/progress-init.html b/httemplate/elements/progress-init.html index 7a282a34c..cef54b824 100644 --- a/httemplate/elements/progress-init.html +++ b/httemplate/elements/progress-init.html @@ -108,7 +108,7 @@ function <%$key%>process () { function <%$key%>myCallback( jobnum ) { - overlib( OLiframeContent('<%$p%>elements/progress-popup.html?jobnum=' + jobnum + ';<%$url_or_message_link%>;formname=<%$formname%>' , 444, 168, '<% $popup_name %>'), CAPTION, 'Please wait...', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', CLOSECLICK, MIDX, 0, MIDY, 0 ); + overlib( OLiframeContent('<%$fsurl%>elements/progress-popup.html?jobnum=' + jobnum + ';<%$url_or_message_link%>;formname=<%$formname%>' , 444, 168, '<% $popup_name %>'), CAPTION, 'Please wait...', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', CLOSECLICK, MIDX, 0, MIDY, 0 ); } diff --git a/httemplate/elements/search-svc_broadband.html b/httemplate/elements/search-svc_broadband.html new file mode 100644 index 000000000..d83516172 --- /dev/null +++ b/httemplate/elements/search-svc_broadband.html @@ -0,0 +1,204 @@ +<%doc> + +Example: + + include( '/elements/search-svc_broadband.html, + 'field' => 'svcnum', + #slightly deprecated old synonym for field#'field_name'=>'svcnum', + 'find_button' => 1, #add a "find" button to the field + 'curr_value' => 54, #current value + 'value => 32, #deprecated synonym for curr_value + ); + +</%doc> +<INPUT TYPE="hidden" NAME="<% $field %>" ID="<% $field %>" VALUE="<% $value %>"> + +<!-- some false laziness w/ misc/batch-cust_pay.html, though not as bad as i'd thought at first... --> + +<INPUT TYPE = "text" + NAME = "<% $field %>_search" + ID = "<% $field %>_search" + SIZE = "32" + VALUE="<% $svc_broadband ? $svc_broadband->label : '(svcnum, ip or mac)' %>" + onFocus="clearhint_<% $field %>_search(this);" + onClick="clearhint_<% $field %>_search(this);" + onChange="smart_<% $field %>_search(this);" +> + +% if ( $opt{'find_button'} ) { + <INPUT TYPE = "button" + VALUE = 'Find', + NAME = "<% $field %>_findbutton" + onClick = "smart_<% $field %>_search(this.form.<% $field %>_search);" + > +% } + +<SELECT NAME="<% $field %>_select" ID="<% $field %>_select" STYLE="color:#ff0000; display:none" onChange="select_<% $field %>(this);"> +</SELECT> + +<% include('/elements/xmlhttp.html', + 'url' => $p. 'misc/xmlhttp-svc_broadband-search.cgi', + 'subs' => [ 'smart_search' ], + ) +%> + +<SCRIPT TYPE="text/javascript"> + + function clearhint_<% $field %>_search (what) { + + what.style.color = '#000000'; + + if ( what.value == '(svcnum, ip or mac)' ) + what.value = ''; + + if ( what.value.indexOf('Service not found: ') == 0 ) + what.value = what.value.substr(20); + + } + + var <% $field %>_search_active = false; + + function smart_<% $field %>_search(what) { + + if ( <% $field %>_search_active ) + return; + + var service = what.value; + + if ( service == 'searching...' || service == '' + || service.indexOf('Service not found: ') == 0 ) + return; + + if ( what.getAttribute('magic') == 'nosearch' ) { + what.setAttribute('magic', ''); + return; + } + + //what.value = 'searching...' + what.disabled = true; + what.style.color= '#000000'; + what.style.backgroundColor = '#dddddd'; + + var service_select = document.getElementById('<% $field %>_select'); + + //alert("search for customer " + customer); + + function <% $field %>_search_update(services) { + + //alert('customers returned: ' + customers); + + var serviceArray = eval('(' + services + ')'); + + what.disabled = false; + what.style.backgroundColor = '#ffffff'; + + if ( serviceArray.length == 0 ) { + + what.form.<% $field %>.value = ''; + + what.value = 'Service not found: ' + what.value; + what.style.color = '#ff0000'; + + what.style.display = ''; + service_select.style.display = 'none'; + + } else if ( serviceArray.length == 1 ) { + + //alert('one customer found: ' + customerArray[0]); + + what.form.<% $field %>.value = serviceArray[0][0]; + what.value = serviceArray[0][1]; + + what.style.display = ''; + service_select.style.display = 'none'; + + } else { + + //alert('multiple customers found, have to create select dropdown'); + + //blank the current list + for ( var i = service_select.length; i >= 0; i-- ) + service_select.options[i] = null; + + opt(service_select, '', 'Multiple services match "' + service + '" - select one', '#ff0000'); + + //add the multiple services + for ( var s = 0; s < serviceArray.length; s++ ) + opt(service_select, serviceArray[s][0], serviceArray[s][1], '#000000'); + + opt(service_select, 'cancel', '(Edit search string)', '#000000'); + + what.style.display = 'none'; + service_select.style.display = ''; + + } + + <% $field %>_search_active = false; + + } + + <% $field %>_search_active = true; + + smart_search( service, <% $field %>_search_update ); + + + } + + function select_<% $field %> (what) { + + var svcnum = what.options[what.selectedIndex].value; + var service = what.options[what.selectedIndex].text; + + var service_obj = document.getElementById('<% $field %>_search'); + + if ( svcnum == '' ) { + //what.style.color = '#ff0000'; + + } else if ( svcnum == 'cancel' ) { + + service_obj.style.color = '#000000'; + + what.style.display = 'none'; + service_obj.style.display = ''; + service_obj.focus(); + + } else { + + what.form.<% $field %>.value = svcnum; + + service_obj.value = service; + service_obj.style.color = '#000000'; + + what.style.display = 'none'; + service_obj.style.display = ''; + + } + + } + + 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; + } + +</SCRIPT> +<%init> + +my( %opt ) = @_; + +my $field = $opt{'field'} || $opt{'field_name'} || 'svcnum'; + +my $value = $opt{'curr_value'} || $opt{'value'}; + +my $svc_broadband = ''; +if ( $value ) { + $svc_broadband = qsearchs({ + 'table' => 'svc_broadband', + 'hashref' => { 'svcnum' => $value }, + #have to join to cust_main for an agentnum 'extra_sql' => " AND ". $FS::CurrentUser::CurrentUser->agentnums_sql, + }); +} + +</%init> diff --git a/httemplate/elements/select-tiered.html b/httemplate/elements/select-tiered.html index e332eeff8..3ff5471ae 100644 --- a/httemplate/elements/select-tiered.html +++ b/httemplate/elements/select-tiered.html @@ -124,13 +124,6 @@ my %opt = @_; my $pre = $opt{prefix} || ''; my $tiers = $opt{tiers} or die "no tiers defined"; -#my $json = JSON->new()->canonical(); #sort -# something super weird and broken going on with JSON's auto-loading, just -# using JSON alone errors out with -# Can't locate object method "new" via package "null" (perhaps you forgot to -# load "null"?) -# yes, "null", not "JSON". so instead, using JSON::XS explicity... -use JSON::XS; my $json = JSON::XS->new(); $json->canonical; diff --git a/httemplate/elements/selectlayers.html b/httemplate/elements/selectlayers.html index 01fd590ca..cb1d2d619 100644 --- a/httemplate/elements/selectlayers.html +++ b/httemplate/elements/selectlayers.html @@ -236,7 +236,7 @@ sub layer_callback { $date_noinit = 1; } else { - $include = "input-$include" if $include =~ /^(text|money)$/; + $include = "input-$include" if $include =~ /^(text|money|percentage)$/; $include = "tr-$include" unless $include eq 'hidden'; $html .= include( "/elements/$include.html", %$lf, diff --git a/httemplate/elements/tr-search-svc_broadband.html b/httemplate/elements/tr-search-svc_broadband.html new file mode 100644 index 000000000..cd7c11500 --- /dev/null +++ b/httemplate/elements/tr-search-svc_broadband.html @@ -0,0 +1,15 @@ +<& tr-td-label.html, @_ &> + + <TD <% $colspan %> <% $cell_style %> ID="<% $opt{input_id} || $opt{id}.'_input0' %>"><& search-svc_broadband.html, @_ &></TD> + +</TR> + +<%init> + +my %opt = @_; + +my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : ''; + +my $colspan = $opt{'colspan'} ? 'COLSPAN="'.$opt{'colspan'}.'"' : ''; + +</%init> diff --git a/httemplate/elements/tr-select-contact.html b/httemplate/elements/tr-select-contact.html new file mode 100644 index 000000000..d6bc67f36 --- /dev/null +++ b/httemplate/elements/tr-select-contact.html @@ -0,0 +1,204 @@ +<%doc> + +Example: + + include('/elements/tr-select-contact.html', + 'cgi' => $cgi, + + 'cust_main' => $cust_main, + #or + 'prospect_main' => $prospect_main, + + #optional + 'empty_label' => '(default contact)', + ) + +</%doc> + +<SCRIPT TYPE="text/javascript"> + + function contact_disable(what) { +% for (@contact_fields) { + what.form.<%$_%>.disabled = true; + var ftype = what.form.<%$_%>.tagName; + if( ftype == 'SELECT') changeSelect(what.form.<%$_%>, ''); + else what.form.<%$_%>.value = ''; + if( ftype != 'SELECT') what.form.<%$_%>.style.backgroundColor = '#dddddd'; +% } + } + + function contact_clear(what) { +% for (@contact_fields) { + var ftype = what.form.<%$_%>.tagName; + if( ftype == 'INPUT' ) what.form.<%$_%>.value = ''; +% } + } + + function contact_enable(what) { +% for (@contact_fields) { + what.form.<%$_%>.disabled = false; + var ftype = what.form.<%$_%>.tagName; + if( ftype != 'SELECT') what.form.<%$_%>.style.backgroundColor = '#ffffff'; +% } + } + + function contactnum_changed(what) { + var contactnum = what.options[what.selectedIndex].value; + if ( contactnum == -1 ) { //Add new contact + contact_clear(what); + + contact_enable(what); + return; + } + +% if ( $editable ) { + if ( contactnum == 0 ) { +% } + +% #sleep/wait until dropdowns are updated? + contact_disable(what); + +% if ( $editable ) { + } else { + +% #sleep/wait until dropdowns are updated? + contact_enable(what); + + } +% } + + } + + function changeSelect(what, value) { + for ( var i=0; i<what.length; i++) { + if ( what.options[i].value == value ) { + what.selectedIndex = i; + } + } + } + +</SCRIPT> + +<TR> + <<%$th%> ALIGN="right" VALIGN="top"><% $opt{'label'} || emt('Service contact') %></<%$th%>> + <TD VALIGN="top" COLSPAN=7> + <SELECT NAME = "contactnum" + ID = "contactnum" + STYLE = "vertical-align:top;margin:3px" + onchange = "contactnum_changed(this);" + > +% if ( $cust_main ) { + <OPTION VALUE=""><% $opt{'empty_label'} || '(customer default)' |h %> +% } +% +% foreach my $contact ( @contact ) { + <OPTION VALUE="<% $contact->contactnum %>" + <% $contactnum == $contact->contactnum ? 'SELECTED' : '' %> + ><% $contact->line |h %> +% } +% if ( $addnew ) { + <OPTION VALUE="-1" + <% $contactnum == -1 ? 'SELECTED' : '' %> + >New contact +% } + </SELECT> + +<% include('/elements/contact.html', + 'object' => $contact, + #'onchange' ? probably not + 'disabled' => $disabled, + 'name_only' => 1, + ) +%> + + </TD> +</TR> + +<SCRIPT TYPE="text/javascript"> + contactnum_changed(document.getElementById('contactnum')); +</SCRIPT> +<%init> + +#based on / kinda false laziness w/tr-select-cust_contact.html + +my $conf = new FS::Conf; + +my %opt = @_; +my $cgi = $opt{'cgi'}; +my $cust_pkg = $opt{'cust_pkg'}; +my $cust_main = $opt{'cust_main'}; +my $prospect_main = $opt{'prospect_main'}; +die "cust_main or prospect_main required" unless $cust_main or $prospect_main; + +my $contactnum = ''; +if ( $cgi->param('error') ) { + $cgi->param('contactnum') =~ /^(\-?\d*)$/ or die "illegal contactnum"; + $contactnum = $1; +} else { + if ( length($opt{'curr_value'}) ) { + $contactnum = $opt{'curr_value'}; + } elsif ($prospect_main) { + my @cust_contact = $prospect_main->cust_contact; + $contactnum = $cust_contact[0]->contactnum if scalar(@cust_contact)==1; + } else { #$cust_main + $cgi->param('contactnum') =~ /^(\-?\d*)$/ or die "illegal contactnum"; + $contactnum = $1; + } +} + +##probably could use explicit controls +#my $editable = $cust_main ? 0 : 1; #could use explicit control +my $editable = 0; +my $addnew = $cust_main ? 1 : ( $contactnum>0 ? 0 : 1 ); + +my @contact_fields = map "contactnum_$_", qw( first last ); + +my $contact; #the one that shows by default in the contact edit space +if ( $contactnum && $contactnum > 0 ) { + $contact = qsearchs('contact', { 'contactnum' => $contactnum } ) + or die "unknown contactnum"; +} else { + $contact = new FS::contact; + if ( $contactnum == -1 ) { + $contact->$_( $cgi->param($_) ) foreach @contact_fields; #XXX + } elsif ( $cust_pkg && $cust_pkg->contactnum ) { + my $pkg_contact = $cust_pkg->contact_obj; + $contact->$_( $pkg_contact->$_ ) foreach @contact_fields; #XXX why are we making a new one gagain?? + $opt{'empty_label'} ||= 'package contact: '.$pkg_contact->line; + } elsif ( $cust_main ) { + $contact = new FS::contact; #I think + } +} + +my $contact_sort = sub { + lc($a->last) cmp lc($b->last) + or lc($a->first) cmp lc($b->first) +}; + +my @contact; +push @contact, $cust_main->cust_contact if $cust_main; +push @contact, $prospect_main->contact if $prospect_main; +push @contact, $contact + if !$cust_main && $contact && $contact->contactnum > 0 + && ! grep { $_->contactnum == $contact->contactnum } @contact; + +@contact = sort $contact_sort grep !$_->disabled, @contact; + +$contact = $contact[0] + if ( $prospect_main ) + && !$opt{'is_optional'} + && @contact; + +my $disabled = + ( $contactnum < 0 + || ( $editable && $contactnum ) + || ( $prospect_main + && !$opt{'is_optional'} && !@contact && $addnew + ) + ) + ? '' + : 'DISABLED'; + +my $th = $opt{'no_bold'} ? 'TD' : 'TH'; + +</%init> diff --git a/httemplate/elements/tr-select-cust_location.html b/httemplate/elements/tr-select-cust_location.html index 7ffbd6c14..780bf96ad 100644 --- a/httemplate/elements/tr-select-cust_location.html +++ b/httemplate/elements/tr-select-cust_location.html @@ -153,25 +153,16 @@ Example: } } + var location_fields = <% encode_json(\@location_fields) %>; function update_location( string ) { - var hash = eval('('+string+')'); - document.getElementById('address1').value = hash['address1']; - document.getElementById('city').value = hash['city']; - document.getElementById('zip').value = hash['zip']; - -% if ( $opt{'alt_format'} ) { - changeSelect( document.getElementById('location_kind'), hash['location_kind']); - changeSelect( document.getElementById('location_type'), hash['location_type']); - document.getElementById('location_number').value = hash['location_number']; -% } else { - document.getElementById('address2').value = hash['address2']; -% } - - var country_el = document.getElementById('country'); - - changeSelect( country_el, hash['country'] ); - - country_changed( country_el, + var hash = JSON.parse(string); + for(var i = 0; i < location_fields.length; i++) { + var f = location_fields[i]; + if (hash[f] && document.getElementById(f)) { + document.getElementById(f).value = hash[f]; + } + } + country_changed( document.getElementById('country'), fix_state_factory( hash['state'], hash['county'] ) @@ -185,7 +176,7 @@ Example: <TD COLSPAN=7> <SELECT NAME = "locationnum" ID = "locationnum" - onChange = "locationnum_changed(this);" + onchange = "locationnum_changed(this);" > % if ( $cust_main ) { <OPTION VALUE="<% $cust_main->ship_locationnum %>"><% $opt{'empty_label'} || '(default service address)' |h %> @@ -258,9 +249,7 @@ if ( $cgi->param('error') ) { my $editable = $cust_main ? 0 : 1; #could use explicit control my $addnew = $cust_main ? 1 : ( $locationnum>0 ? 0 : 1 ); -my @location_fields = qw( address1 address2 city county state zip country - latitude longitude - ); +my @location_fields = FS::cust_main->location_fields; if ( $opt{'alt_format'} ) { push @location_fields, qw( location_type location_number location_kind ); } diff --git a/httemplate/elements/tr-select-voip_class.html b/httemplate/elements/tr-select-voip_class.html index dcc1487cc..afd3e1f8a 100644 --- a/httemplate/elements/tr-select-voip_class.html +++ b/httemplate/elements/tr-select-voip_class.html @@ -18,7 +18,8 @@ my @options = ( '' => '', 1 => 'VoIP without Broadband', 2 => 'VoIP with Broadband', - 3 => 'Wholesale VoIP' + 3 => 'Wholesale VoIP', + 4 => 'Local Exchange (non-VoIP)', ); </%init> 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..0b2f1f18c 100644 --- a/httemplate/misc/batch-cust_pay.html +++ b/httemplate/misc/batch-cust_pay.html @@ -23,15 +23,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 +95,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); } } @@ -198,7 +215,6 @@ function change_app_amount() { && amount_unapplied(rownum) > 0 ) { create_application_row(rownum, parseInt(appnum) + 1); - } } @@ -352,6 +368,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/change_pkg_contact.html b/httemplate/misc/change_pkg_contact.html new file mode 100755 index 000000000..d9da5beec --- /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> + <% $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> + <% $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 6ef7623b3..23099c421 100644 --- a/httemplate/misc/choose_tax_location.html +++ b/httemplate/misc/choose_tax_location.html @@ -11,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/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/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 7ed5c6607..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 diff --git a/httemplate/misc/maestro-customer_status.html b/httemplate/misc/maestro-customer_status.html index 8acae2b2a..a872d4997 100644 --- a/httemplate/misc/maestro-customer_status.html +++ b/httemplate/misc/maestro-customer_status.html @@ -1,4 +1,4 @@ -<% objToJson( $return ) %> +<% encode_json( $return ) %>\ <%init> my $return; 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/order_pkg.html b/httemplate/misc/order_pkg.html index 993ea366c..e09ba986d 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" 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 5084628eb..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'); 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/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/regions.cgi b/httemplate/misc/regions.cgi index 2450ea31a..31538b08e 100644 --- a/httemplate/misc/regions.cgi +++ b/httemplate/misc/regions.cgi @@ -1,4 +1,4 @@ -<% objToJson(\@regions) %> +<% encode_json(\@regions) %>\ <%init> my( $state, $svcpart ) = $cgi->param('arg'); diff --git a/httemplate/misc/xmlhttp-address_standardize.html b/httemplate/misc/xmlhttp-address_standardize.html index 15f266ab0..618265364 100644 --- a/httemplate/misc/xmlhttp-address_standardize.html +++ b/httemplate/misc/xmlhttp-address_standardize.html @@ -1,4 +1,4 @@ -<% encode_json($return) %> +<% encode_json($return) %>\ <%init> local $SIG{__DIE__}; #disable Mason error trap 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..c60a0b05c 100644 --- a/httemplate/misc/xmlhttp-cust_bill-search.html +++ b/httemplate/misc/xmlhttp-cust_bill-search.html @@ -1,4 +1,4 @@ -<% encode_json(\@return) %> +<% encode_json(\@return) %>\ <%init> my $curuser = $FS::CurrentUser::CurrentUser; @@ -6,13 +6,15 @@ die 'access denied' unless $curuser->access_right('View invoices'); my @return; 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); + @return = map { + +{ $_->hash, + 'owed' => $_->owed } + } $cust_main->open_cust_bill + if $curuser->agentnums_href->{ $cust_main->agentnum }; + } } </%init> diff --git a/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html index f618d55ba..c0db3e2c4 100644 --- a/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html +++ b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html @@ -1,4 +1,4 @@ -<% to_json($return) %> +<% encode_json($return) %>\ <%init> my $curuser = $FS::CurrentUser::CurrentUser; 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-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-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> diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html index 84f0832bf..6b94f7175 100644 --- a/httemplate/pref/pref-process.html +++ b/httemplate/pref/pref-process.html @@ -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 diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html index 9537fed34..5babb0181 100644 --- a/httemplate/pref/pref.html +++ b/httemplate/pref/pref.html @@ -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> diff --git a/httemplate/search/477.html b/httemplate/search/477.html index 04764c1da..eed3df946 100755 --- a/httemplate/search/477.html +++ b/httemplate/search/477.html @@ -3,6 +3,14 @@ <Form_477_submission xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://specialreports.fcc.gov/wcb/Form477/XMLSchema-instance/form_477_upload_Schema.xsd" > % } else { #html <& /elements/header.html, "FCC Form 477 Results - $state" &> +%# XXX when we stop supporting IE8, add this to freeside.css using :nth-child +%# selectors, and remove it from everywhere else +<STYLE TYPE="text/css"> +.grid TH { background-color: #cccccc; padding: 0px 3px 2px; text-align: right } +.row0 TD { background-color: #eeeeee; padding: 0px 3px 2px; text-align: right } +.row1 TD { background-color: #ffffff; padding: 0px 3px 2px; text-align: right } +</STYLE> + <TABLE WIDTH="100%"> <TR> <TD></TD> @@ -38,8 +46,11 @@ % if ( $type eq 'xml' ) { <<% 'Part_IA_'. chr(65 + $tech) %>> % } -<& "477part${part}_summary.html", 'tech_code' => $tech, 'url' => $url &> -<& "477part${part}_detail.html", 'tech_code' => $tech, 'url' => $url &> +<& "477part${part}.html", + 'tech_code' => $tech, + 'url' => $url, + 'type' => $type +&> % if ( $type eq 'xml' ) { </<% 'Part_IA_'. chr(65 + $tech) %>> % } diff --git a/httemplate/search/477partIA.html b/httemplate/search/477partIA.html new file mode 100755 index 000000000..1cd0b70e0 --- /dev/null +++ b/httemplate/search/477partIA.html @@ -0,0 +1,165 @@ +% if ( $opt{'type'} eq 'xml' ) { +%# container element <Part_IA_$tech> is in 477.html +% my $col = 'a'; +% foreach ( @summary_row ) { +% my $el = $xml_prefix . $col . '1'; # PartIA_Aa1, PartIA_Ab1, etc. + <<% $el %>><% $_ %><<% "/$el" %>> +% $col++; +% } +% foreach my $col_data ( @data ) { +% my $row = 1; +% foreach my $cell ( @$col_data ) { +% my $el = $xml_prefix . $col . $row; # PartIA_Af1, PartIA_Af2... + <<% $el %>><% $cell->[0] %><<% "/$el" %>> +% if ( $percentages ) { +% $el = $xml_percent . $col . $row; # Part_p_IA_Af1, ... + <<% $el %>><% $cell->[1] %><<% "/$el" %>> +% } +% $row++; +% } # foreach $cell +% $col++; +% } # foreach $col_data +% } else { # not XML + +<H2><% $title %> totals</H2> +<& /elements/table-grid.html &> + <TR> +% foreach ( 'Total Connections', +% '% owned loop', +% '% billed to end users', +% '% residential', +% '% residential > 200 kbps') { + <TH WIDTH="20%"><% $_ |h %></TH> +% } + </TR> + <TR CLASS="row0"> +% foreach ( @summary_row ) { + <TD><% $_ %></TD> +% } + </TR> +</TABLE> +<H2><% $title %> breakdown by speed</H2> +<TABLE CLASS="grid" CELLSPACING=0> + <TR> + <TH WIDTH="12%"></TH> +% for (my $col = 0; $col < scalar(@download_option); $col++) { + <TH WIDTH="11%"> + <% $FS::Report::FCC_477::download[$col] |h %> + </TH> +% } + </TR> +% for (my $row = 0; $row < scalar(@upload_option); $row++) { + <TR CLASS="row<% $row % 2%>"> + <TD STYLE="text-align: left; font-weight: bold"> +% if ( $asymmetric ) { + <% $FS::Report::FCC_477::upload[$row] |h %> +% } + </TD> +% for (my $col = 0; $col < scalar(@download_option); $col++) { + <TD> + <% $data[$col][$row][0] %> +% if ( $percentages ) { + <BR><% $data[$col][$row][1] %> +% } + </TD> +% } # for $col + </TR> +% } # for $row +</TABLE> +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('List packages'); + +my %opt = @_; +my %search_hash; + +for ( qw(agentnum state) ) { + $search_hash{$_} = $cgi->param($_) if $cgi->param($_); +} +$search_hash{'status'} = 'active'; +$search_hash{'country'} = 'US'; +$search_hash{'classnum'} = [ $cgi->param('classnum') ]; + +# arrays of report_option_ numbers, running parallel to +# the download and upload speed arrays +my @download_option = $cgi->param('part1_column_option'); +my @upload_option = $cgi->param('part1_row_option'); + +my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi); + +my $total_count = 0; +my $total_residential = 0; +my $above_200 = 0; +my $tech_code = $opt{tech_code}; +my $technology = $FS::Report::FCC_477::technology[$tech_code] || 'unknown'; +my $title = "Part IA $technology"; +my $xml_prefix = 'PartIA_'. chr(65 + $tech_code); +my $xml_percent = 'Part_p_IA_'. chr(65 + $tech_code); # yes, seriously + +# whether to show the results as a matrix (upload speeds in rows) or a single +# row +my $asymmetric = 1; +if ( $technology eq 'Symmetric xDSL' or $technology eq 'Other Wireline' ) { + $asymmetric = 0; + @upload_option = ( undef ); +} +# whether to show residential percentages in each cell of the matrix +my $percentages = ($technology eq 'Terrestrial Mobile Wireless'); + +my $query = FS::cust_pkg->search(\%search_hash); +my $count_query = $query->{'count_query'}; + +my $is_residential = " AND COALESCE(cust_main.company, '') = ''"; +my $has_option = sub { + my $optionnum = shift; + $optionnum =~ /^\d+$/ ? + " AND EXISTS( + SELECT 1 FROM part_pkg_option + WHERE part_pkg_option.pkgpart = part_pkg.pkgpart + AND optionname = 'report_option_$optionnum' + AND optionvalue = '1' + )" : ''; +}; + +# limit to those that have technology option $tech_code +$count_query .= $has_option->($technology_option[$tech_code]); + +my @data; +for ( my $row = 0; $row < scalar @upload_option; $row++ ) { + for ( my $col = 0; $col < scalar @download_option; $col++ ) { + + my $this_count_query = $count_query . + $has_option->($upload_option[$row]) . + $has_option->($download_option[$col]); + + my $count = FS::Record->scalar_sql($this_count_query); + my $residential = FS::Record->scalar_sql($this_count_query . $is_residential); + + my $percent = sprintf('%.2f', $count ? 100 * $residential / $count : 0); + $data[$col][$row] = [ $count, $percent ]; + + $total_count += $count; + $total_residential += $residential; + $above_200 += $residential if $row > 0 or !$asymmetric; + } +} + +my $total_percentage = + sprintf("%.2f", $total_count ? 100*$total_residential/$total_count : 0); + +my $above_200_percentage = + sprintf("%.2f", $total_count ? 100*$above_200/$total_count : 0); + +my @summary_row = ( + $total_count, + 100.00, # own local loop--consistent with previous practice, but probably wrong + 100.00, # billed to end user--also wrong + $total_percentage, # residential percentage + $above_200_percentage, +); + +</%init> diff --git a/httemplate/search/477partIA_detail.html b/httemplate/search/477partIA_detail.html deleted file mode 100755 index 666032d0c..000000000 --- a/httemplate/search/477partIA_detail.html +++ /dev/null @@ -1,129 +0,0 @@ -<& elements/search.html, - 'html_init' => $html_init, - 'name' => 'lines', - 'query' => $query, - 'count_query' => $count_query, - 'really_disable_download' => 1, - 'disable_download' => 1, - 'nohtmlheader' => 1, - 'disable_total' => 1, - 'header' => [ '', @column_option_name ], - 'xml_elements' => [ @xml_elements ], - 'xml_omit_empty' => 1, - 'fields' => [ @fields ], - -&> -<%init> - -my $curuser = $FS::CurrentUser::CurrentUser; - -die "access denied" - unless $curuser->access_right('List packages'); - -my %opt = @_; -my %search_hash = (); - -for ( qw(agentnum magic state) ) { - $search_hash{$_} = $cgi->param($_) if $cgi->param($_); -} -$search_hash{'country'} = 'US'; - -$search_hash{'classnum'} = [ $cgi->param('classnum') ]; - -my @column_option = grep { /^\d+/ } $cgi->param('part1_column_option') - if $cgi->param('part1_column_option'); - -my @row_option = grep { /^\d+/ } $cgi->param('part1_row_option') - if $cgi->param('part1_row_option'); - -my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi); - -my @column_option_name = scalar(@column_option) - ? ( map { my $part_pkg_report_option = - qsearchs({ 'table' => 'part_pkg_report_option', - 'hashref' => { num => $_ }, - }); - $part_pkg_report_option ? $part_pkg_report_option->name - : 'no such report option'; - } @column_option - ) - : ( 'all packages' ); - -my $where = join(' OR ', map { "num = $_" } @row_option ); -my %row_option_name = $where ? - ( map { $_->num => $_->name } - qsearch({ 'table' => 'part_pkg_report_option', - 'hashref' => {}, - 'extra_sql' => "WHERE $where", - }) - ) : - (); - -my $tech_code = $opt{tech_code}; -my $technology = $FS::Report::FCC_477::technology[$tech_code] || 'unknown'; -my $html_init = "<H2>Part IA $technology breakdown by speeds</H2>"; -my $xml_prefix = 'PartIA_'. chr(65 + $tech_code); - -if ($cgi->param('_type') eq 'xml') { - #rotate data pi/2 - my @temp = @column_option; - @column_option = @row_option; - @row_option = @temp; -} - -my $query = 'SELECT '. join(' UNION ALL SELECT ',@row_option); -my $count_query = 'SELECT '. scalar(@row_option); - -my $xml_element = 'OOPS, I was never set'; -my $rowchar = 101; # 'e' -- rows are columns! (pi/2) - -my $value = sub { - my ($rowref, $column) = (shift, shift); - my $row = $rowref->[0]; - - if ($column eq 'name') { - return $row_option_name{$row} || 'no such report option'; - } elsif ( $column =~ /^(\d+)$/ ) { - my @report_option = ( $row || '', - $column_option[$column] || '', - $technology_option[$tech_code] || '', - ); - - my ( $count, $residential ) = FS::cust_pkg->fcc_477_count( - { %search_hash, 'report_option' => join(',', @report_option) } - ); - - my $percentage = sprintf('%.2f', $count ? 100 * $residential / $count : 0); - my $return = $count; - - if ($cgi->param('_type') eq 'xml') { - $rowchar++ if $column == 0; - $xml_element = $xml_prefix. chr($rowchar). ($column+1); - $return = '' if $count == 0 and $cgi->param('_type') eq 'xml'; - } else { - $return .= "<BR>$percentage% residential"; - } - - return $return; - } else { - return '<FONT SIZE="+1" COLOR="#ff0000">Bad call to column_value</FONT>'; - } -}; - -my @fields = map { my $ci = $_; sub { &{$value}(shift, $ci); } } - ( 'name', (0 .. $#column_option) ); -shift @fields if $cgi->param('_type') eq 'xml'; - -my @xml_elements = ( # -- columns are rows! (pi/2) - sub { return $xml_element; }, - sub { return $xml_element; }, - sub { return $xml_element; }, - sub { return $xml_element; }, - sub { return $xml_element; }, - sub { return $xml_element; }, - sub { return $xml_element; }, - sub { return $xml_element; }, - sub { return $xml_element; }, -); - -</%init> diff --git a/httemplate/search/477partIA_summary.html b/httemplate/search/477partIA_summary.html deleted file mode 100755 index ebf081c71..000000000 --- a/httemplate/search/477partIA_summary.html +++ /dev/null @@ -1,89 +0,0 @@ -<& elements/search.html, - 'html_init' => $html_init, - 'name' => 'lines', - 'query' => 'SELECT 1', - 'count_query' => 'SELECT 1', - 'really_disable_download' => 1, - 'disable_download' => 1, - 'nohtmlheader' => 1, - 'disable_total' => 1, - 'header' => [ - 'Total Connections', - '% owned loop', - '% billed to end users', - '% residential', - '% residential > 200kbps', - ], - 'xml_elements' => [ - $xml_prefix. 'a1', - $xml_prefix. 'b1', - $xml_prefix. 'c1', - $xml_prefix. 'd1', - $xml_prefix. 'e1', - ], - 'fields' => [ - sub { $total_count }, - sub { '100.00' }, - sub { '100.00' }, - sub { $total_percentage }, - sub { $above_200_percentage }, - ], - -&> -<%init> - -my $curuser = $FS::CurrentUser::CurrentUser; - -die "access denied" - unless $curuser->access_right('List packages'); - -my %opt = @_; -my %search_hash = (); - -for ( qw(agentnum magic state) ) { - $search_hash{$_} = $cgi->param($_) if $cgi->param($_); -} -$search_hash{'country'} = 'US'; -$search_hash{'classnum'} = [ $cgi->param('classnum') ]; - -my @column_option = grep { /^\d+$/ } $cgi->param('part1_column_option') - if $cgi->param('part1_column_option'); - -my @row_option = grep { /^\d+$/ } $cgi->param('part1_row_option') - if $cgi->param('part1_row_option'); - -my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi); - -my $total_count = 0; -my $total_residential = 0; -my $above_200 = 0; -my $tech_code = $opt{tech_code}; -my $technology = $FS::Report::FCC_477::technology[$tech_code] || 'unknown'; -my $html_init = "<H2>Part IA $technology totals</H2>"; -my $xml_prefix = 'PartIA_'. chr(65 + $tech_code); - -my $not_first_row = 0; # ugh; -foreach my $row ( @row_option ) { - foreach my $column ( @column_option ) { - - my @report_option = ( $row || '-1', $column || '-1', $technology_option[$tech_code] ); - - my ( $count, $residential ) = FS::cust_pkg->fcc_477_count( - { %search_hash, 'report_option' => join(',', @report_option) } - ); - - $total_count += $count; - $total_residential += $residential; - $above_200 += $residential if $not_first_row; - } - $not_first_row++; -} - -my $total_percentage = - sprintf("%.2f", $total_count ? 100*$total_residential/$total_count : 0); - -my $above_200_percentage = - sprintf("%.2f", $total_count ? 100*$above_200/$total_count : 0); - - -</%init> diff --git a/httemplate/search/477partIIA.html b/httemplate/search/477partIIA.html index 6a532299b..95c00a3e0 100755 --- a/httemplate/search/477partIIA.html +++ b/httemplate/search/477partIIA.html @@ -1,17 +1,44 @@ -<& elements/search.html, - 'html_init' => $html_init, - 'name' => 'lines', - 'query' => $query, - 'count_query' => 'SELECT 11', - 'really_disable_download' => 1, - 'disable_download' => 1, - 'nohtmlheader' => 1, - 'disable_total' => 1, - 'header' => [ @headers ], - 'xml_elements' => [ @xml_elements ], - 'fields' => [ @fields ], - -&> +% if ( $cgi->param('_type') eq 'xml' ) { +% my @cols = qw(a b c d); +% for ( my $row = 0; $row < scalar(@rows); $row++ ) { +% for my $col (0..3) { +% if ( exists($data[$col][$row]) and $data[$col][$row] > 0 ) { +<PartII_<% $row + 1 %><% $cols[$col] %>>\ +<% $data[$col][$row] %>\ +</PartII_<% $row + 1 %><% $cols[$col] %>> +% } +% } #for $col +% } #for $row +% } else { # HTML mode +% # fake up the search-html.html header +<H2>Part IIA</H2> +<TABLE> + <TR><TD VALIGN="bottom"><BR></TD></TR> + <TR><TD COLSPAN=2> + <TABLE CLASS="grid" CELLSPACING=0> + <TR> +% foreach (@row1_headers) { + <TH><% $_ %></TH> +% } + </TR> +% my $row = 0; +% foreach my $rowhead (@rows) { + <TR CLASS="row<%$row % 2%>"> + <TD STYLE="text-align: left; font-weight: bold"><% $rowhead %></TD> +% for my $col (0..3) { + <TD> +% if ( exists($data[$col][$row]) ) { + <% $data[$col][$row] %> +% } + </TD> +% } # for $col + </TR> +% $row++; +% } #for $rowhead + </TABLE> + </TD></TR> +</TABLE> +% } #XML/HTML <%init> my $curuser = $FS::CurrentUser::CurrentUser; @@ -19,83 +46,76 @@ my $curuser = $FS::CurrentUser::CurrentUser; die "access denied" unless $curuser->access_right('List packages'); -my $html_init = '<H2>Part IIA</H2>'; my %search_hash = (); - -for ( qw(agentnum magic state) ) { - $search_hash{$_} = $cgi->param($_) if $cgi->param($_); -} -$search_hash{'country'} = 'US'; -$search_hash{'classnum'} = [ $cgi->param('classnum') ]; - -my @row_option = grep { /^\d+$/ } $cgi->param('part2a_row_option') - if $cgi->param('part2a_row_option'); - -# fudge in two rows of LD carrier -unshift @row_option, $row_option[0]; - -# fudge in the first pair of rows -unshift @row_option, ''; -unshift @row_option, ''; - -my $query = 'SELECT '. join(' UNION SELECT ', 1..11); -my $total_count = 0; -my $column_value = sub { - my $row = shift; - - my @report_option = ( $row_option[$row - 1] || '' ); - - my $sql_query = FS::cust_pkg->search( - { %search_hash, 'report_option' => join(',', @report_option) } - ); - - my $count_sql = delete($sql_query->{'count_query'}); - if ( $row == 2 || $row == 4 ) { - $count_sql =~ s/COUNT\(\*\) FROM/sum(COALESCE(CASE WHEN cust_main.company IS NULL OR cust_main.company = '' THEN CASE WHEN part_pkg.fcc_ds0s IS NOT NULL AND part_pkg.fcc_ds0s > 0 THEN part_pkg.fcc_ds0s WHEN pkg_class.fcc_ds0s IS NOT NULL AND pkg_class.fcc_ds0s > 0 THEN pkg_class.fcc_ds0s ELSE 0 END ELSE 0 END, 0) ) FROM/ - or die "couldn't parse count_sql"; - } else { - $count_sql =~ s/COUNT\(\*\) FROM/sum(COALESCE(CASE WHEN part_pkg.fcc_ds0s IS NOT NULL AND part_pkg.fcc_ds0s > 0 THEN part_pkg.fcc_ds0s WHEN pkg_class.fcc_ds0s IS NOT NULL AND pkg_class.fcc_ds0s > 0 THEN pkg_class.fcc_ds0s ELSE 0 END, 0)) FROM/ - or die "couldn't parse count_sql"; - } - - my $count_sth = dbh->prepare($count_sql) - or die "Error preparing $count_sql: ". dbh->errstr; - $count_sth->execute - or die "Error executing $count_sql: ". $count_sth->errstr; - my $count_arrayref = $count_sth->fetchrow_arrayref; - my $count = $count_arrayref->[0]; +$search_hash{'agentnum'} = $cgi->param('agentnum'); +$search_hash{'state'} = $cgi->param('state'); +$search_hash{'classnum'} = [ $cgi->param('classnum') ]; +$search_hash{'status'} = 'active'; - $total_count = $count if $row == 1; - $count = sprintf('%.2f', $total_count ? 100*$count/$total_count : 0) - if $row != 1; +my @row_option; +foreach ($cgi->param('part2a_row_option')) { + push @row_option, (/^\d+$/ ? $_ : undef); +} - return "$count"; +my $is_residential = "AND COALESCE(cust_main.company, '') = ''"; +my $has_report_option = sub { + map { + defined($row_option[$_]) ? + " AND EXISTS( + SELECT 1 FROM part_pkg_option + WHERE part_pkg_option.pkgpart = part_pkg.pkgpart + AND optionname = 'report_option_" . $row_option[$_]."' + AND optionvalue = '1' + )" : ' AND FALSE' + } @_ }; -my @headers = ( - '', - 'End user lines', - 'UNE-P replacement', - 'UNE (unswitched)', - 'UNE-P', +# an arrayref for each column +my @data; +# get the skeleton of the query +my $sql_query = FS::cust_pkg->search(\%search_hash); +my $from_where = $sql_query->{'count_query'}; +$from_where =~ s/^SELECT COUNT\(\*\) //; + +# for row 1 +my $query_ds0 = "SELECT SUM(COALESCE(part_pkg.fcc_ds0s, pkg_class.fcc_ds0s, 0)) + $from_where AND fcc_voip_class = '4'"; # 4 = Local Exchange + +my $total_lines = FS::Record->scalar_sql($query_ds0); +# always return zero for the number of resold lines, until an actual ILEC +# starts using this report + +@data = ( + [ $total_lines ], + [ 0 ], + [ 0 ], + [ 0 ], ); -my @xml_elements = ( - sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}a" }, - sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}b" }, - sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}c" }, - sub { my $row = shift; my $rownum = $row->[0] + 1; "PartII_${rownum}d" }, +my @row_conds = ( + $is_residential, + $has_report_option->(0), # LD carrier + ($has_report_option->(0))[0] . $is_residential, + $has_report_option->(1..7), ); +if ( $total_lines > 0 ) { + foreach (@row_conds) { + my $sql = $query_ds0 . $_; + my $lines = FS::Record->scalar_sql($sql); + my $percent = sprintf('%.2f', 100 * $lines / $total_lines); + push @{ $data[0] }, $percent; + } +} my @rows = ( 'lines', '% residential', '% LD carrier', - '% residential and LD carrier', - '% own loops', - '% obtained unswitched UNE loops', + '% residential and LD', + '% owned loops', + '% unswitched UNE', '% UNE-P', '% UNE-P replacement', '% FTTP', @@ -103,13 +123,12 @@ my @rows = ( '% wireless', ); -my @fields = ( - sub { my $row = shift; $rows[$row->[0] - 1]; }, - sub { my $row = shift; &{$column_value}($row->[0]); }, - sub { 0; }, - sub { 0; }, - sub { 0; }, +my @row1_headers = ( + '', + 'End user lines', + 'UNE-P replacement', + 'unswitched UNE', + 'UNE-P', ); -shift @fields if $cgi->param('_type') eq 'xml'; </%init> diff --git a/httemplate/search/477partIIB.html b/httemplate/search/477partIIB.html index c58310d36..5b9b30769 100755 --- a/httemplate/search/477partIIB.html +++ b/httemplate/search/477partIIB.html @@ -3,9 +3,10 @@ % for ( my $row = 0; $row < scalar(@rows); $row++ ) { % for my $col (0..2) { % if ( exists($data[$col][$row]) ) { -<PartII_<% $row %><% $cols[$col] %>> +<PartII_<% $row + 1 %><% $cols[$col] %>>\ +<% $data[$col][$row] %>\ +</PartII_<% $row + 1 %><% $cols[$col] %>> % } -</PartII_<% $row %><% $cols[$col] %>> % } #for $col % } #for $row % } else { # HTML mode @@ -14,19 +15,18 @@ <TABLE> <TR><TD VALIGN="bottom"><BR></TD></TR> <TR><TD COLSPAN=2> - <TABLE CLASS="grid" CELLSPACING=0 STYLE="border: 1px solid #cccccc;" BGCOLOR="#cccccc"> + <TABLE CLASS="grid" CELLSPACING=0> <TR> % foreach (@headers) { - <TH class="grid"><% $_ %></TH> + <TH><% $_ %></TH> % } </TR> -% my @bgcolor = ('eeeeee','ffffff'); % my $row = 0; % foreach my $rowhead (@rows) { - <TR> - <TD CLASS="grid" BGCOLOR="#<% $bgcolor[$row % 2] %>"><% $rowhead %></TD> + <TR CLASS="row<% $row % 2 %>"> + <TD STYLE="text-align: left; font-weight: bold"><% $rowhead %></TD> % for my $col (0..2) { - <TD CLASS="grid" BGCOLOR="#<% $bgcolor[$row % 2] %>"> + <TD> % if ( exists($data[$col][$row]) ) { <% $data[$col][$row] %> % } diff --git a/httemplate/search/477partV.html b/httemplate/search/477partV.html index 2106a44d6..b2dd9ca95 100755 --- a/httemplate/search/477partV.html +++ b/httemplate/search/477partV.html @@ -1,3 +1,6 @@ +% if ( $cgi->param('_type') =~ /^xml$/ ) { +<zip_code> +% } <& elements/search.html, 'html_init' => $html_init, 'name' => 'zip code', @@ -14,6 +17,9 @@ &> +% if ( $cgi->param('_type') =~ /^xml$/ ) { +</zip_code> +% } <%init> my $curuser = $FS::CurrentUser::CurrentUser; diff --git a/httemplate/search/agent_commission.html b/httemplate/search/agent_commission.html index b8fbe200f..b94ae9f6e 100644 --- a/httemplate/search/agent_commission.html +++ b/httemplate/search/agent_commission.html @@ -1,6 +1,12 @@ %# still not a good way to do rows grouped by some field in a search.html %# report +% if ( $type eq 'xls' ) { +<% $data %>\ +% } else { <& /elements/header.html, $title &> +<P ALIGN="right" CLASS="noprint"> +Download full results<BR> +as <A HREF="<% $cgi->self_url %>;_type=xls">Excel spreadsheet</A></P> <BR> <STYLE TYPE="text/css"> td.cust_head { @@ -58,6 +64,7 @@ td.money:before { content: '<% $money_char %>'; } </TR> </TABLE> <& /elements/footer.html &> +% } <%init> die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); @@ -100,10 +107,91 @@ my @cust_pkg = qsearch($query); my $money_char = FS::Conf->new->config('money_char') || '$'; -#my $count_query = -# 'SELECT COUNT(*) FROM cust_pkg '.$query->{'addl_from'}.$query->{'extra_sql'}. -# ' AND EXISTS(SELECT 1 FROM cust_bill_pkg JOIN cust_bill USING (invnum) '. -# ' WHERE cust_bill_pkg.pkgnum = cust_pkg.pkgnum AND '. -# "cust_bill._date >= $begin AND cust_bill._date < $end". -# ')'; +my $data = ''; +my $type = $cgi->param('_type'); +if ( $type eq 'xls') { + # some false laziness with the above... + my $format = $FS::CurrentUser::CurrentUser->spreadsheet_format; + my $filename = 'agent_commission' . $format->{extension}; + http_header('Content-Type' => $format->{mime_type}); + http_header('Content-Disposition' => qq!attachment;filename="$filename"!); + my $XLS = IO::Scalar->new(\$data); + my $workbook = $format->{class}->new($XLS); + my $worksheet = $workbook->add_worksheet(substr($title, 0, 31)); + + my $cust_head_format = $workbook->add_format( + bold => 1, + underline => 1, + text_wrap => 0, + bg_color => 'white', + ); + + my $col_head_format = $workbook->add_format( + bold => 1, + align => 'center', + bg_color => 'silver' + ); + + my @format; + foreach (0, 1) { + my %bg = (bg_color => $_ ? 'white' : 'silver'); + $format[$_] = { + 'text' => $workbook->add_format(%bg), + 'money' => $workbook->add_format(%bg, num_format => $money_char.'#0.00'), + 'percent' => $workbook->add_format(%bg, num_format => '0.00%'), + }; + } + my $total_format = $workbook->add_format( + bg_color => 'yellow', + num_format => $money_char.'#0.00', + top => 1 + ); + + my ($r, $c) = (0, 0); + foreach (qw(Package Sales Percentage Commission)) { + $worksheet->write($r, $c++, $_, $col_head_format); + } + $r++; + + my ($custnum, $sales, $commission, $row, $bgcolor) = (0, 0, 0, 0); + my $label_length = 0; + foreach my $cust_pkg ( @cust_pkg ) { + if ( $custnum ne $cust_pkg->custnum ) { + # start of a new customer section + my $cust_main = $cust_pkg->cust_main; + my $label = $cust_main->custnum . ': '. $cust_main->name; + $bgcolor = 0; + $worksheet->set_row($r, 20); + $worksheet->merge_range($r, 0, $r, 3, $label, $cust_head_format); + $r++; + } + $c = 0; + my $percent = $cust_pkg->percent / 100; + $worksheet->write($r, $c++, $cust_pkg->pkg_label, $format[$bgcolor]{text}); + $worksheet->write($r, $c++, $cust_pkg->sum_charged, $format[$bgcolor]{money}); + $worksheet->write($r, $c++, $percent, $format[$bgcolor]{percent}); + $worksheet->write($r, $c++, ($cust_pkg->sum_charged * $percent), + $format[$bgcolor]{money}); + + $label_length = max($label_length, length($cust_pkg->pkg_label)); + $sales += $cust_pkg->sum_charged; + $commission += $cust_pkg->sum_charged * $cust_pkg->percent / 100; + $row++; + $bgcolor = 1-$bgcolor; + $custnum = $cust_pkg->custnum; + $r++; + } + + $c = 0; + $label_length = max($label_length, 20); + $worksheet->set_column($c, $c, $label_length); + $worksheet->write($r, $c++, mt('[quant,_1,package] with commission', $row), + $total_format); + $worksheet->set_column($c, $c + 2, 11); + $worksheet->write($r, $c++, $sales, $total_format); + $worksheet->write($r, $c++, '', $total_format); + $worksheet->write($r, $c++, $commission, $total_format); + + $workbook->close; +} </%init> diff --git a/httemplate/search/cust_bill.html b/httemplate/search/cust_bill.html index 88cdaf5ab..473aed311 100755 --- a/httemplate/search/cust_bill.html +++ b/httemplate/search/cust_bill.html @@ -97,7 +97,7 @@ if ( $cgi->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/ ) { $search{'refnum'} = $1; } - if ( $cgi->param('cust_classnum') ) { +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { $search{'cust_classnum'} = [ $cgi->param('cust_classnum') ]; } diff --git a/httemplate/search/cust_bill_pay.html b/httemplate/search/cust_bill_pay.html index 0b64e650f..ff20458d8 100644 --- a/httemplate/search/cust_bill_pay.html +++ b/httemplate/search/cust_bill_pay.html @@ -99,9 +99,12 @@ if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) { $title = $part_referral->referral. " $title"; } -if ( $cgi->param('cust_classnum') ) { - my @classnums = grep /^\d+$/, $cgi->param('cust_classnum'); - push @search, 'cust_main.classnum IN('.join(',',@classnums).')' +# cust_classnum (false laziness w/ elements/cust_main_dayranges.html, prepaid_income.html, cust_bill_pkg.html, cust_bill_pkg_referral.html, unearned_detail.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @search, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' if @classnums; } diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi index 0f51d9481..3a3b0feb9 100644 --- a/httemplate/search/cust_bill_pkg.cgi +++ b/httemplate/search/cust_bill_pkg.cgi @@ -260,13 +260,16 @@ if ( $cgi->param('refnum') =~ /^(\d+)$/ ) { push @where, "cust_main.refnum = $1"; } -# cust_classnum -if ( $cgi->param('cust_classnum') ) { - my @classnums = grep /^\d+$/, $cgi->param('cust_classnum'); - push @where, 'cust_main.classnum IN('.join(',',@classnums).')' +# cust_classnum (false laziness w/ elements/cust_main_dayranges.html, elements/cust_pay_or_refund.html, prepaid_income.html, cust_bill_pay.html, cust_bill_pkg_referral.html, unearned_detail.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @where, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' if @classnums; } + # custnum if ( $cgi->param('custnum') =~ /^(\d+)$/ ) { push @where, "cust_main.custnum = $1"; diff --git a/httemplate/search/cust_bill_pkg_referral.html b/httemplate/search/cust_bill_pkg_referral.html index 1289ff7ee..c4dde32a0 100644 --- a/httemplate/search/cust_bill_pkg_referral.html +++ b/httemplate/search/cust_bill_pkg_referral.html @@ -156,9 +156,13 @@ if ( @refnum ) { push @where, 'cust_main.refnum IN ('.join(',', @refnum).')'; } -my @cust_classnums = grep /^\d+$/, $cgi->param('cust_classnum'); -if ( @cust_classnums ) { - push @where, 'cust_main.classnum IN ('.join(',', @cust_classnums).')'; +# cust_classnum (false laziness w/ elements/cust_main_dayranges.html, elements/cust_pay_or_refund.html, prepaid_income.html, cust_bill_pay.html, cust_bill_pkg.html, unearned_detail.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @where, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' + if @classnums; } if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { diff --git a/httemplate/search/cust_credit.html b/httemplate/search/cust_credit.html index d1f41df00..cabf8c002 100755 --- a/httemplate/search/cust_credit.html +++ b/httemplate/search/cust_credit.html @@ -103,9 +103,13 @@ if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) { $title = $part_referral->referral. " $title"; } -if ( $cgi->param('cust_classnum') ) { - my @classnums = grep /^\d+$/, $cgi->param('cust_classnum'); - push @search, 'cust_main.classnum IN('.join(',',@classnums).')' + +# cust_classnum (false laziness w/ elements/cust_main_dayranges.html, elements/cust_pay_or_refund.html, prepaid_income.html, cust_bill_pay.html, cust_bill_pkg.html, cust_bill_pkg_referral.html, unearned_detail.html, cust_credit_refund.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @search, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' if @classnums; } diff --git a/httemplate/search/cust_credit_refund.html b/httemplate/search/cust_credit_refund.html index 1504f0fbe..817420054 100644 --- a/httemplate/search/cust_credit_refund.html +++ b/httemplate/search/cust_credit_refund.html @@ -85,9 +85,12 @@ if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) { $title = $part_referral->referral. " $title"; } -if ( $cgi->param('cust_classnum') ) { - my @classnums = grep /^\d+$/, $cgi->param('cust_classnum'); - push @search, 'cust_main.classnum IN('.join(',',@classnums).')' +# cust_classnum (false laziness w/ elements/cust_main_dayranges.html, elements/cust_pay_or_refund.html, prepaid_income.html, cust_bill_pay.html, cust_bill_pkg.html, cust_bill_pkg_referral.html, unearned_detail.html, cust_credit.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @search, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' if @classnums; } diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi index 8e3c8133e..2c09c692c 100755 --- a/httemplate/search/cust_main.cgi +++ b/httemplate/search/cust_main.cgi @@ -244,7 +244,7 @@ % my $pkg_rowspan = shift @pkg_rowspans; <% $n1 %><TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ROWSPAN="<% $pkg_rowspan%>"> - <A HREF="<% $pkgview %>"><FONT SIZE=-1><% $pkg_comment %></FONT></A> + <A HREF="<% $pkgview %>"><FONT SIZE=-1><% $pkg_comment |h %></FONT></A> </TD> % my $n2 = ''; diff --git a/httemplate/search/cust_main.html b/httemplate/search/cust_main.html index 8b39ea962..24348ff8a 100755 --- a/httemplate/search/cust_main.html +++ b/httemplate/search/cust_main.html @@ -42,10 +42,11 @@ my %search_hash = (); #scalars my @scalars = qw ( agentnum status address zip paydate_year paydate_month invoice_terms - no_censustract with_geocode with_email no_POST + no_censustract with_geocode with_email POST no_POST custbatch usernum cancelled_pkgs cust_fields flattened_pkgs + all_tags ); for my $param ( @scalars ) { diff --git a/httemplate/search/cust_pkg.cgi b/httemplate/search/cust_pkg.cgi index 887ec6039..110da91ae 100755 --- a/httemplate/search/cust_pkg.cgi +++ b/httemplate/search/cust_pkg.cgi @@ -9,6 +9,7 @@ emt('Package'), emt('Class'), emt('Status'), + emt('Ordered by'), emt('Setup'), emt('Base Recur'), emt('Freq.'), @@ -34,6 +35,7 @@ sub { $_[0]->pkg; }, 'classname', sub { ucfirst(shift->status); }, + 'otaker', sub { sprintf( $money_char.'%.2f', shift->part_pkg->option('setup_fee'), ); @@ -96,13 +98,14 @@ '', '', '', + '', FS::UI::Web::cust_colors(), '', ], - 'style' => [ '', '', '', '', 'b', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'style' => [ '', '', '', '', 'b', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', FS::UI::Web::cust_styles() ], 'size' => [ '', '', '', '', '-1' ], - 'align' => 'rrlccrrlrrrrrrrrrrl'. FS::UI::Web::cust_aligns(). 'r', + 'align' => 'rrlcccrrlrrrrrrrrrrl'. FS::UI::Web::cust_aligns(). 'r', 'links' => [ $link, $link, @@ -119,6 +122,7 @@ '', '', '', + '', '', # link to changed-from package? '', '', diff --git a/httemplate/search/customer_accounting_summary.html b/httemplate/search/customer_accounting_summary.html index 12c896276..b48ff21e3 100644 --- a/httemplate/search/customer_accounting_summary.html +++ b/httemplate/search/customer_accounting_summary.html @@ -142,8 +142,6 @@ $title .= $sel_part_referral->referral.' ' $title .= 'Customer Accounting Summary Report'; -my @cust_classnums = grep /^\d+$/, $cgi->param('cust_classnum'); - my @items = ('netsales', 'cashflow'); my @params = ( [], [] ); my $setuprecur = ''; @@ -173,7 +171,7 @@ foreach (qw(agentnum refnum status)) { } } $search_hash{'classnum'} = [ $cgi->param('cust_classnum') ] - if $cgi->param('cust_classnum'); + if grep { $_ eq 'cust_classnum' } $cgi->param; my $query = FS::cust_main::Search->search(\%search_hash); my @custs = qsearch($query); diff --git a/httemplate/search/elements/cust_main_dayranges.html b/httemplate/search/elements/cust_main_dayranges.html index c9c71f274..cf2d495b1 100644 --- a/httemplate/search/elements/cust_main_dayranges.html +++ b/httemplate/search/elements/cust_main_dayranges.html @@ -162,6 +162,15 @@ if ( grep { $cgi->param('status') eq $_ } FS::cust_main->statuses() ) { push @where, FS::cust_main->$method(); } +# cust_classnum (false laziness w/prepaid_income.html, elements/cust_pay_or_refund.html, cust_bill_pay.html, cust_bill_pkg.html, cust_bill_pkg_referral.html, unearned_detail.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @where, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' + if @classnums; +} + #here is the agent virtualization push @where, $FS::CurrentUser::CurrentUser->agentnums_sql; diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html index 3e5d504c6..7b2a17058 100755 --- a/httemplate/search/elements/cust_pay_or_refund.html +++ b/httemplate/search/elements/cust_pay_or_refund.html @@ -252,9 +252,12 @@ if ( $cgi->param('magic') ) { $title = $part_referral->referral. " $title"; } - if ( $cgi->param('cust_classnum') ) { - my @classnums = grep /^\d+$/, $cgi->param('cust_classnum'); - push @search, 'cust_main.classnum IN('.join(',',@classnums).')' + # cust_classnum (false laziness w/ elements/cust_main_dayranges.html, prepaid_income.html, cust_bill_pay.html, cust_bill_pkg.html cust_bill_pkg_referral.html, unearned_detail.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql) + if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @search, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' if @classnums; } diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html index 68c488837..d44b45465 100644 --- a/httemplate/search/elements/search.html +++ b/httemplate/search/elements/search.html @@ -353,7 +353,7 @@ if ( $opt{'disableable'} ) { my $limit = ''; my($confmax, $maxrecords, $offset ); -unless ( $type =~ /^(csv|\w*.xls)$/) { +unless ( $type =~ /^(csv|xml|\w*.xls)$/) { # html mode unless (exists($opt{count_query}) && length($opt{count_query})) { ( $opt{count_query} = $opt{query} ) =~ diff --git a/httemplate/search/employee_audit.html b/httemplate/search/employee_audit.html index 753c7bff3..2bc6ff46e 100644 --- a/httemplate/search/employee_audit.html +++ b/httemplate/search/employee_audit.html @@ -7,7 +7,7 @@ <%init> die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + unless $FS::CurrentUser::CurrentUser->access_right('Employees: Audit Report'); my %tables = ( cust_pay => 'Payments', diff --git a/httemplate/search/part_pkg.html b/httemplate/search/part_pkg.html index 2178346e2..a90f13c95 100644 --- a/httemplate/search/part_pkg.html +++ b/httemplate/search/part_pkg.html @@ -23,7 +23,7 @@ my $curuser = $FS::CurrentUser::CurrentUser; die "access denied" - unless $curuser->access_right('Financial reports'); + unless $curuser->access_right('Employees: Commission Report'); #that's all this does so far my $conf = new FS::Conf; my $money_char = $conf->config('money_char') || '$'; diff --git a/httemplate/search/pay_batch.cgi b/httemplate/search/pay_batch.cgi index 00c6ba1b9..620996abd 100755 --- a/httemplate/search/pay_batch.cgi +++ b/httemplate/search/pay_batch.cgi @@ -150,8 +150,8 @@ my($begin, $end) = ( '', '' ); my @where; my($beginning,$ending) = FS::UI::Web::parse_beginning_ending($cgi); -push @where, "download >= $beginning", - "download <= $ending"; +push @where, "( (download >= $beginning AND download <= $ending) ". + ' OR download IS NULL )'; my @status; if ( $cgi->param('open') ) { diff --git a/httemplate/search/prepaid_income.html b/httemplate/search/prepaid_income.html index 03d121d70..cb58a666d 100644 --- a/httemplate/search/prepaid_income.html +++ b/httemplate/search/prepaid_income.html @@ -129,10 +129,13 @@ if ( $cgi->param('status') =~ /^([a-z]+)$/ ) { push @where, FS::cust_main->cust_status_sql . " = '$status'"; } -if ( $cgi->param('cust_classnum') ) { - my @classnums = grep /^\d+$/, $cgi->param('cust_classnum'); +# cust_classnum (false laziness w/ elements/cust_main_dayranges.html, elements/cust_pay_or_refund.html, cust_bill_pay.html, cust_bill_pkg.html, cust_bill_pkg_referral.html, unearned_detail.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); $link .= ";cust_classnum=$_" foreach @classnums; - push @where, 'cust_main.classnum IN('.join(',',@classnums).')' + push @where, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' if @classnums; } diff --git a/httemplate/search/report_cust_bill.html b/httemplate/search/report_cust_bill.html index 51618fb24..b339c80e0 100644 --- a/httemplate/search/report_cust_bill.html +++ b/httemplate/search/report_cust_bill.html @@ -4,7 +4,7 @@ <INPUT TYPE="hidden" NAME="magic" VALUE="_date"> <INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> -<TABLE BGCOLOR="#cccccc" CELLSPACING=0 +<TABLE BGCOLOR="#cccccc" CELLSPACING=0> % unless ( $custnum ) { <& /elements/tr-select-agent.html, diff --git a/httemplate/search/report_cust_main.html b/httemplate/search/report_cust_main.html index acc49aec6..bac4346cf 100755 --- a/httemplate/search/report_cust_main.html +++ b/httemplate/search/report_cust_main.html @@ -96,11 +96,21 @@ </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', @@ -174,10 +184,28 @@ </TR> <TR> + <TD ALIGN="right" VALIGN="center"><% mt('With postal mail invoices') |h %></TD> + <TD><INPUT TYPE="checkbox" NAME="POST" ID="POST" onClick="POST_changed();"></TD> + </TR> + + <TR> <TD ALIGN="right" VALIGN="center"><% mt('Without postal mail invoices') |h %></TD> - <TD><INPUT TYPE="checkbox" NAME="no_POST"></TD> + <TD><INPUT TYPE="checkbox" NAME="no_POST" ID="no_POST" onClick="no_POST_changed();"></TD> </TR> + <SCRIPT TYPE="text/javascript"> + function POST_changed() { + if ( document.getElementById('POST').checked == true ) { + document.getElementById('no_POST').checked = false; + } + } + function no_POST_changed() { + if ( document.getElementById('no_POST').checked == true ) { + document.getElementById('POST').checked = false; + } + } + </SCRIPT> + <TR> <TH CLASS="background" COLSPAN=2> </TH> </TR> diff --git a/httemplate/search/report_employee_audit.html b/httemplate/search/report_employee_audit.html index 757b8232f..461849b76 100644 --- a/httemplate/search/report_employee_audit.html +++ b/httemplate/search/report_employee_audit.html @@ -23,7 +23,7 @@ <%init> die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + unless $FS::CurrentUser::CurrentUser->access_right('Employees: Audit Report'); my %tables = ( cust_pay => 'Payments', diff --git a/httemplate/search/report_employee_commission.html b/httemplate/search/report_employee_commission.html index 51afad3b5..ebfcae82d 100644 --- a/httemplate/search/report_employee_commission.html +++ b/httemplate/search/report_employee_commission.html @@ -25,6 +25,6 @@ <%init> die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + unless $FS::CurrentUser::CurrentUser->access_right('Employees: Commission Report'); </%init> diff --git a/httemplate/search/report_receivables.html b/httemplate/search/report_receivables.html index 5cff0f4fc..854b24a00 100755 --- a/httemplate/search/report_receivables.html +++ b/httemplate/search/report_receivables.html @@ -15,7 +15,15 @@ <& /elements/tr-select-cust_main-status.html, 'label' => emt('Customer Status'), &> - + + <& /elements/tr-select-cust_class.html, + 'label' => emt('Customer class'), + 'field' => 'cust_classnum', + 'multiple' => 1, + 'pre_options' => [ '' => emt('(none)') ], + 'all_selected' => 1, + &> + <TR> <TD ALIGN="right"><% mt('Customers') |h %></TD> <TD> diff --git a/httemplate/search/report_tax.cgi b/httemplate/search/report_tax.cgi index 42a52d154..479b99044 100755 --- a/httemplate/search/report_tax.cgi +++ b/httemplate/search/report_tax.cgi @@ -250,8 +250,10 @@ my $conf = new FS::Conf; my $out = 'Out of taxable region(s)'; my %label_opt = ( out => 1 ); #enable 'Out of Taxable Region' label -$label_opt{no_city} = 1 unless $cgi->param('show_cities'); -$label_opt{no_taxclass} = 1 unless $cgi->param('show_taxclasses'); +$label_opt{with_city} = 1 if $cgi->param('show_cities'); +$label_opt{with_district} = 1 if $cgi->param('show_districts'); + +$label_opt{with_taxclass} = 1 if $cgi->param('show_taxclasses'); my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi); @@ -487,7 +489,8 @@ my $tot_tax = 0; my $tot_credit = 0; my @loc_params = qw(country state county); -push @loc_params, qw(city district) if $cgi->param('show_cities'); +push @loc_params, 'city' if $cgi->param('show_cities'); +push @loc_params, 'district' if $cgi->param('show_districts'); foreach my $r ( qsearch({ 'table' => 'cust_main_county', })) { my $taxnum = $r->taxnum; @@ -522,7 +525,7 @@ foreach my $r ( qsearch({ 'table' => 'cust_main_county', })) { } if ( $cgi->param('show_taxclasses') ) { - my $base_label = $r->label(%label_opt, 'no_taxclass' => 1); + my $base_label = $r->label(%label_opt, 'with_taxclass' => 0); $base_regions{$base_label} ||= { label => $base_label, diff --git a/httemplate/search/report_tax.html b/httemplate/search/report_tax.html index 2ab0e0b2e..8a207aafb 100755 --- a/httemplate/search/report_tax.html +++ b/httemplate/search/report_tax.html @@ -34,9 +34,21 @@ % if ( $city ) { <TR> - <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_cities" VALUE="1"></TD> + <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_cities" VALUE="1" onclick="toggle_show_cities(this)"></TD> <TD>Show cities</TD> </TR> + <TR> + <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_districts" VALUE="1" DISABLED></TD> + <TD>Show districts</TD> + </TR> + <SCRIPT TYPE="text/javascript"> + function toggle_show_cities() { + what = document.getElementsByName('show_cities')[0]; + what.form.show_districts.disabled = !what.checked; + what.form.show_districts.checked = what.checked; + } + toggle_show_cities(); + </SCRIPT> % } % if ( $conf->exists('enable_taxclasses') ) { diff --git a/httemplate/search/sqlradius.cgi b/httemplate/search/sqlradius.cgi index 5363944e4..22984731a 100644 --- a/httemplate/search/sqlradius.cgi +++ b/httemplate/search/sqlradius.cgi @@ -51,7 +51,7 @@ % @{ $part_export->usage_sessions( { % 'stoptime_start' => $beginning, % 'stoptime_end' => $ending, -% 'open_sessions' => $open_sessions, +% 'session_status' => $status, % 'starttime_start' => $starttime_beginning, % 'starttime_end' => $starttime_ending, % 'svc_acct' => $cgi_svc_acct, @@ -117,9 +117,9 @@ if ( $cgi->param('end') && $cgi->param('end') =~ /^(\d+)$/ ) { $ending = $1; } -my $open_sessions = ''; -if ( $cgi->param('open_sessions') =~ /^(\d*)$/ ) { - $open_sessions = $1; +my $status = ''; +if ( $cgi->param('session_status') =~ /^(closed|open)$/ ) { + $status = $1; } my( $starttime_beginning, $starttime_ending ) = ( '', '' ); @@ -242,8 +242,15 @@ my $time_format = sub { $pretty; }; +my $time_format_or_open = sub { + my $time = shift; + return '<CENTER>OPEN</CENTER>' if $time == 0; + &{$time_format}($time); +}; + my $duration_format = sub { my $seconds = shift; + return '' if $seconds eq ''; # open session my $hour = int($seconds/3600); my $min = int( ($seconds%3600) / 60 ); my $sec = $seconds%60; @@ -339,7 +346,7 @@ tie %fields, 'Tie::IxHash', 'acctstoptime' => { name => 'End time', attrib => 'Acct-Stop-Time', - fmt => $time_format, + fmt => $time_format_or_open, align => 'left', }, 'acctsessiontime' => { diff --git a/httemplate/search/sqlradius.html b/httemplate/search/sqlradius.html index 7b9fce310..547a9bb44 100644 --- a/httemplate/search/sqlradius.html +++ b/httemplate/search/sqlradius.html @@ -52,8 +52,9 @@ <TR> <TD>Show:</TD> <TD> - <INPUT TYPE="radio" NAME="open_sessions" VALUE="0" onClick="open_changed(this);" CHECKED>Completed sessions<BR> - <INPUT TYPE="radio" NAME="open_sessions" VALUE="1" onClick="open_changed(this);">Open sessions + <INPUT TYPE="radio" NAME="session_status" VALUE="" onClick="enable_stop(true);" CHECKED>All sessions<BR> + <INPUT TYPE="radio" NAME="session_status" VALUE="closed" onClick="enable_stop(true);">Completed sessions<BR> + <INPUT TYPE="radio" NAME="session_status" VALUE="open" onClick="enable_stop(false);">Open sessions </TD> </TR> @@ -69,41 +70,31 @@ <SCRIPT TYPE="text/javascript"> - function open_changed(what) { - - var value=get_open_value(what); - if ( value == '1' ) { - what.form.stoptime_beginning_text.disabled = true; - what.form.stoptime_ending_text.disabled = true; - what.form.stoptime_beginning_text.style.backgroundColor = '#dddddd'; - what.form.stoptime_ending_text.style.backgroundColor = '#dddddd'; - what.form.stoptime_beginning_button.style.display = 'none'; - what.form.stoptime_ending_button.style.display = 'none'; - what.form.stoptime_beginning_disabled.style.display = ''; - what.form.stoptime_ending_disabled.style.display = ''; - } else if ( value == '0' ) { - what.form.stoptime_beginning_text.disabled = false; - what.form.stoptime_ending_text.disabled = false; - what.form.stoptime_beginning_text.style.backgroundColor = '#ffffff'; - what.form.stoptime_ending_text.style.backgroundColor = '#ffffff'; - what.form.stoptime_beginning_button.style.display = ''; - what.form.stoptime_ending_button.style.display = ''; - what.form.stoptime_beginning_disabled.style.display = 'none'; - what.form.stoptime_ending_disabled.style.display = 'none'; + function enable_stop(value) { + + var f = document.OneTrueForm; + if ( value ) { + f.stoptime_beginning_text.disabled = false; + f.stoptime_ending_text.disabled = false; + f.stoptime_beginning_text.style.backgroundColor = '#ffffff'; + f.stoptime_ending_text.style.backgroundColor = '#ffffff'; + f.stoptime_beginning_button.style.display = ''; + f.stoptime_ending_button.style.display = ''; + f.stoptime_beginning_disabled.style.display = 'none'; + f.stoptime_ending_disabled.style.display = 'none'; + } else { + f.stoptime_beginning_text.disabled = true; + f.stoptime_ending_text.disabled = true; + f.stoptime_beginning_text.style.backgroundColor = '#dddddd'; + f.stoptime_ending_text.style.backgroundColor = '#dddddd'; + f.stoptime_beginning_button.style.display = 'none'; + f.stoptime_ending_button.style.display = 'none'; + f.stoptime_beginning_disabled.style.display = ''; + f.stoptime_ending_disabled.style.display = ''; } } - function get_open_value(what) { - var rad_val = ''; - for (var i=0; i < what.form.open_sessions.length; i++) { - if (what.form.open_sessions[i].checked) { - var rad_val = what.form.open_sessions[i].value; - } - } - return rad_val; - } - </SCRIPT> <TR> diff --git a/httemplate/search/unearned_detail.html b/httemplate/search/unearned_detail.html index 425aa5a4e..285fb50a7 100644 --- a/httemplate/search/unearned_detail.html +++ b/httemplate/search/unearned_detail.html @@ -114,13 +114,12 @@ if ( $cgi->param('status') =~ /^([a-z]+)$/ ) { push @where, "cust_bill._date >= $beginning", "cust_bill._date <= $ending"; -if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { - push @where, "cust_main.agentnum = $1"; -} - -if ( $cgi->param('cust_classnum') ) { - my @classnums = grep /^\d+$/, $cgi->param('cust_classnum'); - push @where, 'cust_main.classnum IN('.join(',',@classnums).')' +# cust_classnum (false laziness w/ elements/cust_main_dayranges.html, elements/cust_pay_or_refund.html, prepaid_income.html, cust_bill_pay.html, cust_bill_pkg.html, cust_bill_pkg_referral.html, cust_credit.html, cust_credit_refund.html, cust_main::Search::search_sql) +if ( grep { $_ eq 'cust_classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('cust_classnum'); + push @where, 'COALESCE( cust_main.classnum, 0) IN ( '. + join(',', map { $_ || '0' } @classnums ). + ' )' if @classnums; } diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html index 5c46803d2..b863a734b 100644 --- a/httemplate/view/cust_main/billing.html +++ b/httemplate/view/cust_main/billing.html @@ -247,6 +247,10 @@ <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> % } diff --git a/httemplate/view/cust_main/change_history.html b/httemplate/view/cust_main/change_history.html index ea84b8f75..bf32a49f9 100644 --- a/httemplate/view/cust_main/change_history.html +++ b/httemplate/view/cust_main/change_history.html @@ -43,10 +43,12 @@ tie my %tables, 'Tie::IxHash', 'svc_external' => 'External service', 'svc_phone' => 'Phone', 'phone_device' => 'Phone device', + 'cust_pkg_discount' => 'Discount', #? it gets provisioned anyway 'phone_avail' => 'Phone', ; -my $svc_join = 'JOIN cust_svc USING ( svcnum ) JOIN cust_pkg USING ( pkgnum )'; +my $pkg_join = "JOIN cust_pkg USING ( pkgnum )"; +my $svc_join = "JOIN cust_svc USING ( svcnum ) $pkg_join"; my %table_join = ( 'svc_acct' => $svc_join, @@ -58,6 +60,7 @@ my %table_join = ( 'svc_external' => $svc_join, 'svc_phone' => $svc_join, 'phone_device' => $svc_join, + 'cust_pkg_discount'=> $pkg_join, ); @@ -104,7 +107,7 @@ my $conf = new FS::Conf; my $curuser = $FS::CurrentUser::CurrentUser; -die "access deined" +die "access denied" unless $curuser->access_right('View customer history'); # find out the beginning of this customer history, if possible diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index 24a12cc46..546dd89c3 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -101,7 +101,7 @@ table.usage { <TR> <TD COLSPAN=2> -% if ( $conf->exists('cust_pkg-group_by_location') and $show_location ) { +% if ( $conf->exists('cust_pkg-group_by_location') ) { <& locations.html, 'cust_main' => $cust_main, 'packages' => $packages, @@ -113,7 +113,6 @@ table.usage { <& packages/section.html, 'cust_main' => $cust_main, 'packages' => $packages, - 'show_location' => $show_location, &> </TABLE> % } @@ -140,10 +139,6 @@ my $curuser = $FS::CurrentUser::CurrentUser; my( $packages, $num_old_packages ) = get_packages($cust_main, $conf); - -my $show_location = $conf->exists('cust_pkg-always_show_location') - || (grep $_->locationnum ne $cust_main->ship_locationnum, @$packages); - my $countrydefault = scalar($conf->config('countrydefault')) || 'US'; #subroutines diff --git a/httemplate/view/cust_main/packages/contact.html b/httemplate/view/cust_main/packages/contact.html new file mode 100644 index 000000000..93129915f --- /dev/null +++ b/httemplate/view/cust_main/packages/contact.html @@ -0,0 +1,61 @@ +% if ( $contact ) { + <% $contact->line |h %> +% if ( $show_link ) { + <FONT SIZE=-1> + ( <%pkg_change_contact_link($cust_pkg)%> ) + </FONT> +% } +% } elsif ( $show_link ) { + <FONT SIZE=-1> + ( <%pkg_add_contact_link($cust_pkg)%> ) + </FONT> +% } +<%init> + +my $conf = new FS::Conf; +my %opt = @_; + +my $cust_pkg = $opt{'cust_pkg'}; + +my $show_link = + ! $cust_pkg->get('cancel') + && $FS::CurrentUser::CurrentUser->access_right('Change customer package'); + +my $contact = $cust_pkg->contact_obj; + +sub pkg_change_contact_link { + my $cust_pkg = shift; + #my $pkgpart = $cust_pkg->pkgpart; + include( '/elements/popup_link-cust_pkg.html', + 'action' => $p. "misc/change_pkg_contact.html", + 'label' => emt('Change'), # contact'), + 'actionlabel' => emt('Change'), + 'cust_pkg' => $cust_pkg, + 'width' => 616, + 'height' => 220, + ); +} + +sub pkg_add_contact_link { + my $cust_pkg = shift; + #my $pkgpart = $cust_pkg->pkgpart; + include( '/elements/popup_link-cust_pkg.html', + 'action' => $p. "misc/change_pkg_contact.html", + 'label' => emt('Add contact'), + 'actionlabel' => emt('Change'), + 'cust_pkg' => $cust_pkg, + 'width' => 616, + 'height' => 192, + ); +} + +#sub edit_contact_link { +# my $contactnum = shift; +# include( '/elements/popup_link.html', +# 'action' => $p. "edit/cust_contact.cgi?contactnum=$contactnum", +# 'label' => emt('Edit contact'), +# 'actionlabel' => emt('Edit'), +# ); +#} + +</%init> diff --git a/httemplate/view/cust_main/packages/location.html b/httemplate/view/cust_main/packages/location.html index 34e3a64c3..f2d379841 100644 --- a/httemplate/view/cust_main/packages/location.html +++ b/httemplate/view/cust_main/packages/location.html @@ -1,7 +1,5 @@ -<TD CLASS="inv" BGCOLOR="<% $bgcolor %>" WIDTH="20%"> - -% unless ( $cust_pkg->locationnum ) { - <I><FONT SIZE=-1>(<% mt('default service address') |h %>)</FONT><BR> +% if ( $default ) { + <DIV STYLE="font-style: italic; font-size: small"> % } <% $loc->location_label( 'join_string' => '<BR>', @@ -24,8 +22,8 @@ </FONT> % } -% unless ( $cust_pkg->locationnum ) { - </I> +% if ( $default ) { + </DIV> % } % if ( ! $cust_pkg->get('cancel') @@ -41,19 +39,19 @@ </FONT> % } -</TD> <%init> my $conf = new FS::Conf; my %opt = @_; -my $bgcolor = $opt{'bgcolor'}; my $cust_pkg = $opt{'cust_pkg'}; my $countrydefault = $opt{'countrydefault'} || 'US'; my $statedefault = $opt{'statedefault'} || ($countrydefault eq 'US' ? 'CA' : ''); my $loc = $cust_pkg->cust_location_or_main; +# dubious--they should all have a location now +my $default = $cust_pkg->locationnum == $opt{'cust_main'}->ship_locationnum; sub pkg_change_location_link { my $cust_pkg = shift; diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html index d0fc182cb..520305a9a 100644 --- a/httemplate/view/cust_main/packages/package.html +++ b/httemplate/view/cust_main/packages/package.html @@ -24,16 +24,19 @@ <TD COLSPAN=2> <FONT SIZE=-1> -% unless ( $cust_pkg->get('cancel') ) { +% unless ( $cust_pkg->get('cancel') ) { % -% if ( $supplemental ) { -% # then only show "Edit dates", "Add invoice details", and "Add -% # comments". +% if ( $supplemental or $part_pkg->freq eq '0' ) { +% # Supplemental packages can't be changed independently. +% # One-time charges don't need to be changed. +% # For both of those, we only show "Edit dates", "Add comments", +% # and "Add invoice details". % if ( $curuser->access_right('Edit customer package dates') ) { ( <%pkg_dates_link($cust_pkg)%> ) % } % } else { -% # the usual case +% # the usual case: links to change package definition, +% # discount, and customization % my $br = 0; % if ( $curuser->access_right('Change customer package') ) { % $br=1; @@ -181,11 +184,13 @@ % if ( $curuser->access_right('Change customer package') and % !$cust_pkg->get('cancel') and % !$supplemental and -% !$opt{'show_location'}) { +% $part_pkg->freq ne '0' ) { <TR> +% if ( FS::Conf->new->exists('invoice-unitprice') ) { <TD><FONT SIZE="-1"> - ( <% pkg_change_location_link($cust_pkg) %> ) + ( <% pkg_change_quantity_link($cust_pkg) %> ) </FONT></TD> +% } </TR> % } % } @@ -274,6 +279,17 @@ sub pkg_change_location_link { ); } +sub pkg_change_quantity_link { + include( '/elements/popup_link-cust_pkg.html', + 'action' => $p. 'edit/cust_pkg_quantity.html?', + 'label' => emt('Change quantity'), + 'actionlabel' => emt('Change'), + 'cust_pkg' => shift, + 'width' => 390, + 'height' => 220, + ); +} + sub pkg_dates_link { pkg_link('edit/REAL_cust_pkg', emt('Edit dates'), @_ ); } sub pkg_discount_link { diff --git a/httemplate/view/cust_main/packages/section.html b/httemplate/view/cust_main/packages/section.html index 52246192f..5f54c0a36 100755 --- a/httemplate/view/cust_main/packages/section.html +++ b/httemplate/view/cust_main/packages/section.html @@ -3,9 +3,7 @@ % #my $width = $show_location ? 'WIDTH="25%"' : 'WIDTH="33%"'; <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Package') |h %></TH> <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Status') |h %></TH> -% if ( $show_location ) { - <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Location') |h %></TH> -% } + <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Contact/Location') |h %></TH> <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Services') |h %></TH> </TR> @@ -13,6 +11,7 @@ % foreach my $cust_pkg (@$packages) { <& .packagerow, $cust_pkg, 'cust_main' => $opt{'cust_main'}, + 'bgcolor' => $opt{'bgcolor'}, %conf_opt &> % } @@ -27,10 +26,11 @@ <!--pkgnum: <% $cust_pkg->pkgnum %>--> <TR CLASS="row<%$row % 2%>"> <& package.html, %iopt &> - <& status.html, %iopt &> -% if ( $iopt{'show_location'} ) { - <& location.html, %iopt &> -% } + <& status.html, %iopt &> + <TD CLASS="inv" BGCOLOR="<% $iopt{bgcolor} %>" WIDTH="20%" VALIGN="top"> + <& contact.html, %iopt &> + <& location.html, %iopt &> + </TD> <& services.html, %iopt &> </TR> % $row++; @@ -51,7 +51,6 @@ my $conf = new FS::Conf; my $curuser = $FS::CurrentUser::CurrentUser; my $packages = $opt{'packages'}; -my $show_location = $opt{'show_location'}; # Sort order is hardcoded for now, can change this if needed. @$packages = sort { @@ -84,10 +83,8 @@ my %conf_opt = ( 'manage_link_loc' => scalar($conf->config('svc_broadband-manage_link_loc')), 'manage_link-new_window' => $conf->exists('svc_broadband-manage_link-new_window'), 'maestro-status_test' => $conf->exists('maestro-status_test'), - 'cust_pkg-large_pkg_size' => $conf->config('cust_pkg-large_pkg_size'), + 'cust_pkg-large_pkg_size' => scalar($conf->config('cust_pkg-large_pkg_size')), - # for packages.html Change location link - 'show_location' => $show_location, ); </%init> diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html index 6be0296a3..9d5a88e0f 100644 --- a/httemplate/view/cust_main/packages/status.html +++ b/httemplate/view/cust_main/packages/status.html @@ -1,4 +1,4 @@ -<TD CLASS="inv" BGCOLOR="<% $bgcolor %>"> +<TD CLASS="inv" BGCOLOR="<% $bgcolor %>" VALIGN="top"> <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%"> %#this should use cust_pkg->status and cust_pkg->statuscolor eventually diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html index 7701cb6b0..915be49e5 100644 --- a/httemplate/view/cust_main/payment_history.html +++ b/httemplate/view/cust_main/payment_history.html @@ -228,57 +228,20 @@ %#display payment history -%my $money_char = $conf->config('money_char') || '$'; -% -%sub balance_forward_row { -% my( $b, $date, $money_char ) = @_; -% ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/; - - <TR ID="balance_forward_row"> - <TD CLASS="grid" BGCOLOR="#dddddd"> - <% time2str($date_format, $date) %> - </TD> - - <TD CLASS="grid" BGCOLOR="#dddddd"> - <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I> - (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>) - </TD> - - <TD CLASS="grid" BGCOLOR="#dddddd"></TD> - <TD CLASS="grid" BGCOLOR="#dddddd"></TD> - <TD CLASS="grid" BGCOLOR="#dddddd"></TD> - <TD CLASS="grid" BGCOLOR="#dddddd"></TD> - <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD> - - </TR> -%} -% -%my $balance = 0; %my %target = (); % -%my $years = $conf->config('payment_history-years') || 2; -%my $older_than = time - $years * 31556926; #60*60*24*365.2422 %my $hidden = 0; %my $seen = 0; %my $old_history = 0; %my $lastdate = 0; % -%foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) { +%foreach my $item ( @history ) { % % $lastdate = $item->{'date'}; % -% my $display; -% if ( $item->{'date'} < $older_than ) { +% my $display = ''; +% if ( $item->{'hide'} ) { % $display = ' STYLE="display:none" '; -% $hidden = 1; -% } else { -% -% $display = ''; -% -% if ( $hidden && ! $seen++ ) { -% balance_forward_row($balance, $item->{'date'}, $money_char); -% } -% % } % % if ( $bgcolor eq $bgcolor1 ) { @@ -314,16 +277,8 @@ % % my $target = exists($item->{'target'}) ? $item->{'target'} : ''; % -% $balance += $item->{'charge'} if exists $item->{'charge'}; -% $balance -= $item->{'payment'} if exists $item->{'payment'}; -% $balance -= $item->{'credit'} if exists $item->{'credit'}; -% $balance += $item->{'refund'} if exists $item->{'refund'}; -% $balance = sprintf("%.2f", $balance); -% $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp -% ( my $showbalance = $money_char. $balance ) =~ s/^\$\-/- \$/; -% -% - +% my $showbalance = $money_char . $item->{'balance'}; +% $showbalance =~ s/^\$\-/- \$/; <TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>> <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>"> @@ -359,11 +314,11 @@ <% $showbalance %> </TD> </TR> -% } -%if ( scalar(@history) && $hidden && ! $seen++ ) { -% balance_forward_row($balance, $lastdate, $money_char); -%} +% if ( $item->{'balance_forward'} ) { +<& .balance_forward_row, $item->{'balance'}, $item->{'date'} &> +% } +%} # foreach $item </TABLE> </TD> @@ -386,14 +341,37 @@ function show_history () { } </SCRIPT> +<%def .balance_forward_row> +% my( $b, $date ) = @_; +% ( my $balance_forward = $money_char. $b ) =~ s/^\$\-/- \$/; -<%init> + <TR ID="balance_forward_row"> + <TD CLASS="grid" BGCOLOR="#dddddd"> + <% time2str($date_format, $date) %> + </TD> -my( $cust_main ) = @_; -my $custnum = $cust_main->custnum; + <TD CLASS="grid" BGCOLOR="#dddddd"> + <I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I> + (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>) + </TD> + <TD CLASS="grid" BGCOLOR="#dddddd"></TD> + <TD CLASS="grid" BGCOLOR="#dddddd"></TD> + <TD CLASS="grid" BGCOLOR="#dddddd"></TD> + <TD CLASS="grid" BGCOLOR="#dddddd"></TD> + <TD CLASS="grid" BGCOLOR="#dddddd" ALIGN="right"><I><% $balance_forward %></I></TD> + + </TR> +</%def> +<%shared> my $conf = new FS::Conf; my $date_format = $conf->config('date_format') || '%m/%d/%Y'; +my $money_char = $conf->config('money_char') || '$'; +</%shared> +<%init> + +my( $cust_main ) = @_; +my $custnum = $cust_main->custnum; my $curuser = $FS::CurrentUser::CurrentUser; @@ -533,6 +511,41 @@ foreach my $cust_refund ($cust_main->cust_refund) { } +# sort in forward order first, and calculate running balances +my $years = $conf->config('payment_history-years') || 2; +my $older_than = time - $years * 31556926; #60*60*24*365.2422 +my $balance = 0; + +@history = sort { $a->{date} <=> $b->{date} } @history; +my $i = 0; +my $balance_forward; +foreach my $item (@history) { + $balance += $item->{'charge'} if exists $item->{'charge'}; + $balance -= $item->{'payment'} if exists $item->{'payment'}; + $balance -= $item->{'credit'} if exists $item->{'credit'}; + $balance += $item->{'refund'} if exists $item->{'refund'}; + $balance = sprintf("%.2f", $balance); + $balance =~ s/^\-0\.00$/0.00/; + $item->{'balance'} = $balance; + + if ( $item->{'date'} < $older_than ) { + $item->{'hide'} = 1; + } elsif ( $history[$i-1]->{'hide'} ) { + # this is the end of the hidden section + $history[$i-1]->{'balance_forward'} = 1; + } + $i++; +} +if ( @history and $history[-1]->{'hide'} ) { + # then everything is hidden + $history[-1]->{'balance_forward'} = 1; +} + +# then sort in user-pref order +if ( $curuser->option('history_order') eq 'newest' ) { + @history = sort { $b->{date} <=> $a->{date} } @history; +} # else it's already oldest-first, and there are no other options yet + sub translate_payby { my ($payby,$payinfo) = (shift,shift); my %payby = ( diff --git a/httemplate/view/svc_acct.cgi b/httemplate/view/svc_acct.cgi index 76631baad..858ccbe67 100755 --- a/httemplate/view/svc_acct.cgi +++ b/httemplate/view/svc_acct.cgi @@ -22,6 +22,7 @@ % } + <& svc_acct/radius_usage.html, 'svc_acct' => $svc_acct, 'part_svc' => $part_svc, @@ -29,6 +30,7 @@ %gopt, &> + <& svc_acct/change_svc_form.html, 'part_svc' => \@part_svc, 'svcnum' => $svcnum, @@ -43,6 +45,9 @@ %gopt, &> +</FORM> + + <& svc_acct/basics.html, 'svc_acct' => $svc_acct, 'part_svc' => $part_svc, diff --git a/httemplate/view/svc_broadband.cgi b/httemplate/view/svc_broadband.cgi index 05b6ac56d..7d6520e57 100644 --- a/httemplate/view/svc_broadband.cgi +++ b/httemplate/view/svc_broadband.cgi @@ -36,6 +36,14 @@ my @fields = ( #'longitude', { field => 'coordinates', value_callback => \&coordinates }, 'altitude', + + 'radio_serialnum', + 'radio_location', + 'poe_location', + 'rssi', + 'suid', + { field => 'shared_svcnum', value_callback=> \&shared_svcnum, }, #value_callback => + 'vlan_profile', 'authkey', 'plan_id', @@ -112,9 +120,36 @@ sub coordinates { ); } +sub shared_svcnum { + my $svc_broadband = shift; + return '' unless $svc_broadband->shared_svcnum; + + my $shared_svc_broadband = + qsearchs('svc_broadband', { 'svcnum' => $svc_broadband->shared_svcnum, + } + #agent virt? + ) + or return ''; + my $shared_cust_pkg = $shared_svc_broadband->cust_svc->cust_pkg; + + $shared_svc_broadband->label. + ( $shared_cust_pkg + ? ' ('. $shared_cust_pkg->cust_main->name. ')' + : '' + ); +} + sub svc_callback { # trying to move to the callback style my ($cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt) = @_; + + if ( $part_svc->part_svc_column('latitude')->columnflag eq 'F' + && $part_svc->part_svc_column('longitude')->columnflag eq 'F' + ) + { + @$fields = grep { !ref($_) || $_->{field} ne 'coordinates' } @$fields; + } + # again, we assume at most one of these exports per part_svc my ($nas_export) = $part_svc->part_export('broadband_nas'); if ( $nas_export ) { |