diff options
Diffstat (limited to 'httemplate')
-rwxr-xr-x | httemplate/browse/part_pkg.cgi | 30 | ||||
-rw-r--r-- | httemplate/browse/part_pkg_usage.html | 112 | ||||
-rw-r--r-- | httemplate/edit/cust_main/top_misc.html | 48 | ||||
-rwxr-xr-x | httemplate/edit/process/cust_main.cgi | 12 | ||||
-rw-r--r-- | httemplate/edit/process/part_pkg_usage.html | 67 | ||||
-rw-r--r-- | httemplate/elements/auto-table.html | 59 | ||||
-rw-r--r-- | httemplate/elements/location.html | 5 | ||||
-rw-r--r-- | httemplate/pref/pref-process.html | 2 | ||||
-rw-r--r-- | httemplate/pref/pref.html | 4 | ||||
-rwxr-xr-x | httemplate/view/cust_main/packages.html | 9 | ||||
-rw-r--r-- | httemplate/view/cust_main/packages/package.html | 33 |
11 files changed, 365 insertions, 16 deletions
diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi index 5dee5b8d1..bb5bc5215 100755 --- a/httemplate/browse/part_pkg.cgi +++ b/httemplate/browse/part_pkg.cgi @@ -1,5 +1,6 @@ <% include( 'elements/browse.html', 'title' => 'Package Definitions', + 'menubar' => \@menubar, 'html_init' => $html_init, 'html_form' => $html_form, 'html_posttotal' => $html_posttotal, @@ -517,6 +518,8 @@ push @fields, sub { my $part_pkg = shift; + my @part_pkg_usage = sort { $a->priority <=> $b->priority } + $part_pkg->part_pkg_usage; [ (map { @@ -559,7 +562,27 @@ push @fields, ] } $part_pkg->svc_part_pkg_link - ) + ), + ( scalar(@part_pkg_usage) ? + [ { data => 'Usage minutes', + align => 'center', + colspan => 2, + data_style => 'b', + link => $p.'browse/part_pkg_usage.html#pkgpart'. + $part_pkg->pkgpart + } ] + : () + ), + ( map { + [ { data => $_->minutes, + align => 'right' + }, + { data => $_->description, + align => 'left' + }, + ] + } @part_pkg_usage + ), ]; }; @@ -590,4 +613,9 @@ if ( $acl_edit_bulk ) { ) . '</FORM>'; } +my @menubar; +# show this if there are any voip_cdr packages defined +if ( FS::part_pkg->count("plan = 'voip_cdr'") ) { + push @menubar, 'Per-package usage minutes' => $p.'browse/part_pkg_usage.html'; +} </%init> diff --git a/httemplate/browse/part_pkg_usage.html b/httemplate/browse/part_pkg_usage.html new file mode 100644 index 000000000..209fd3a01 --- /dev/null +++ b/httemplate/browse/part_pkg_usage.html @@ -0,0 +1,112 @@ +<& /elements/header.html, 'Package usage minutes' &> +<& /elements/menubar.html, 'Package definitions', $p.'browse/part_pkg.cgi' &> +<STYLE TYPE="text/css"> +.pkg_head { + background-color: #dddddd; + font-style: italic; +} +.pkg_head > td { + border-style: solid; + border-radius: 3px; + border-color: #555555; + border-width: 1px; +} +.usage > td { + text-align: center; +} +.error { + color: #ff0000; +} +</STYLE> +<FORM METHOD="POST" ACTION="<%$fsurl%>edit/process/part_pkg_usage.html"> + <TABLE STYLE="margin-top: 1em"> + <TR> + <TH>Minutes</TH> + <TH>Shared</TH> + <TH>Rollover</TH> + <TH>Description</TH> + <TH>Priority</TH> +% foreach my $class (@usage_class) { + <TH><% $class->classname %></TH> +% } + </TR> + +% my $error = $cgi->param('error'); +% foreach my $part_pkg (@part_pkg) { +% my $pkgpart = $part_pkg->pkgpart; +% my @part_pkg_usage; +% if ( $error ) { +% @part_pkg_usage = @{ $error->{$pkgpart} }; +% } else { +% @part_pkg_usage = $part_pkg->part_pkg_usage; +% foreach my $usage (@part_pkg_usage) { +% foreach ($usage->classnums) { +% $usage->set("class$_".'_', 'Y'); +% } +% } +% } + <TR CLASS="pkg_head" ID="pkgpart<%$pkgpart%>"> + <TD COLSPAN=<%$n_cols%>><% $part_pkg->pkg_comment %></TD> +% # make it easy to enumerate the pkgparts later + <INPUT TYPE="hidden" NAME="pkgpart" VALUE="<% $pkgpart %>"> + </TR> +% # template row + <TR id="pkgpart<%$pkgpart%>_template" CLASS="usage"> + <TD> + <INPUT TYPE="hidden" NAME="pkgusagepart"> + <INPUT TYPE="text" NAME="minutes" ID="minutes" SIZE=7> + </TD> +% foreach (qw(shared rollover)) { + <TD> + <INPUT TYPE="checkbox" NAME="<% $_ %>" ID="<% $_ %>" VALUE="Y"> + </TD> +% } + <TD> + <INPUT TYPE="text" NAME="description" ID="description" SIZE=20> + </TD> + <TD> + <INPUT TYPE="text" NAME="priority" ID="priority" SIZE=3> + </TD> +% foreach (@usage_class) { +% my $classnum = 'class' . $_->classnum . '_'; + <TD> + <INPUT TYPE="checkbox" NAME="<% $classnum %>" ID="<% $classnum %>" VALUE="Y"> + </TD> +% } + </TR> + <& /elements/auto-table.html, + table => "pkgpart$pkgpart", + template_row => "pkgpart$pkgpart".'_template', + data => \@part_pkg_usage, + &> +% } + </TABLE> + <BR> + <INPUT TYPE="submit"> +</FORM> +<& /elements/footer.html &> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right( + ['Edit package definitions', 'Edit global package definitions'] + ); + +my @where = ("(plan = 'voip_cdr' OR plan = 'voip_inbound')", + "freq != '0'", + "disabled IS NULL"); +push @where, FS::part_pkg->curuser_pkgs_sql + unless $curuser->access_right('Edit global package definitions'); +my $extra_sql = ' WHERE '.join(' AND ', @where); +my @part_pkg = qsearch({ + 'table' => 'part_pkg', + 'extra_sql' => $extra_sql, + 'order_by' => ' ORDER BY pkgpart', +}); + +my @usage_class = sort { $a->weight <=> $b->weight } + qsearch('usage_class', { disabled => '' }); + +my $n_usage_classes = scalar(@usage_class); +my $n_cols = $n_usage_classes + 5; # minutes, shared, rollover, desc, prio +</%init> diff --git a/httemplate/edit/cust_main/top_misc.html b/httemplate/edit/cust_main/top_misc.html index cfed8e4f6..f2f584fc5 100644 --- a/httemplate/edit/cust_main/top_misc.html +++ b/httemplate/edit/cust_main/top_misc.html @@ -32,6 +32,35 @@ document.getElementById('contacts_div').style.display = 'none'; } } + + var ship_locked_agents = <% encode_json(\%ship_locked_agents) %>; + var ship_fields = ['address1', 'city', 'state', 'zip', 'country', + 'latitude', 'longitude', 'district']; + function agent_changed(what) { + var agentnum = what.value; + var f = what.form; + if ( ship_locked_agents[agentnum] ) { + for(var x in ship_locked_agents[agentnum]) { + f['ship_'+x].value = ship_locked_agents[agentnum][x]; + f['ship_'+x].disabled = true; + } + f['same'].checked = false; + f['same'].disabled = true; + } else { + for(var i=0; i<ship_fields.length; i++) { + x = ship_fields[i]; + f['ship_'+x].value = ''; + f['ship_'+x].disabled = false; + } + f['same'].checked = true; + f['same'].disabled = false; + } + samechanged(f['same']); + } + window.onload = function() { + agent_changed(document.getElementById('agentnum')); + } + </SCRIPT> % foreach my $field ($cust_main->virtual_fields) { @@ -51,12 +80,13 @@ % $cust_main->agentnum($agentnum); <INPUT TYPE="hidden" NAME="lock_agentnum" VALUE="<% $agentnum %>"> - <INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agentnum %>"> + <INPUT TYPE="hidden" NAME="agentnum" ID="agentnum" + VALUE="<% $agentnum %>"> <TR> <TD ALIGN="right"><% mt('Agent') |h %></TD> <TD CLASS="fsdisabled"><% $cust_main->agent->agent |h %></TD> </TR> - + % } else { <& /elements/tr-select-agent.html, @@ -65,6 +95,7 @@ 'empty_label' => emt('Select agent'), 'disable_empty' => ( $cust_main->agentnum ? 1 : 0 ), 'viewall_right' => emt('None'), + 'onchange' => 'agent_changed(this)', &> % } @@ -201,4 +232,17 @@ my $curuser = $FS::CurrentUser::CurrentUser; my $r = qq!<font color="#ff0000">*</font> !; +# which agents lock the service address, if any +my %ship_locked_agents; +foreach (qsearch('agent',{})) { + my $agentnum = $_->agentnum; + next unless $conf->exists('agent-ship_address', $_->agentnum); + my $cust_main = $_->agent_cust_main or next; + my $agent_ship_location = $cust_main->ship_location; + $ship_locked_agents{$agentnum} = +{ + map { $_ => $agent_ship_location->$_ } + qw(address1 city state zip country latitude longitude district) + }; +} + </%init> diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi index 584462c8c..054973f23 100755 --- a/httemplate/edit/process/cust_main.cgi +++ b/httemplate/edit/process/cust_main.cgi @@ -62,6 +62,18 @@ $cgi->param('invoicing_list', join(',', @invoicing_list) ); $cgi->param('duplicate_of_custnum') =~ /^(\d+)$/; my $duplicate_of = $1; +# if this is enabled, enforce it +if ( $conf->exists('agent-ship_address', $cgi->param('agentnum')) ) { + my $agent = FS::agent->by_key($cgi->param('agentnum')); + my $agent_cust_main = $agent->agent_cust_main; + if ( $agent_cust_main ) { + my $agent_location = $agent_cust_main->ship_location; + foreach (qw(address1 city state zip country latitude longitude district)) { + $cgi->param("ship_$_", $agent_location->get($_)); + } + } +} + my %locations; for my $pre (qw(bill ship)) { diff --git a/httemplate/edit/process/part_pkg_usage.html b/httemplate/edit/process/part_pkg_usage.html new file mode 100644 index 000000000..eb6c37b82 --- /dev/null +++ b/httemplate/edit/process/part_pkg_usage.html @@ -0,0 +1,67 @@ +% if ( $is_error ) { +% $cgi->param('error' => \%part_pkg_usage); +% # internal redirect, because it's a lot of state to pass through +<& /browse/part_pkg_usage.html &> +% } else { +% # uh, not quite sure... +<% $cgi->redirect($fsurl.'browse/part_pkg.cgi') %> +% } +<%init> +my %vars = $cgi->Vars; +my %part_pkg_usage; +my $is_error; +foreach my $pkgpart ($cgi->param('pkgpart')) { + next unless $pkgpart =~ /^\d+$/; + my $part_pkg = FS::part_pkg->by_key($pkgpart) + or die "unknown pkgpart $pkgpart"; + my %old = map { $_->pkgusagepart => $_ } $part_pkg->part_pkg_usage; + $part_pkg_usage{$pkgpart} ||= []; + my @rows; + foreach (grep /^pkgpart$pkgpart/, keys %vars) { + /^pkgpart\d+_(\w+\D)(\d+)$/ or die "misspelled field name '$_'"; + my $value = delete $vars{$_}; + my $field = $1; + my $row = $2; + $rows[$row] ||= {}; + $rows[$row]->{$field} = $value; + } + + foreach my $row (@rows) { + next if !defined($row); + my $error; + my %classes; + foreach my $class (grep /^class/, keys %$row) { + $class =~ /^class(\d+)_$/; + my $classnum = $1; + $classes{$classnum} = delete $row->{$class}; + } + my $usage = FS::part_pkg_usage->new($row); + $usage->set('pkgpart', $pkgpart); + if ( $usage->pkgusagepart and $row->{minutes} > 0 ) { + $error = $usage->replace(\%classes); + # and don't delete the existing one + delete($old{$usage->pkgusagepart}); + } elsif ( $row->{minutes} > 0 ) { + $error = $usage->insert(\%classes); + } else { + next; + } + if ( $error ) { + $usage->set('error', $error); + $is_error = 1; + } + push @{ $part_pkg_usage{$pkgpart} }, $usage; + } + + foreach my $usage (values %old) { + # all of these were not sent back by the client, so delete them + my $error = $usage->delete; + if ( $error ) { + $usage->set('error', $error); + $is_error = 1; + unshift @{ $part_pkg_usage{$pkgpart} }, $usage; + } + } + +} +</%init> diff --git a/httemplate/elements/auto-table.html b/httemplate/elements/auto-table.html index 9aff94e67..3a3bd405d 100644 --- a/httemplate/elements/auto-table.html +++ b/httemplate/elements/auto-table.html @@ -70,8 +70,8 @@ function <%$pre%>set_rownum(obj, rownum) { if ( obj.id ) { obj.id = obj.id + rownum; } - if ( obj.name ) { - obj.name = obj.name + rownum; + if ( obj.getAttribute('name') ) { + obj.setAttribute('name', obj.getAttribute('name') + rownum); // also, in this case it's a form field that will be part of the record // so set up an onchange handler obj.onchange = <%$pre%>possiblyAddRow_factory(obj); @@ -96,17 +96,32 @@ function <%$pre%>addRow(data) { <%$pre%>set_rownum(row, this_rownum); if(data instanceof Array) { for (i = 0; i < data.length && i < <%$pre%>fieldorder.length; i++) { - var el = document.getElementsByName(<%$pre%>fieldorder[i] + this_rownum)[0]; + var el = document.getElementsByName(<%$pre |js_string%> + + <%$pre%>fieldorder[i] + + this_rownum)[0]; if (el) { - el.value = data[i]; + if ( el.tagName.toLowerCase() == 'span' ) { + el.innerHTML = data[i]; + } else if ( el.type == 'checkbox' ) { + el.checked = (el.value == data[i]); + } else { + el.value = data[i]; + } } } } else if (data instanceof Object) { for (var field in data) { - var el = document.getElementsByName(field + this_rownum)[0]; + var el = document.getElementsByName(<%$pre |js_string%> + + field + + this_rownum)[0]; if (el) { - el.value = data[field]; -% # doesn't work for checkbox + if ( el.tagName.toLowerCase() == 'span' ) { + el.innerHTML = data[field]; + } else if ( el.type == 'checkbox' ) { + el.checked = (el.value == data[field]); + } else { + el.value = data[field]; + } } } } // else nothing @@ -123,6 +138,20 @@ function <%$pre%>deleteRow(rownum) { <%$pre%>tbody.removeChild(r); } +function <%$pre%>set_prefix(obj) { + if ( obj.id ) { + obj.id = <%$pre |js_string%> + obj.id; + } + if ( obj.getAttribute('name') ) { + obj.setAttribute('name', <%$pre |js_string%> + obj.getAttribute('name')); + } + for (var i = 0; i < obj.children.length; i++) { + if ( obj.children[i] instanceof Node ) { + <%$pre%>set_prefix(obj.children[i]); + } + } +} + function <%$pre%>init() { <%$pre%>template = document.getElementById(<% $template_row |js_string%>); <%$pre%>tbody = document.getElementById('<%$pre%>autotable'); @@ -131,8 +160,10 @@ function <%$pre%>init() { var table = <%$pre%>template.parentNode; table.removeChild(<%$pre%>template); // give it an id - <%$pre%>template.id = <%$pre |js_string%> + 'row'; - // and a magic identifier so we know it's been submitted + <%$pre%>template.id = 'row'; + // prefix the ids and names of the TR object and all its descendants + <%$pre%>set_prefix(<%$pre%>template); + // add a magic identifier so we know it's been submitted var magic = document.createElement('INPUT'); magic.setAttribute('type', 'hidden'); magic.setAttribute('name', '<%$pre%>magic'); @@ -140,14 +171,22 @@ function <%$pre%>init() { // and a delete button %# should this be enclosed in an actual <button> for aesthetics? var delete_button = document.createElement('IMG'); - delete_button.id = 'delete_button'; + delete_button.id = '<%$pre%>delete_button'; delete_button.src = '<%$fsurl%>images/cross.png'; delete_button.alt = 'X'; // use an inline string for this so that it will be cloned properly delete_button.setAttribute('onclick', "<%$pre%>deleteRow(this.rownum);"); + // and an error display + var error_span = document.createElement('SPAN'); + error_span.className = 'error'; + error_span.style.color = '#FF0000'; + error_span.setAttribute('name', '<%$pre%>error'); + error_span.style.padding = '5px'; var delete_cell = document.createElement('TD'); + delete_cell.style.textAlign = 'left'; delete_cell.appendChild(delete_button); delete_cell.appendChild(magic); // it has to go somewhere + delete_cell.appendChild(error_span); <%$pre%>template.appendChild(delete_cell); // preload rows diff --git a/httemplate/elements/location.html b/httemplate/elements/location.html index 873fe1621..b142aa690 100644 --- a/httemplate/elements/location.html +++ b/httemplate/elements/location.html @@ -200,7 +200,7 @@ Example: </TR> % } else { % foreach (qw(latitude longitude)) { -<INPUT TYPE="hidden" NAME="<% $_ %>" VALUE="<% $object->get($_) |h%>"> +<INPUT TYPE="hidden" NAME="<% $_ %>" ID="<% $_ %>" VALUE="<% $object->get($_) |h%>"> % } % } <INPUT TYPE="hidden" NAME="<%$pre%>coord_auto" VALUE="<% $object->coord_auto %>"> @@ -226,12 +226,13 @@ Example: <TD COLSPAN=8> <INPUT TYPE="text" SIZE=15 NAME="<%$pre%>district" + ID="<%$pre%>district" VALUE="<% $object->district |h %>"> <% '(automatic)' %> </TD> </TR> % } else { - <INPUT TYPE="hidden" NAME="<%$pre%>district" VALUE="<% $object->district %>"> + <INPUT TYPE="hidden" ID="<%$pre%>" NAME="<%$pre%>district" VALUE="<% $object->district %>"> % } % } diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html index c4fef0311..84f0832bf 100644 --- a/httemplate/pref/pref-process.html +++ b/httemplate/pref/pref-process.html @@ -57,7 +57,7 @@ unless ( $error ) { # if ($access_user) { vonage-fromnumber vonage-username vonage-password cust_pkg-display_times show_pkgnum show_confitem_counts export_getsettings - show_db_profile save_db_profile + show_db_profile save_db_profile save_tmp_typesetting height width availHeight availWidth colorDepth ); diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html index 1e9671dcc..9537fed34 100644 --- a/httemplate/pref/pref.html +++ b/httemplate/pref/pref.html @@ -157,6 +157,10 @@ Development <TH>Save database profiling logs (when available): </TH> <TD><INPUT TYPE="checkbox" NAME="save_db_profile" VALUE="1" <% $curuser->option('save_db_profile') ? 'CHECKED' : '' %>></TD> </TR> + <TR> + <TH>Save temporary invoice typesetting files: </TH> + <TD><INPUT TYPE="checkbox" NAME="save_tmp_typesetting" VALUE="1" <% $curuser->option('save_tmp_typesetting') ? 'CHECKED' : '' %>></TD> + </TR> </TABLE> <BR> diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index da4d587f2..24a12cc46 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -11,6 +11,15 @@ table.package { border-spacing: 0; width: 100%; } +table.usage { + border: 1px solid black; + margin: auto; + width: 60%; + border-spacing: 0px; +} +.shared > * { + background-color: #ffffaa; +} .row0 { background-color: #eeeeee; } .row1 { background-color: #ffffff; } diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html index 3a362b6fa..d0fc182cb 100644 --- a/httemplate/view/cust_main/packages/package.html +++ b/httemplate/view/cust_main/packages/package.html @@ -190,6 +190,28 @@ % } % } </TABLE> +% if ( @cust_pkg_usage ) { + <TABLE CLASS="usage inv"> + <TR><TH COLSPAN=4><% mt('Included usage') %></TH></TR> +% foreach my $usage (@cust_pkg_usage) { +% my $part = $usage->part_pkg_usage; +% my $ratio = 255 * ($usage->minutes / $part->minutes); +% $ratio = 255 if $ratio > 255; # because rollover +% my $color = sprintf('STYLE="font-weight: bold; color: #%02x%02x00"', 255 - $ratio, $ratio); +% my $trstyle = ''; +% $trstyle = ' CLASS="shared"' if $part->shared; + <TR<%$trstyle%>> + <TD ALIGN="right"><% $part->description %>: </TD> + <TD <%$color%> ALIGN="right"><% $usage->minutes %></TD> + <TD <%$color%>> / </TD> + <TD <%$color%>><% $part->minutes %></TD> +% if ( $part->shared ) { + <TD><I>(shared)</I></TD> +% } + </TR> +% } + </TABLE> +% } </TD> @@ -208,6 +230,17 @@ my $statedefault = $opt{'statedefault'} || ($countrydefault eq 'US' ? 'CA' : ''); my $supplemental = $opt{'supplemental'} || 0; + +$cust_pkg->pkgnum =~ /^(\d+)$/; +my $pkgnum = $1; +my @cust_pkg_usage = qsearch({ + 'select' => 'cust_pkg_usage.*', + 'table' => 'cust_pkg_usage', + 'addl_from' => ' JOIN part_pkg_usage USING (pkgusagepart)', + 'extra_sql' => " WHERE pkgnum = $1", + 'order_by' => ' ORDER BY priority ASC, description ASC', +}); + #subroutines #false laziness w/status.html |