diff options
Diffstat (limited to 'httemplate/edit')
80 files changed, 2909 insertions, 1576 deletions
diff --git a/httemplate/edit/REAL_cust_pkg.cgi b/httemplate/edit/REAL_cust_pkg.cgi deleted file mode 100755 index 166a3b7ea..000000000 --- a/httemplate/edit/REAL_cust_pkg.cgi +++ /dev/null @@ -1,233 +0,0 @@ -<% include("/elements/header.html",'Customer package - Edit dates') %> - -%#, menubar( -%# "View this customer (#$custnum)" => popurl(2). "view/cust_main.cgi?$custnum", -%#)); - -<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2"> -<SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT> -<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT> -<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> - -<FORM NAME="formname" ACTION="process/REAL_cust_pkg.cgi" METHOD="POST"> -<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> - -% # raw error from below -% if ( $error ) { - <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error %></FONT> -% } -% #or, regular error handler -<% include('/elements/error.html') %> - -<% ntable("#cccccc",2) %> - - <TR> - <TD ALIGN="right">Package number</TD> - <TD BGCOLOR="#ffffff"><% $cust_pkg->pkgnum %></TD> - </TR> - - <TR> - <TD ALIGN="right">Package</TD> - <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD> - </TR> - - <TR> - <TD ALIGN="right">Custom</TD> - <TD BGCOLOR="#ffffff"><% $part_pkg->custom %></TD> - </TR> - - <TR> - <TD ALIGN="right">Comment</TD> - <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD> - </TR> - - <TR> - <TD ALIGN="right">Order taker</TD> - <TD BGCOLOR="#ffffff"><% $cust_pkg->otaker %></TD> - </TR> - - <& .row_display, cust_pkg=>$cust_pkg, column=>'order_date', label=>'Order' &> -% if ( $cust_pkg->setup && ! $cust_pkg->start_date ) { - <& .row_display, cust_pkg=>$cust_pkg, column=>'start_date', label=>'Start' &> -% } else { - <& .row_edit, cust_pkg=>$cust_pkg, column=>'start_date', label=>'Start' &> -% } - - <& .row_edit, cust_pkg=>$cust_pkg, column=>'setup', label=>'Setup' &> - <& .row_edit, cust_pkg=>$cust_pkg, column=>'last_bill', label=>$last_bill_or_renewed &> - <& .row_edit, cust_pkg=>$cust_pkg, column=>'bill', label=>$next_bill_or_prepaid_until &> -%#if ( $cust_pkg->contract_end or $part_pkg->option('contract_end_months',1) ) { - <& .row_edit, cust_pkg=>$cust_pkg, column=>'contract_end',label=>'Contract end' &> -%#} - <& .row_display, cust_pkg=>$cust_pkg, column=>'adjourn', label=>'Adjournment', note=>'(will <b>suspend</b> this package when the date is reached)' &> - <& .row_display, cust_pkg=>$cust_pkg, column=>'susp', label=>'Suspension' &> - <& .row_display, cust_pkg=>$cust_pkg, column=>'resume', label=>'Resumption', note=> '(will <b>unsuspend</b> this package when the date is reached' &> - - <& .row_display, cust_pkg=>$cust_pkg, column=>'expire', label=>'Expiration', note=>'(will <b>cancel</b> this package when the date is reached)' &> - <& .row_display, cust_pkg=>$cust_pkg, column=>'cancel', label=>'Cancellation' &> - - -<%def .row_edit> -<%args> - $cust_pkg - $column - $label - $note => '' -</%args> -% my $value = $cust_pkg->get($column); -% $value = $value ? time2str($format, $value) : ""; - - <TR> - <TD ALIGN="right"><% $label %> date</TD> - <TD> - <INPUT TYPE = "text" - NAME = "<% $column %>" - SIZE = 32 - ID = "<% $column %>_text" - VALUE = "<% $value %>" - > - <IMG SRC = "../images/calendar.png" - ID = "<% $column %>_button" - STYLE = "cursor: pointer" - TITLE = "Select date" - > -% if ( $note ) { - <BR><FONT SIZE=-1><% $note %></FONT> -% } - </TD> - </TR> - - <SCRIPT TYPE="text/javascript"> - Calendar.setup({ - inputField: "<% $column %>_text", - ifFormat: "<% $date_format %>", - button: "<% $column %>_button", - align: "BR" - }); - </SCRIPT> - -</%def> - -<%def .row_display> -<%args> - $cust_pkg - $column - $label - $note => '' -</%args> -% if ( $cust_pkg->get($column) ) { - <TR> - <TD ALIGN="right"><% $label %> date</TD> - <TD BGCOLOR="#ffffff"><% time2str($format,$cust_pkg->get($column)) %> -% if ( $note ) { - <BR><FONT SIZE=-1><% $note %></FONT> -% } - </TD> - </TR> -% } -</%def> - -</TABLE> - -<BR> -<INPUT TYPE="submit" VALUE="<% mt('Apply changes') |h %>"> -</FORM> - -<% include('/elements/footer.html') %> -<%shared> - -my $conf = new FS::Conf; -my $date_format = $conf->config('date_format') || '%m/%d/%Y'; - -my $format = $date_format. ' %T'; # %z (%Z)'; - -</%shared> -<%init> - -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Edit customer package dates'); - - -my $error = ''; -my( $pkgnum, $cust_pkg ); - -if ( $cgi->param('error') ) { - - $pkgnum = $cgi->param('pkgnum'); - - if ( $cgi->param('error') =~ /^_/ ) { - - my @errors = (); - my %errors = map { $_=>1 } split(',', $cgi->param('error')); - $cgi->param('error', ''); - - if ( $errors{'_bill_areyousure'} ) { - if ( $cgi->param('bill') =~ /^([\s\d\/\:\-\(\w\)]*)$/ ) { - my $bill = $1; - push @errors, - "You are attempting to set the next bill date to $bill, which is - in the past. This will charge the customer for the interval - from $bill until now. Are you sure you want to do this? ". - '<INPUT TYPE="checkbox" NAME="bill_areyousure" VALUE="1">'; - } - } - - if ( $errors{'_setup_areyousure'} ) { - push @errors, - "You are attempting to remove the setup date. This will re-charge the - customer for the setup fee. Are you sure you want to do this? ". - '<INPUT TYPE="checkbox" NAME="setup_areyousure" VALUE="1">'; - } - - if ( $errors{'_setupadd_areyousure'} ) { - push @errors, - "You are attempting to add a setup date. This will prevent charging the - customer for the setup fee. Are you sure you want to do this? ". - '<INPUT TYPE="checkbox" NAME="setupadd_areyousure" VALUE="1">'; - } - - if ( $errors{'_start'} ) { - push @errors, - "You are attempting to add a start date to a package that has already - started billing."; - } - - $error = join('<BR><BR>', @errors ); - - } - - #get package record - $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); - die "No package!" unless $cust_pkg; - - foreach my $col (qw( start_date setup last_bill bill )) { - my $value = $cgi->param($col); - $cust_pkg->set( $col, $value ? parse_datetime($value) : '' ); - } - -} else { - - my($query) = $cgi->keywords; - $query =~ /^(\d+)$/ or die "no pkgnum"; - $pkgnum = $1; - - #get package record - $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); - die "No package!" unless $cust_pkg; - -} - -my $part_pkg = qsearchs( 'part_pkg', { 'pkgpart' => $cust_pkg->pkgpart } ); - -my( $last_bill_or_renewed, $next_bill_or_prepaid_until ); -unless ( $part_pkg->is_prepaid ) { - #$billed_or_prepaid = 'billed'; - $last_bill_or_renewed = 'Last bill'; - $next_bill_or_prepaid_until = 'Next bill'; -} else { - #$billed_or_prepaid = 'prepaid'; - $last_bill_or_renewed = 'Renewed'; - $next_bill_or_prepaid_until = 'Prepaid until'; -} - -</%init> diff --git a/httemplate/edit/access_user.html b/httemplate/edit/access_user.html index 86ce25374..b087943c2 100644 --- a/httemplate/edit/access_user.html +++ b/httemplate/edit/access_user.html @@ -3,8 +3,7 @@ 'table' => 'access_user', 'fields' => [ 'username', - { field=>'_password', type=>'password' }, - { field=>'_password2', type=>'password' }, + @pw_fields, 'last', 'first', { field=>'user_custnum', type=>'search-cust_main', }, @@ -50,6 +49,13 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); +my @pw_fields = + FS::Auth->auth_class->can('change_password') + ? ( { field=>'_password', type=>'password' }, + { field=>'_password2', type=>'password' }, + ) + : (); + my $check_user_custnum_search = <<END; <SCRIPT TYPE="text/javascript"> function check_user_custnum_search(what) { diff --git a/httemplate/edit/agent.cgi b/httemplate/edit/agent.cgi index b043d1efe..2eddd30a4 100755 --- a/httemplate/edit/agent.cgi +++ b/httemplate/edit/agent.cgi @@ -170,9 +170,30 @@ % } </TABLE> +<BR> + +% if ( $conf->config('currencies') ) { + + <FONT CLASS="fsinnerbox-title"><% mt('Currencies') |h %></FONT> + <TABLE CLASS="fsinnerbox"> + <TR> + <TD> + <& /elements/checkboxes-table-name.html, + 'link_table' => 'agent_currency', + 'name_col' => 'currency', + 'names_list' => [ map [ $_, {label=>"$_: ".code2currency($_)} ], + $conf->config('currencies') + ], + &> + </TD> + </TR> + </TABLE> +% } <BR> + + <INPUT TYPE="submit" VALUE="<% $agent->agentnum ? "Apply changes" : "Add agent" %>"> </FORM> diff --git a/httemplate/edit/agent_payment_gateway.html b/httemplate/edit/agent_payment_gateway.html index 4a7cedf79..41a9f3e95 100644 --- a/httemplate/edit/agent_payment_gateway.html +++ b/httemplate/edit/agent_payment_gateway.html @@ -34,6 +34,7 @@ for <SELECT NAME="cardtype" MULTIPLE> % "Switch", % "Solo", % 'ACH', +% 'PayPal', %) { <OPTION VALUE="<% $cardtype %>"><% $cardtype || '(Default fallback)' %> diff --git a/httemplate/edit/agent_type.cgi b/httemplate/edit/agent_type.cgi index 8a6fbc255..b75757fb1 100755 --- a/httemplate/edit/agent_type.cgi +++ b/httemplate/edit/agent_type.cgi @@ -20,7 +20,7 @@ Select which packages agents of this type may sell to customers<BR> 'source_obj' => $agent_type, 'link_table' => 'type_pkgs', 'target_table' => 'part_pkg', - 'name_callback' => sub { $_[0]->pkg_comment(nopkgpart => 1); }, + 'name_callback' => sub { encode_entities( $_[0]->pkg_comment(nopkgpart => 1) ); }, 'target_link' => $p.'edit/part_pkg.cgi?', 'disable-able' => 1, diff --git a/httemplate/edit/bulk-cust_svc-pkgnum.html b/httemplate/edit/bulk-cust_svc-pkgnum.html new file mode 100644 index 000000000..a3437292f --- /dev/null +++ b/httemplate/edit/bulk-cust_svc-pkgnum.html @@ -0,0 +1,47 @@ +<& /elements/header-popup.html, 'Move services' &> + +Select the target package and the services to be moved.<BR><BR> + +<FORM ACTION="<%$p%>edit/process/bulk-cust_svc-pkgnum.html" METHOD=POST> + +<& /view/cust_main/packages.html, $cust_main, + no_links => 1, + before_pkg_callback => sub { + my $cust_pkg = shift; + '<INPUT TYPE="radio" NAME="pkgnum" VALUE="'. $cust_pkg->pkgnum. '">'; + }, + before_svc_callback => sub { + my $cust_svc = shift; + my $nameid = 'svcnum'. $cust_svc->svcnum; + '<TABLE CELLSPACING=0 CELLPADDING=0><TR><TD>'. + qq( <INPUT TYPE="checkbox" NAME="$nameid" ID="$nameid" VALUE="1"> ). + '</TD><TD> </TD><TD>'; + }, + after_svc_callback => sub { + #my $cust_svc = shift; + '</TD></TR></TABLE>'; + }, +&> + +<BR> +<INPUT TYPE="submit" VALUE="Move services"> + +</FORM> + + </BODY> +</HTML> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" unless $curuser->access_right('Bulk move customer services'); + +$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum'; +my $custnum = $1; + +my $cust_main = qsearchs({ + 'table' => 'cust_main', + 'hashref' => { 'custnum' => $custnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, +}) or die 'unknown customer'; + +</%init> diff --git a/httemplate/edit/bulk-part_pkg.html b/httemplate/edit/bulk-part_pkg.html new file mode 100644 index 000000000..a1c6f0c9b --- /dev/null +++ b/httemplate/edit/bulk-part_pkg.html @@ -0,0 +1,74 @@ +<& /elements/header.html, 'Edit package report classes' &> +%# change that title if we add any other editing controls + +%# this should be centralized somewhere +<STYLE TYPE="text/css"> +.row0 { background-color: #eeeeee; } +.row1 { background-color: #ffffff; } +</STYLE> + +<FORM ACTION="process/bulk-part_pkg.html" METHOD="POST"> +<DIV> +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 |h %><BR> +% } +</DIV> +<BR> +<& /elements/table-grid.html &>\ +<& /elements/tr-justtitle.html, value => mt('Report classes') &> +% my $row = 0; +% foreach my $num (sort keys %report_class) { + <TR CLASS="row<%$row % 2%>"> + <TD> +% if ( defined $initial_state{$num} ) { + <& /elements/checkbox.html, + field => 'report_option_'.$num, + value => 1, + curr_value => $initial_state{$num} + &> +% } else { +% # needs to be a tristate so that you can say "don't change it" + <& /elements/checkbox-tristate.html, field => 'report_option_'.$num &> +% } + </TD> + <TD><% $report_class{$num}->name %></TD> + </TR> +% $row++; +% } +</TABLE> +<BR> +<INPUT TYPE="submit"> +</FORM> +<& /elements/footer.html &> +<%init> +die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Bulk edit package definitions'); +my @pkgparts = $cgi->param('pkgpart') + or die "no package definitions selected"; + +my %part_pkg = map { $_ => FS::part_pkg->by_key($_) } @pkgparts; +my %part_pkg_option = map { $_ => { $part_pkg{$_}->options } } @pkgparts; +my %report_class = map { $_->num => $_ } + qsearch('part_pkg_report_option', { disabled => '' }); + +my %initial_state; +foreach my $num (keys %report_class) { + my $yes = 0; + my $no = 0; + foreach my $option (values %part_pkg_option) { + if ( $option->{"report_option_$num"} ) { + $yes = 1; + } else { + $no = 1; + } + } + if ( $yes and $no ) { + $initial_state{$num} = undef; + } elsif ( $yes ) { + $initial_state{$num} = 1; + } elsif ( $no ) { + $initial_state{$num} = 0; + } # else, uh, you didn't provide any pkgparts +} +</%init> diff --git a/httemplate/edit/cable_device.html b/httemplate/edit/cable_device.html new file mode 100644 index 000000000..eb91ad728 --- /dev/null +++ b/httemplate/edit/cable_device.html @@ -0,0 +1,114 @@ +<% include( 'elements/edit.html', + 'name' => 'Cable device', + 'table' => 'cable_device', + 'labels' => { + 'devicenum' => 'Device', + 'devicepart' => 'Device type', + 'mac_addr' => 'MAC address', + 'serial' => 'Serial number', + }, + 'fields' => [ { 'field' => 'devicepart', + 'type' => 'select-table', + 'table' => 'part_device', + 'name_col' => 'devicename', + 'onchange' => 'devicepart_changed', + 'empty_label' =>'Select device type', + #'hashref' =>{ disabled => '' }, + }, + { field => 'mac_addr', + type => 'select-mac', + }, + { 'field' => 'svcnum', + 'type' => 'hidden', + }, + ], + 'menubar' => [], #disable viewall + #'viewall_dir' => 'browse', + 'new_callback' => sub { + my( $cgi, $object ) = @_; + $object->svcnum( $cgi->param('svcnum') ); + }, + 'html_foot' => $html_foot, + ) +%> +<%init> + +#bad: pretty much entirely false laziness w/phone_device, except for labels and +# the serial field + +my @deviceparts_with_inventory = + map $_->devicepart, + qsearch({ 'table' => 'part_device', + 'extra_sql' => 'WHERE inventory_classnum IS NOT NULL', + }); + +my $html_foot = sub { + my $js = " +<SCRIPT TYPE=\"text/javascript\"> + + function opt(what,value,text) { + var optionName = new Option(text, value, false, false); + var length = what.length; + what.options[length] = optionName; + } + + function devicepart_changed(what){ + + var macsel = document.getElementById('sel_mac_addr'); + var mac = document.getElementById('mac_addr'); + + function update_macs(macs) { + for ( var i = macsel.length; i >= 0; i-- ) + macsel.options[i] = null; + + var macArray = eval('(' + macs + ')' ); + if(macArray.length == 0) + opt(macsel,'','No MAC addresses found in inventory for this device type'); + else + opt(macsel,'','Select MAC address'); + + for ( var i = 0; i < macArray.length; i++ ) { + opt(macsel,macArray[i],macArray[i]); + } + + } + + var devicepart = what.options[what.selectedIndex].value; + + var deviceparts_with_inventory = new Array("; +$js .= join(',', map qq("$_"), @deviceparts_with_inventory); +$js .= "); + + var hasInventory = false; + for ( i = 0; i < deviceparts_with_inventory.length; i++ ) { + if ( deviceparts_with_inventory[i] == devicepart ) + hasInventory = true; + } + + + if(hasInventory) { // do the AJAX thing, disable text field + macsel.style.display = 'inline'; + mac.style.display = 'none'; + mac.value = ''; + get_macs( devicepart, update_macs ); + } else { // clear & display text field only, clear/hide select + mac.style.display = 'inline'; + macsel.style.display = 'none'; + macsel.selectedIndex = 0; + } + + } + + devicepart_changed(document.getElementById('devicepart')); +</SCRIPT>"; + + $js; +}; + +# :/ needs agent-virt so you can't futz with arbitrary devices + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + + +</%init> diff --git a/httemplate/edit/cdr_carrier.html b/httemplate/edit/cdr_carrier.html new file mode 100644 index 000000000..47a358a41 --- /dev/null +++ b/httemplate/edit/cdr_carrier.html @@ -0,0 +1,19 @@ +<& elements/edit.html, + 'table' => 'cdr_carrier', + 'name_singular' => 'carrier', + 'fields' => [ + { field=>'carriername', type=>'text', size=>20 }, + { field=>'disabled', type=>'checkbox', value=>'Y' }, + ], + 'labels' => { 'carrierid' => 'Carrier', + 'carriername' => 'Carrier', + 'disabled' => 'Disabled', + }, + 'viewall_dir' => 'browse', +&> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/cdr_type.cgi b/httemplate/edit/cdr_type.cgi index 5d2c66216..c69610607 100644 --- a/httemplate/edit/cdr_type.cgi +++ b/httemplate/edit/cdr_type.cgi @@ -7,11 +7,24 @@ calls and SMS messages. Each CDR type must have a set of rates configured in the rate tables. <BR> <FORM METHOD="POST" ACTION="<% "${p}edit/process/cdr_type.cgi" %>"> -<% include('/elements/auto-table.html', - 'header' => [ 'Type#', 'Name' ], - 'fields' => [ qw( cdrtypenum cdrtypename ) ], +<TABLE ID="AutoTable" BORDER=0 CELLSPACING=0> + <TR> + <TH>Type#</TH> + <TH>Name</TH> + </TR> + <TR ID="cdr_template"> + <TD> + <INPUT NAME="cdrtypenum" SIZE=16 MAXLENGTH=16 ALIGN="right"> + </TD> + <TD> + <INPUT NAME="cdrtypename" SIZE=16 MAXLENGTH=16> + </TD> + </TR> +<& /elements/auto-table.html, + 'template_row' => 'cdr_template', 'data' => \@data, - ) %> +&> +</TABLE> <INPUT TYPE="submit" VALUE="Apply changes"> </FORM> <BR> <% include('/elements/footer.html') %> <%init> @@ -20,7 +33,6 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); my @data = ( - map { [ $_->cdrtypenum, $_->cdrtypename ] } qsearch({ 'table' => 'cdr_type', 'hashref' => {}, diff --git a/httemplate/edit/credit-cust_bill_pkg.html b/httemplate/edit/credit-cust_bill_pkg.html new file mode 100644 index 000000000..a5ecb69e3 --- /dev/null +++ b/httemplate/edit/credit-cust_bill_pkg.html @@ -0,0 +1,284 @@ +<& /elements/header-popup.html, 'Credit line items' &> + +<FORM ACTION="process/credit-cust_bill_pkg.html" METHOD="POST"> +<INPUT TYPE="hidden" NAME="crednum" VALUE=""> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum |h %>"> +<INPUT TYPE="hidden" NAME="paybatch" VALUE=""> +<INPUT TYPE="hidden" NAME="_date" VALUE="<% time %>"> +<table> + +% my $old_invnum = 0; +%# foreach my $cust_bill_pkg ( @cust_bill_pkg ) { +% foreach my $item ( @items ) { +% my( $setuprecur, $cust_bill_pkg ) = @$item; + +% my $method = $setuprecur eq 'setup' ? 'setup' : 'recur'; +% my $amount = $cust_bill_pkg->$method(); +% my $credited = $cust_bill_pkg->credited('', '', 'setuprecur'=>$method); +% $amount -= $credited; +% $amount = sprintf('%.2f', $amount); +% next unless $amount > 0; + +% if ( $cust_bill_pkg->invnum ne $old_invnum ) { + <TR><TD COLSPAN=4 BGCOLOR="#f8f8f8"> </TD></TR> + <TR><TH COLSPAN=4 BGCOLOR="#f8f8f8" ALIGN="left">Invoice #<% $cust_bill_pkg->invnum %> - <% time2str($date_format, $cust_bill_pkg->cust_bill->_date) %></TD></TR> +% $old_invnum = $cust_bill_pkg->invnum; +% } + +% my $el_name = 'billpkgnum'. $cust_bill_pkg->billpkgnum. '-'. $setuprecur; + <TR> + <TD> + <INPUT TYPE = "checkbox" + NAME = "<% $el_name %>" + ID = "<% $el_name %>" + onClick = "calc_total(this)" + data-billpkgnum = "<% $cust_bill_pkg->billpkgnum %>" + data-setuprecur = "<% $setuprecur %>" + > + </TD> + <TD BGCOLOR="#ffffff"><% $cust_bill_pkg->desc |h %></TD> +%# show one-time/setup vs recur vs usage? + <TD BGCOLOR="#ffffff" ALIGN="right"><% $money_char. $amount %></TD> + <TD ALIGN="right"> + <% $money_char %><INPUT TYPE = "text" + NAME = "<% $el_name %>-amount" + ID = "<% $el_name %>-amount" + VALUE = "<% $amount %>" + SIZE = 6 + onChange = "calc_total(this)" + STYLE = "text-align:right;" + DISABLED + > + </TD> + </TR> + +% } + +<TR><TD COLSPAN=4 BGCOLOR="#f8f8f8"> </TD></TR> +<TR> + <TD></TD> + <TD ALIGN="right" COLSPAN=2>Subtotal: </TD> + <TD ALIGN="right" ID="subtotal_td"><% $money_char %><% sprintf('%.2f', 0) %></TD> +</TR> +<TR> + <TD></TD> + <TD ALIGN="right" COLSPAN=2>Taxes: </TD> + <TD ALIGN="right" ID="taxtotal_td"><% $money_char %><% sprintf('%.2f', 0) %></TD> +</TR> +<TR> + <TD></TD> + <TH ALIGN="right" COLSPAN=2>Total credit amount: </TD> + <TH ALIGN="right" ID="total_td"><% $money_char %><% sprintf('%.2f', 0) %></TD> +</TR> + +</table> + +<INPUT TYPE="hidden" NAME="amount" ID="total_el" VALUE="0.00"> + +<table> + +<& /elements/tr-select-reason.html, + 'field' => 'reasonnum', + 'reason_class' => 'R', + #XXX reconcile both this and show_taxes wanteding to enable this + 'id' => 'select_reason', + 'control_button' => "document.getElementById('credit_button')", + 'cgi' => $cgi, +&> + +<TR> + <TD ALIGN="right"><% mt('Additional info') |h %></TD> + <TD> + <INPUT TYPE="text" NAME="addlinfo" VALUE="<% $cgi->param('addlinfo') |h %>"> + </TD> +</TR> + +% if ( $conf->exists('credits-auto-apply-disable') ) { + <INPUT TYPE="HIDDEN" NAME="apply" VALUE="no"> +% } else { + <TR> + <TD ALIGN="right"><% mt('Apply to selected line items') |h %></TD> + <TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED><% mt('yes') |h %><OPTION><% mt('no') |h %></SELECT></TD> + </TR> +% } + +</table> + +<BR> +<INPUT TYPE="submit" ID="credit_button" VALUE="Credit" DISABLED> + +</FORM> + +<% include( '/elements/xmlhttp.html', + 'url' => $p.'misc/xmlhttp-cust_bill_pkg-calculate_taxes.html', + 'subs' => [ 'calculate_taxes' ], + ) +%> +<SCRIPT TYPE="text/javascript"> + +document.getElementById('select_reason').disabled = true; + // start it disabled because no line items are selected yet +function show_taxes(arg) { + var argsHash = eval('(' + arg + ')'); + + //XXX add an 'ErrorMessage' section to the HTML and re-enable + //var error = argsHash['error']; + + //var paragraph = document.getElementById('ErrorMessage'); + //if (error) { + // paragraph.innerHTML = 'Error: ' + error; + // paragraph.style.color = '#ff0000'; + //} else { + // paragraph.innerHTML = ''; + //} + + var taxlines = argsHash['taxlines']; + +//XXX display the tax lines? just a total will do for now +// +// var table = document.getElementById('ApplicationTable'); +// +// var aFoundRow = 0; +// for (i = 0; taxlines[i]; i++) { +// var itemdesc = taxlines[i][0]; +// var locnum = taxlines[i][2]; +// if (taxlines[i][3]) { +// locnum = taxlines[i][3]; +// } +// +// var found = 0; +// for (var row = 2; table.rows[row]; row++) { +// var inputs = table.rows[row].getElementsByTagName('input'); +// if (! inputs.length) { +// while ( table.rows[row] ) { +// table.deleteRow(row); +// } +// break; +// } +// if ( inputs.item(4).value == itemdesc && inputs.item(2).value == locnum ) +// { +// inputs.item(0).value = taxlines[i][1]; +// aFoundRow = found = row; +// break; +// } +// } +// if (! found) { +// var row = table.insertRow(table.rows.length); +// var warning_cell = document.createElement('TD'); +// warning_cell.style.color = '#ff0000'; +// warning_cell.colSpan = 2; +// warning_cell.innerHTML = 'Calculated Tax - ' + itemdesc + ' - ' + +// taxlines[i][1] + ' will not be applied'; +// row.appendChild(warning_cell); +// } +// } +// +// if (aFoundRow) { +// sub_changed(table.rows[aFoundRow].getElementsByTagName('input').item(0)); +// } + + var subtotal = parseFloat( argsHash['subtotal'] ); + + var taxtotal = parseFloat( argsHash['taxtotal'] ); + document.getElementById('taxtotal_td').innerHTML = + '<% $money_char %>' + taxtotal.toFixed(2); + + var total = subtotal + taxtotal; + document.getElementById('total_td').innerHTML = + '<% $money_char %>' + total.toFixed(2); + document.getElementById('total_el').value = total.toFixed(2); + + //XXX reconcile both this and the reason selector wanteding to enable this + if ( total > 0 ) { + //document.getElementById('credit_button').disabled = false; + document.getElementById('select_reason').disabled = false; + } + +} + +function calc_total(what) { + + //document.getElementById('credit_button').disabled = true; + document.getElementById('select_reason').disabled = true; + + var subtotal = 0; + // bah, a pain, just using an attribute var re = /^billpkgnum(\d+)$/; + + var el = what.form.elements; + var billpkgnums = []; + var setuprecurs = []; + var amounts = []; + for (var i=0; i<el.length; i++) { + + if ( el[i].type == 'checkbox' ) { + var amount_el = document.getElementById( el[i].id + '-amount' ); + if ( el[i].checked ) { + amount_el.disabled = false; + var amount = amount_el.value; + subtotal += parseFloat( amount ); + amounts.push( amount ); + billpkgnums.push( el[i].getAttribute('data-billpkgnum') ); + setuprecurs.push( el[i].getAttribute('data-setuprecur') ); + } else { + amount_el.disabled = true; + } + } + + } + + document.getElementById('subtotal_td').innerHTML = + '<% $money_char %>' + subtotal.toFixed(2); + + var args = new Array( + 'custnum', '<% $custnum %>', + 'subtotal', subtotal, + 'billpkgnums', billpkgnums.join(), + 'setuprecurs', setuprecurs.join(), + 'amounts', amounts.join() + ); + + calculate_taxes( args, show_taxes ); + +} +</SCRIPT> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" unless $curuser->access_right('Credit line items'); + +#a tiny bit of false laziness w/search/cust_bill_pkg.cgi, but we're pretty +# specialized and a piece of UI, not a report +#slightly more false laziness w/httemplate/edit/elements/ApplicationCommon.html +# show_taxes & calc_total here/do_calculate_tax there + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; +my $date_format = $conf->config('date_format') || '%m/%d/%Y'; + +$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum'; +my $custnum = $1; + +my $cust_main = qsearchs({ + 'table' => 'cust_main', + 'hashref' => { 'custnum' => $custnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, +}) or die 'unknown customer'; + +my @cust_bill_pkg = qsearch({ + 'select' => 'cust_bill_pkg.*', + 'table' => 'cust_bill_pkg', + 'addl_from' => 'LEFT JOIN cust_bill USING (invnum)', + 'extra_sql' => "WHERE custnum = $custnum AND pkgnum != 0", + 'order_by' => 'ORDER BY invnum ASC, billpkgnum ASC', +}); + +my @items = map { my %hash = $_->disintegrate; + map [ $_, $hash{$_} ], + keys(%hash); + } + @cust_bill_pkg; + +#omit line items which have been previously credited? would be nice + +</%init> diff --git a/httemplate/edit/currency_exchange.html b/httemplate/edit/currency_exchange.html new file mode 100755 index 000000000..573ace5ee --- /dev/null +++ b/httemplate/edit/currency_exchange.html @@ -0,0 +1,73 @@ +<& /elements/header.html, 'Exchange rates' &> + +<FORM METHOD="POST" ACTION="process/currency_exchange.html"> + +<& /elements/table-grid.html &> +% my $bgcolor1 = '#eeeeee'; +% my $bgcolor2 = '#ffffff'; +% my $bgcolor = ''; + +<TR> + <TH CLASS="grid" BGCOLOR="#cccccc">From</TH> + <TH CLASS="grid" BGCOLOR="#cccccc">Rate</TH> + <TH CLASS="grid" BGCOLOR="#cccccc">To</TH> +</TR> + +%foreach my $currency (@currencies) { +% +% if ( $bgcolor eq $bgcolor1 ) { +% $bgcolor = $bgcolor2; +% } else { +% $bgcolor = $bgcolor1; +% } +% +% my %hash = ( 'from_currency' => $currency, +% 'to_currency' => $to_currency, +% ); +% +% my $currency_exchange = qsearchs('currency_exchange', \%hash) +% || new FS::currency_exchange \%hash; +% +% $currency_exchange->rate('1.000000') if length($currency_exchange->rate) == 0; + + <TR> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> + <% $currency %>: <% code2currency($currency) %> + </TD> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="right"> + <INPUT TYPE = "text" + NAME = "<% "$currency-$to_currency" %>" + VALUE = "<% $currency_exchange->rate %>" + SIZE = 14 + MAXLENGTH = 14 + > + </TD> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> + <% $to_currency %>: <% code2currency($to_currency) %> + </TD> + + </TR> +% } + + </TABLE> + +<BR> +<INPUT TYPE="submit" VALUE="Update rates"> +</FORM> + +<& /elements/footer.html &> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; + +my $to_currency = $conf->config('currency') || 'USD'; + +my @currencies = sort { $a cmp $b } $conf->config('currencies'); + +</%init> diff --git a/httemplate/edit/cust_credit.cgi b/httemplate/edit/cust_credit.cgi index 6e8a9c989..09300c629 100755 --- a/httemplate/edit/cust_credit.cgi +++ b/httemplate/edit/cust_credit.cgi @@ -8,7 +8,6 @@ <INPUT TYPE="hidden" NAME="paybatch" VALUE=""> <INPUT TYPE="hidden" NAME="_date" VALUE="<% $_date %>"> <INPUT TYPE="hidden" NAME="credited" VALUE=""> -<INPUT TYPE="hidden" NAME="otaker" VALUE="<% $otaker %>"> <% ntable("#cccccc", 2) %> @@ -34,6 +33,7 @@ <TD> <INPUT TYPE="text" NAME="addlinfo" VALUE="<% $cgi->param('addlinfo') |h %>"> </TD> + </TR> % if ( $conf->exists('credits-auto-apply-disable') ) { <INPUT TYPE="HIDDEN" NAME="apply" VALUE="no"> @@ -73,7 +73,6 @@ die "access denied" my $custnum = $cgi->param('custnum'); my $amount = $cgi->param('amount'); my $_date = time; -my $otaker = getotaker; my $p1 = popurl(1); </%init> diff --git a/httemplate/edit/cust_location.cgi b/httemplate/edit/cust_location.cgi index 80b27c2b3..93ce32382 100755 --- a/httemplate/edit/cust_location.cgi +++ b/httemplate/edit/cust_location.cgi @@ -7,20 +7,37 @@ 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();', + 'with_census' => 1, +&> </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()"> +function submit_abort() { + nd(1); +} +</SCRIPT> +<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 2628b4e01..d597d0bc2 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,19 +91,14 @@ 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')); +//samechanged(document.getElementById('same')); </SCRIPT> <BR> @@ -285,7 +277,8 @@ if ( $cgi->param('error') ) { my( $query ) = $cgi->keywords; $query =~ /^(\d+)$/; $custnum=$1; - $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ); + $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) + or die "custnum $custnum not found"; if ( $cust_main->dbdef_table->column('paycvv') && length($cust_main->paycvv) ) { my $paycvv = $cust_main->paycvv; @@ -306,7 +299,6 @@ if ( $cgi->param('error') ) { $cust_main = new FS::cust_main ( {} ); $cust_main->agentnum( $conf->config('default_agentnum') ) if $conf->exists('default_agentnum'); - $cust_main->otaker( &getotaker ); $cust_main->referral_custnum( $cgi->param('referral_custnum') ); @invoicing_list = (); push @invoicing_list, 'POST' @@ -355,14 +347,18 @@ if ( $cgi->param('error') ) { my $countrydefault = $conf->config('countrydefault') || 'US'; my $statedefault = $conf->config('statedefault') || 'CA'; $cust_main->set('bill_location', - FS::cust_location->new( - { country => $countrydefault, state => $statedefault } - ) + FS::cust_location->new( { + country => $countrydefault, + state => $statedefault, + coord_auto => 'Y', + } ) ); $cust_main->set('ship_location', - FS::cust_location->new( - { country => $countrydefault, state => $statedefault } - ) + FS::cust_location->new( { + country => $countrydefault, + state => $statedefault, + coord_auto => 'Y', + } ) ); } diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html index 2925ca87c..da5f0f27f 100644 --- a/httemplate/edit/cust_main/billing.html +++ b/httemplate/edit/cust_main/billing.html @@ -444,10 +444,11 @@ <TR><TD> </TD></TR> +% my $curuser = $FS::CurrentUser::CurrentUser; % my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); - % if ( $conf->exists('cust_class-tax_exempt') % || $conf->exists('tax-cust_exempt-groups-require_individual_nums') +% || ! $curuser->access_right('Edit customer tax exemptions') % ) % { @@ -461,14 +462,16 @@ % } -% foreach my $exempt_group ( @exempt_groups ) { -% my $cust_main_exemption = $cust_main->tax_exemption($exempt_group); -% #escape $exempt_group for NAME etc. -% my $checked = ($cust_main_exemption || $cgi->param("tax_$exempt_group")); - <TR> - <TD> <INPUT TYPE="checkbox" NAME="tax_<% $exempt_group %>" ID="tax_<% $exempt_group %>" VALUE="Y" <% $checked ? 'CHECKED' : '' %> onChange="tax_changed(this)"> Tax Exempt (<% $exempt_group %> taxes)</TD> - <TD> - Exemption number <INPUT TYPE="text" NAME="tax_<% $exempt_group %>_num" ID="tax_<% $exempt_group %>_num" VALUE="<% $cgi->param("tax_$exempt_group".'_num') || ( $cust_main_exemption ? $cust_main_exemption->exempt_number : '' ) |h %>" <% $checked ? '' : 'DISABLED' %>></TD> - </TR> +% if ( $curuser->access_right('Edit customer tax exemptions') ) { +% foreach my $exempt_group ( @exempt_groups ) { +% my $cust_main_exemption = $cust_main->tax_exemption($exempt_group); +% #escape $exempt_group for NAME etc. +% my $checked = ($cust_main_exemption || $cgi->param("tax_$exempt_group")); + <TR> + <TD> <INPUT TYPE="checkbox" NAME="tax_<% $exempt_group %>" ID="tax_<% $exempt_group %>" VALUE="Y" <% $checked ? 'CHECKED' : '' %> onChange="tax_changed(this)"> Tax Exempt (<% $exempt_group %> taxes)</TD> + <TD> - Exemption number <INPUT TYPE="text" NAME="tax_<% $exempt_group %>_num" ID="tax_<% $exempt_group %>_num" VALUE="<% $cgi->param("tax_$exempt_group".'_num') || ( $cust_main_exemption ? $cust_main_exemption->exempt_number : '' ) |h %>" <% $checked ? '' : 'DISABLED' %>></TD> + </TR> +% } % } % unless ( $conf->exists('emailinvoiceonly') ) { @@ -518,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> % } @@ -606,6 +615,23 @@ function toggle(obj) { <INPUT TYPE="hidden" NAME="cdr_termination_percentage" VALUE="<% $cust_main->cdr_termination_percentage %>"> % } +%my @currencies = $conf->config('currencies'); +%if ( scalar(@currencies) ) { +% unshift @currencies, ''; #default +% my %currency_labels = map { $_ => "$_: ". code2currency($_) } @currencies; +% $currency_labels{''} = +% 'Default: '. code2currency( $conf->config('currency') || 'USD' ); + + <& /elements/tr-select.html, + 'label' => emt('Invoicing currency'), + 'field' => 'currency', + 'options' => \@currencies, + 'labels' => \%currency_labels, + 'curr_value' => $cust_main->currency, + &> +% } + + %my @available_locales = $conf->config('available-locales'); %if ( scalar(@available_locales) ) { % push @available_locales, '' diff --git a/httemplate/edit/cust_main/bottomfixup.html b/httemplate/edit/cust_main/bottomfixup.html index 60edcc111..b5d10c467 100644 --- a/httemplate/edit/cust_main/bottomfixup.html +++ b/httemplate/edit/cust_main/bottomfixup.html @@ -1,15 +1,9 @@ <& /elements/init_overlib.html &> <& /elements/xmlhttp.html, - url => $p.'misc/xmlhttp-cust_main-address_standardize.html', + url => $p.'misc/xmlhttp-address_standardize.html', subs => [ 'address_standardize' ], - #'method' => 'POST', #could get too long? -&> - -<& /elements/xmlhttp.html, - url => $p.'misc/xmlhttp-cust_main-censustract.html', - subs => [ 'censustract' ], - #'method' => 'POST', #could get too long? + method => 'POST', #could get too long? &> <INPUT TYPE="hidden" NAME="duplicate_of_custnum" VALUE=""> diff --git a/httemplate/edit/cust_main/bottomfixup.js b/httemplate/edit/cust_main/bottomfixup.js index 77d4294a6..9e18fa0df 100644 --- a/httemplate/edit/cust_main/bottomfixup.js +++ b/httemplate/edit/cust_main/bottomfixup.js @@ -7,26 +7,31 @@ my $company_longitude = $conf->config('company_longitude'); my @fixups = ('copy_payby_fields', 'standardize_locations'); -push @fixups, 'fetch_censustract' +push @fixups, 'confirm_censustract' if $conf->exists('cust_main-require_censustract'); -push @fixups, 'check_unique' - if $conf->exists('cust_main-check_unique') and !$opt{'custnum'}; +# currently doesn't work; disable to avoid problems +#push @fixups, 'check_unique' +# if $conf->exists('cust_main-check_unique') and !$opt{'custnum'}; push @fixups, 'do_submit'; # always last </%init> var fixups = <% encode_json(\@fixups) %>; var fixup_position; +var running = false; %# state machine to deal with all the asynchronous stuff we're doing %# call this after each fixup on success: function submit_continue() { - window[ fixups[fixup_position++] ].call(); + if ( running ) { + window[ fixups[fixup_position++] ].call(); + } } %# or on failure: function submit_abort() { + running = false; fixup_position = 0; document.CustomerForm.submitButton.disabled = false; cClick(); @@ -35,6 +40,7 @@ function submit_abort() { function bottomfixup(what) { fixup_position = 0; document.CustomerForm.submitButton.disabled = true; + running = true; submit_continue(); } @@ -63,112 +69,11 @@ function copy_payby_fields() { submit_continue(); } -%# call submit_continue() on completion... -%# otherwise not touching standardize_locations for now -<% include( '/elements/standardize_locations.js', - 'callback' => 'submit_continue();', - 'main_prefix' => 'bill_', - 'no_company' => 1, - ) -%> - -var prefix; -function fetch_censustract() { - - //alert('fetch census tract data'); - prefix = document.getElementById('same').checked ? 'bill_' : 'ship_'; - var cf = document.CustomerForm; - var state_el = cf.elements[prefix + 'state']; - var census_data = new Array( - 'year', <% $conf->config('census_year') || '2012' %>, - 'address1', cf.elements[prefix + 'address1'].value, - 'city', cf.elements[prefix + 'city'].value, - 'state', state_el.options[ state_el.selectedIndex ].value, - 'zip', cf.elements[prefix + 'zip'].value - ); - - censustract( census_data, update_censustract ); - -} - -var set_censustract; - -function update_censustract(arg) { - - var argsHash = eval('(' + arg + ')'); - - var cf = document.CustomerForm; - -/* var msacode = argsHash['msacode']; - var statecode = argsHash['statecode']; - var countycode = argsHash['countycode']; - var tractcode = argsHash['tractcode']; - - var newcensus = - new String(statecode) + - new String(countycode) + - new String(tractcode).replace(/\s$/, ''); // JSON 1 workaround */ - var error = argsHash['error']; - var newcensus = argsHash['censustract']; - - set_censustract = function () { - - cf.elements[prefix + 'censustract'].value = newcensus; - submit_continue(); - - } - - if (error || cf.elements[prefix + 'censustract'].value != newcensus) { - // popup an entry dialog - - if (error) { newcensus = error; } - newcensus.replace(/.*ndefined.*/, 'Not found'); - - var latitude = cf.elements[prefix + 'latitude'].value - || '<% $company_latitude %>'; - var longitude= cf.elements[prefix + 'longitude'].value - || '<% $company_longitude %>'; - - var choose_censustract = - '<CENTER><BR><B>Confirm censustract</B><BR>' + - '<A href="http://maps.ffiec.gov/FFIECMapper/TGMapSrv.aspx?' + - 'census_year=<% $conf->config('census_year') || '2012' %>' + - '&latitude=' + latitude + - '&longitude=' + longitude + - '" target="_blank">Map service module location</A><BR>' + - '<A href="http://maps.ffiec.gov/FFIECMapper/TGMapSrv.aspx?' + - 'census_year=<% $conf->config('census_year') || '2012' %>' + - '&zip_code=' + cf.elements[prefix + 'zip'].value + - '" target="_blank">Map zip code center</A><BR><BR>' + - '<TABLE>'; - - choose_censustract = choose_censustract + - '<TR><TH style="width:50%">Entered census tract</TH>' + - '<TH style="width:50%">Calculated census tract</TH></TR>' + - '<TR><TD>' + cf.elements[prefix + 'censustract'].value + - '</TD><TD>' + newcensus + '</TD></TR>' + - '<TR><TD> </TD><TD> </TD></TR>'; - - choose_censustract = choose_censustract + - '<TR><TD ALIGN="center">' + - '<BUTTON TYPE="button" onClick="submit_continue();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered census tract </BUTTON>' + - '</TD><TD ALIGN="center">' + - '<BUTTON TYPE="button" onClick="set_censustract();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use calculated census tract </BUTTON>' + - '</TD></TR>' + - '<TR><TD COLSPAN=2 ALIGN="center">' + - '<BUTTON TYPE="button" onClick="submit_abort();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' + - - '</TABLE></CENTER>'; - - overlib( choose_censustract, CAPTION, 'Confirm censustract', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 ); - - } else { - - submit_continue(); - - } - -} +<& /elements/standardize_locations.js, + 'callback' => 'submit_continue();', + 'billship' => 1, + 'with_census' => 1, # no with_firm, apparently +&> function copyelement(from, to) { if ( from == undefined ) { @@ -192,6 +97,40 @@ function copyelement(from, to) { //alert(from + " (" + from.type + "): " + to.name + " => " + to.value); } +% # the value in pre+'censustract' is the confirmed censustract; if it's set, +% # do nothing here +function confirm_censustract() { + var cf = document.CustomerForm; + var pre = cf.elements['same'].checked ? 'bill_' : 'ship_'; + if ( cf.elements[pre+'censustract'].value == '' ) { + var address_info = form_address_info(); + address_info[pre+'latitude'] = cf.elements[pre+'latitude'].value; + address_info[pre+'longitude'] = cf.elements[pre+'longitude'].value; + OLpostAJAX( + '<%$p%>/misc/confirm-censustract.html', + 'q=' + encodeURIComponent(JSON.stringify(address_info)), + function() { + overlib( OLresponseAJAX, CAPTION, 'Confirm censustract', STICKY, + AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, + 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', + TEXTSIZE, 3 ); + }, + 0); + } else submit_continue(); +} + +%# called from confirm-censustract.html +function set_censustract(tract, year) { + var cf = document.CustomerForm; + var pre = 'ship_'; + if ( cf.elements['same'].checked ) { + pre = 'bill_'; + } + cf.elements[pre + 'censustract'].value = tract; + cf.elements[pre + 'censusyear'].value = year; + submit_continue(); +} + function check_unique() { var search_hash = new Object; % foreach ($conf->config('cust_main-check_unique')) { diff --git a/httemplate/edit/cust_main/choose_tax_location.html b/httemplate/edit/cust_main/choose_tax_location.html deleted file mode 100644 index ac475c54b..000000000 --- a/httemplate/edit/cust_main/choose_tax_location.html +++ /dev/null @@ -1,87 +0,0 @@ -<FORM NAME="choosegeocodeform"> -<CENTER><BR><B>Choose tax location</B><BR><BR> -<P>the geocode is:<% $header %></P> -<P STYLE="<% $style %>"><% $header %></P> - -<SELECT NAME='geocodes' ID='geocodes' STYLE="<% $style %>"> -% foreach my $location (@cust_tax_location) { -% my %value = ( zip => $zip5, -% map { $_ => $location->$_ } -% qw ( city state geocode ) -% ); -% map { $value{$_} = $location{$_} } qw ( city state ) -% if $location{country} eq 'CA'; -% -% my $value = encode_entities(objToJson({ %value }) -% ); -% my $content = ''; -% $content .= $location->$_. ' ' x ( $max{$_} - length($location->$_) ) -% foreach qw( city county state ); -% $content .= $location->cityflag eq 'I' ? 'Y' : 'N' ; -% my $selected = '' ; -% if ($geocode && $location->geocode eq $geocode) { -% $selected = 'SELECTED'; -% } - <OPTION VALUE="<% $value %>" STYLE="<% $style %>" <% $selected %>><% $content %> -% } -</SELECT><BR><BR> - -<TABLE><TR> - <TD> <BUTTON TYPE="button" onClick="set_geocode(document.getElementById('geocodes'));"><IMG SRC="<%$p%>images/tick.png" ALT=""> Set location </BUTTON></TD> - <TD><BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission </BUTTON></TD> -</TR> -</TABLE> - -</CENTER> -</FORM> -<%init> - -my $conf = new FS::Conf; - -my %location = (); - -($location{data_vendor}) = $cgi->param('data_vendor') =~ /^([-\w]+)$/; -($location{city}) = $cgi->param('city') =~ /^([\w ]+)$/; -($location{state}) = $cgi->param('state') =~ /^(\w+)$/; -($location{zip}) = $cgi->param('zip') =~ /^([-\w ]+)$/; -($location{country}) = $cgi->param('country') =~ /^([\w ]+)$/; - -my($geocode) = $cgi->param('geocode') =~ /^([\w]+)$/; - -my($zip5, $zip4) = split('-', $location{zip}); - -#only support US & CA -my $hashref = { 'data_vendor' => $location{data_vendor} }; -$hashref->{zip} = $location{country} eq 'CA' ? substr($zip5,0,1) : $zip5, - -my @keys = keys(%$hashref); -my @cust_tax_location = (); -until ( @cust_tax_location ) { - @cust_tax_location = qsearch({ table => 'cust_tax_location', - hashref => $hashref, - order_by => 'LIMIT 50', - }); - last unless scalar(@keys); - delete $hashref->{ shift @keys }; -} - -my %max = ( city => 4, county => 6, state => 5); -foreach my $location (@cust_tax_location) { - foreach ( qw( city county state ) ) { - my $length = length($location->$_); - $max{$_} = ($length > $max{$_}) ? $length : $max{$_}; - } -} -foreach ( qw( city county state ) ) { - $max{$_} = $location{$_} if $location{$_} > $max{$_}; - $max{$_}++; -} - -my $header = ' '; -$header .= $_. ' ' x ( $max{lc($_)} - length($_) ) - foreach qw( City County State ); -$header .= "In city?"; - -my $style = "font-family:monospace;"; - -</%init> diff --git a/httemplate/edit/cust_main/contact.html b/httemplate/edit/cust_main/contact.html index 57490b962..4140ec1ea 100644 --- a/httemplate/edit/cust_main/contact.html +++ b/httemplate/edit/cust_main/contact.html @@ -174,9 +174,7 @@ $cust_main->set('stateid_state', $cust_main->state ) $opt{geocode} ||= $cust_main->get('geocode'); -if ( $conf->exists('cust_main-require_censustract') ) { - $opt{censustract} ||= $cust_main->censustract; -} +$opt{censustract} ||= $cust_main->censustract; $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/ ? 'Day' diff --git a/httemplate/edit/cust_main/first_pkg/select-part_pkg.html b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html index 709a8ad6c..4f8914065 100644 --- a/httemplate/edit/cust_main/first_pkg/select-part_pkg.html +++ b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html @@ -111,7 +111,7 @@ &> <SCRIPT TYPE="text/javascript"> - pkgpart_svcpart_changed_too( document.CustomerForm.pkgpart_svcpart, + pkgpart_svcpart_changed_too( document.<% $opt{form_name} || 'CustomerForm' %>.pkgpart_svcpart, <% $opt{saved_domsvc} %> ); </SCRIPT> diff --git a/httemplate/edit/cust_main/first_pkg/svc_acct.html b/httemplate/edit/cust_main/first_pkg/svc_acct.html index b1ccc137c..717bf5025 100644 --- a/httemplate/edit/cust_main/first_pkg/svc_acct.html +++ b/httemplate/edit/cust_main/first_pkg/svc_acct.html @@ -5,7 +5,7 @@ <TD> <INPUT TYPE = "text" NAME = "username" - VALUE = "<% $opt{'username'} %>" + VALUE = "<% $opt{'username'} |h %>" SIZE = <% $ulen2 %> MAXLENGTH = <% $ulen %> > @@ -26,7 +26,7 @@ <TD> <INPUT TYPE = "text" NAME = "_password" - VALUE = "<% $opt{'password'} %>" + VALUE = "<% $opt{'password'} |h %>" SIZE = <% $pmax2 %> MAXLENGTH = <% $passwordmax %>> % unless ( $opt{'password_verify'} ) { @@ -41,7 +41,7 @@ <TD> <INPUT TYPE = "text" NAME = "_password2" - VALUE = "<% $opt{'password2'} %>" + VALUE = "<% $opt{'password2'} |h %>" SIZE = <% $pmax2 %> MAXLENGTH = <% $passwordmax %>> </TD> @@ -51,7 +51,7 @@ % if ( $conf->exists('security_phrase') ) { <TR> <TD ALIGN="right"><% mt('Security Phrase') |h %></TD> - <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $opt{'sec_phrase'} %>"> + <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $opt{'sec_phrase'} |h %>"> </TD> </TR> % } else { diff --git a/httemplate/edit/cust_main/top_misc.html b/httemplate/edit/cust_main/top_misc.html index 7ce283c6c..b7e86ba78 100644 --- a/httemplate/edit/cust_main/top_misc.html +++ b/httemplate/edit/cust_main/top_misc.html @@ -32,6 +32,44 @@ 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 this agent, the service location (except address2) +% # should be locked to the agent's location. +% # Set the ship_ fields to those values (just for display) and +% # then disable them. + 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 { +% # Unlock the ship_ location fields. If they were previously +% # disabled, then they contain some agent's address, which is +% # no longer meaningful. So set them back to the customer's +% # current location. + for(var i=0; i<ship_fields.length; i++) { + x = ship_fields[i]; + if ( f['ship_'+x].disabled ) { + f['ship_'+x].value = f['old_ship_'+x].value; + } + f['ship_'+x].disabled = false; + } + 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 +89,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 +104,7 @@ 'empty_label' => emt('Select agent'), 'disable_empty' => ( $cust_main->agentnum ? 1 : 0 ), 'viewall_right' => emt('None'), + 'onchange' => 'agent_changed(this)', &> % } @@ -114,7 +154,7 @@ <TR> <TD ALIGN="right"><% mt('Referring customer') |h %></TD> <TD> - <A HREF="<% popurl(1) %>/cust_main.cgi?<% $cust_main->referral_custnum %>"><% $cust_main->referral_custnum %>: <% $referring_cust_main->name %></A> + <A HREF="<% popurl(1) %>/cust_main.cgi?<% $cust_main->referral_custnum %>"><% $cust_main->referral_custnum %>: <% $referring_cust_main->name |h %></A> </TD> </TR> <INPUT TYPE="hidden" NAME="referral_custnum" VALUE="<% $cust_main->referral_custnum %>"> @@ -201,4 +241,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/cust_pkg.cgi b/httemplate/edit/cust_pkg.cgi index dd1ed335f..d86049940 100755 --- a/httemplate/edit/cust_pkg.cgi +++ b/httemplate/edit/cust_pkg.cgi @@ -7,7 +7,6 @@ <INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> %#current packages -%my @cust_pkg = qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } ); %if (@cust_pkg) { Current packages - select to remove (services are moved to a new package below) @@ -18,13 +17,7 @@ </TR> <BR><BR> % -% -% foreach ( sort { $all_pkg{ $a->getfield('pkgpart') } -% cmp $all_pkg{ $b->getfield('pkgpart') } -% } -% @cust_pkg -% ) -% { +% foreach ( @main_pkgs ) { % my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') ); % my $checked = $remove_pkg{$pkgnum} ? ' CHECKED' : ''; % @@ -34,8 +27,15 @@ <TR> <TD><INPUT TYPE="checkbox" NAME="remove_pkg" VALUE="<% $pkgnum %>"<% $checked %>></TD> <TD ALIGN="right"><% $pkgnum %>:</TD> - <TD><% $all_pkg{$pkgpart} %> - <% $all_comment{$pkgpart} %></TD> + <TD><% $all_pkg{$pkgpart} |h %> - <% $all_comment{$pkgpart} |h %></TD> + </TR> +% foreach my $supp_pkg ( @{ $supp_pkgs_of{$pkgnum} } ) { + <TR> + <TD></TD> + <TD></TD> + <TD>+ <% $all_pkg{$supp_pkg->pkgpart} |h %> - <% $all_comment{$supp_pkg->pkgpart} |h %></TD> </TR> +% } % } @@ -79,7 +79,7 @@ Order new packages <INPUT TYPE="text" NAME="<% "pkg$pkgpart" %>" VALUE="<% $value %>" SIZE="2" MAXLENGTH="2"> </TD> <TD ALIGN="right"><% $pkgpart %>:</TD> - <TD><% $pkg{$pkgpart} %> - <% $comment{$pkgpart}%></TD> + <TD><% $pkg{$pkgpart} |h %> - <% $comment{$pkgpart} |h %></TD> </TR> % % $count ++ ; @@ -147,4 +147,24 @@ if ( $cgi->param('error') ) { my $p1 = popurl(1); +my @cust_pkg = qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } ); +my @main_pkgs; +my %supp_pkgs_of; # main pkgnum => arrayref of cust_pkgs + + +foreach my $cust_pkg + ( sort { $all_pkg{ $a->pkgpart } cmp $all_pkg{ $b->getfield('pkgpart') } } + @cust_pkg + ) + # XXX does not properly handle recursive supplemental links +{ + if ( my $main_pkgnum = $cust_pkg->main_pkgnum ) { + $supp_pkgs_of{$main_pkgnum} ||= []; + push @{ $supp_pkgs_of{$main_pkgnum} }, $cust_pkg; + } else { + push @main_pkgs, $cust_pkg; + $supp_pkgs_of{$cust_pkg->pkgnum} ||= []; + } +} + </%init> diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/cust_pkg_detail.html index 009ed5c6e..5e107066d 100644 --- a/httemplate/edit/cust_pkg_detail.html +++ b/httemplate/edit/cust_pkg_detail.html @@ -28,7 +28,7 @@ <TR> <TD ALIGN="right">Comment</TD> - <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->comment |h %></TD> </TR> <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/cust_refund.cgi b/httemplate/edit/cust_refund.cgi index 1ef69fdae..df42e63ae 100755 --- a/httemplate/edit/cust_refund.cgi +++ b/httemplate/edit/cust_refund.cgi @@ -59,33 +59,29 @@ </TD> </TR> % } - -% -% #false laziness w/FS/FS/cust_main::realtime_refund_bop -% if ( $cust_pay->paybatch =~ /^(\w+):(\w+)(:(\w+))?$/ ) { -% my ( $processor, $auth, $order_number ) = ( $1, $2, $4 ); -% - - +% if ( $cust_pay->processor ) { <TR> - <TD ALIGN="right">Processor</TD><TD BGCOLOR="#ffffff"><% $processor %></TD> + <TD ALIGN="right">Processor</TD> + <TD BGCOLOR="#ffffff"><% $cust_pay->processor %></TD> </TR> -% if ( length($auth) ) { +% if ( length($cust_pay->auth) ) { <TR> - <TD ALIGN="right">Authorization</TD><TD BGCOLOR="#ffffff"><% $auth %></TD> + <TD ALIGN="right">Authorization</TD> + <TD BGCOLOR="#ffffff"><% $cust_pay->auth %></TD> </TR> % } -% if ( length($order_number) ) { +% if ( length($cust_pay->order_number) ) { <TR> - <TD ALIGN="right">Order number</TD><TD BGCOLOR="#ffffff"><% $order_number %></TD> + <TD ALIGN="right">Order number</TD> + <TD BGCOLOR="#ffffff"><% $cust_pay->order_number %></TD> </TR> % } -% } +% } # if ($cust_pay->processor) </TABLE> -% } +% } #if $cust_pay <BR>Refund diff --git a/httemplate/edit/elements/ApplicationCommon.html b/httemplate/edit/elements/ApplicationCommon.html index 7b1050ade..acc3368b8 100644 --- a/httemplate/edit/elements/ApplicationCommon.html +++ b/httemplate/edit/elements/ApplicationCommon.html @@ -441,8 +441,6 @@ if ( $cgi->param('error') ) { $dst_pkeyvalue = ''; } -my $otaker = getotaker; - my $p1 = popurl(1); my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } ); diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html index a24f23805..08408297b 100644 --- a/httemplate/edit/elements/edit.html +++ b/httemplate/edit/elements/edit.html @@ -282,6 +282,7 @@ Example: % #text and derivitives % 'size' => $f->{'size'}, % 'maxlength' => $f->{'maxlength'}, +% 'prefix' => $f->{'prefix'}, % 'postfix' => $f->{'postfix'}, % % #textarea @@ -329,6 +330,7 @@ Example: % qw( country ), #select-country % qw( width height ), #htmlarea % qw( alt_format ), #select-cust_location +% qw( classnum ), # select-inventory_item % ; % % #select-table diff --git a/httemplate/edit/elements/part_export/broadband_snmp.html b/httemplate/edit/elements/part_export/broadband_snmp.html new file mode 100644 index 000000000..4c0367c5a --- /dev/null +++ b/httemplate/edit/elements/part_export/broadband_snmp.html @@ -0,0 +1,101 @@ +<%doc> +</%doc> +<& head.html, %opt &> +<INPUT TYPE="hidden" NAME="options" VALUE="community,version,ip_addr_change_to_new,timeout"> +<& /elements/tr-select.html, + label => 'SNMP version', + field => 'version', + options => [ '', 'v1', 'v2c' ], + labels => { v1 => '1', v2c => '2c' }, + curr_value => $part_export->option('version') &> +<& /elements/tr-input-text.html, + label => 'Community', + field => 'community', + curr_value => $part_export->option('community'), +&> +<& /elements/tr-checkbox.html, + label => 'Send IP address changes to new address', + field => 'ip_addr_change_to_new', + value => 1, + curr_value => $part_export->option('ip_addr_change_to_new'), +&> +<& /elements/tr-input-text.html, + label => 'Timeout (seconds)', + field => 'timeout', + curr_value => $part_export->option('timeout'), +&> +</TABLE> +<script type="text/javascript"> +function open_select_mib(obj) { + nd(1); // if there's already one open, close it + var rownum = obj.rownum; + var curr_oid = obj.value || ''; + var url = '<%$fsurl%>/elements/select-mib-popup.html?' + + 'callback=receive_mib;' + + 'arg=' + rownum + + ';curr_value=' + curr_oid; + overlib( + OLiframeContent(url, 550, 450, '<% $popup_name %>', 0, 'auto'), + CAPTION, 'Select MIB object', STICKY, AUTOSTATUSCAP, + MIDX, 0, MIDY, 0, DRAGGABLE, CLOSECLICK, + BGCOLOR, '#333399', CGCOLOR, '#333399', + CLOSETEXT, 'Close' + ); +} +function receive_mib(obj, rownum) { + //console.log(JSON.stringify(obj)); + // we don't really need the numeric OID or any of the other properties + document.getElementById('oid'+rownum).value = obj.fullname; + document.getElementById('datatype'+rownum).value = obj.type; +} +</script> + +<table bgcolor="#cccccc" border=0 cellspacing=3> +<TR> + <TH>Action</TH> + <TH>Object</TH> + <TH>Type</TH> + <TH>Value</TH> +</TR> +<TR id="mytemplate"> + <TD> + <SELECT NAME="action"> +% foreach ('', qw(insert delete replace suspend unsuspend)) { + <OPTION VALUE="<%$_%>"><%$_%></OPTION> +% } + </SELECT> + </TD> + <TD> + <INPUT NAME="oid" ID="oid" SIZE="60" onclick="open_select_mib(this)"> + </TD> + <TD> + <INPUT TYPE="text" NAME="datatype" ID="datatype" READONLY=1> + </TD> + <TD> + <INPUT NAME="value" ID="value"> + </TD> +</TR> +<& /elements/auto-table.html, + template_row => 'mytemplate', + fieldorder => ['action', 'oid', 'datatype', 'value'], + data => \@data, +&> +<INPUT TYPE="hidden" NAME="multi_options" VALUE="action,oid,datatype,value"> +<& foot.html, %opt &> +<%init> +my %opt = @_; +my $part_export = $opt{part_export} || FS::part_export->new; + +my @actions = split("\n", $part_export->option('action')); +my @oids = split("\n", $part_export->option('oid')); +my @types = split("\n", $part_export->option('datatype')); +my @values = split("\n", $part_export->option('value')); + +my @data; +while (@actions or @oids or @values) { + my @thisrow = (shift(@actions), shift(@oids), shift(@types), shift(@values)); + push @data, \@thisrow if grep length($_), @thisrow; +} + +my $popup_name = 'popup-'.time."-$$-".rand() * 2**32; +</%init> diff --git a/httemplate/edit/elements/part_export/foot.html b/httemplate/edit/elements/part_export/foot.html new file mode 100644 index 000000000..9cb8073ce --- /dev/null +++ b/httemplate/edit/elements/part_export/foot.html @@ -0,0 +1,6 @@ +</TABLE> +<INPUT TYPE="hidden" NAME="nodomain" VALUE="<% $opt{export_info}{nodomain} %>"> +<INPUT TYPE="submit" VALUE="<% $opt{part_export}->exportnum ? 'Apply changes' : 'Add export' %>"> +<%init> +my %opt = @_; +</%init> diff --git a/httemplate/edit/elements/part_export/head.html b/httemplate/edit/elements/part_export/head.html new file mode 100644 index 000000000..cb0ab894a --- /dev/null +++ b/httemplate/edit/elements/part_export/head.html @@ -0,0 +1,19 @@ +% if ( $export_info->{no_machine} ) { +<INPUT TYPE="hidden" NAME="machine" VALUE=""> +<INPUT TYPE="hidden" NAME="svc_machine" VALUE="N"> +% } else { +% # clone this from edit/part_export.cgi if this case ever gets used +% } +<INPUT TYPE="hidden" NAME="exporttype" VALUE="<%$layer |h%>"> +<% ntable('cccccc', 2) %> +<TR> + <TD ALIGN="right" ><% emt('Description') %></TD> + <TD BGCOLOR="#ffffff" WIDTH="600"><% $notes %></TD> +</TR> +<%init> +my %opt = @_; +my $layer = $opt{layer}; +my $part_export = $opt{part_export}; +my $export_info = $opt{export_info}; +my $notes = $opt{notes} || $export_info->{notes}; +</%init> diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html new file mode 100644 index 000000000..1c5b45314 --- /dev/null +++ b/httemplate/edit/elements/part_svc_column.html @@ -0,0 +1,311 @@ +<%doc> +To be called from part_svc.cgi. +<& elements/part_svc_column.html, + 'svc_acct', + # options... + 'part_svc' => $part_svc, # the existing part_svc to edit + 'clone' => 0, # or a svcpart to clone from +&> + +</%doc> +<%once> +# the semantics of this could be better + +# all of these conditions are when NOT to allow that flag choice +# don't allow the 'inventory' flags (M, A) to be chosen for +# fields that aren't free-text +my $inv_sub = sub { $_[0]->{disable_inventory} || $_[0]->{type} ne 'text' }; +tie my %flag, 'Tie::IxHash', + '' => { 'desc' => 'No default', 'condition' => sub { 0 } }, + 'D' => { 'desc' => 'Default', + 'condition' => + sub { $_[0]->{disable_default } } + }, + 'F' => { 'desc' => 'Fixed (unchangeable)', + 'condition' => + sub { $_[0]->{disable_fixed} }, + }, + 'S' => { 'desc' => 'Selectable Choice', + 'condition' => + sub { $_[0]->{disable_select} }, + }, + 'M' => { 'desc' => 'Manual selection from inventory', + 'condition' => $inv_sub, + }, + 'A' => { 'desc' => 'Automatically fill in from inventory', + 'condition' => $inv_sub, + }, + 'H' => { 'desc' => 'Select from hardware class', + 'condition' => sub { $_[0]->{type} ne 'select-hardware' }, + }, + 'X' => { 'desc' => 'Excluded', + 'condition' => sub { 1 }, # obsolete + }, +; + +# the semantics of this could be much better +sub flag_condition { + my $f = shift; + not &{ $flag{$f}->{'condition'} }(@_); +} + +my %communigate_fields = ( + 'svc_acct' => { map { $_=>1 } + qw( file_quota file_maxnum file_maxsize + password_selfchange password_recover + ), + grep /^cgp_/, fields('svc_acct') + }, + 'svc_domain' => { map { $_=>1 } + qw( max_accounts trailer parent_svcnum ), + grep /^(cgp|acct_def)_/, fields('svc_domain') + }, +); +</%once> +<INPUT TYPE="hidden" NAME="svcdb" VALUE="<% $svcdb %>"> +<BR><BR> +<& /elements/table.html &> + <TR><TH COLSPAN=<% $columns %>>Exports</TH></TR> + <TR> +% # exports +% foreach my $part_export (@part_export) { + <TD> + <INPUT TYPE="checkbox" \ + NAME="exportnum<% $part_export->exportnum %>" \ + VALUE=1 \ + <% $has_export_svc{$part_export->exportnum} ? 'CHECKED' : '' %>> + <% $part_export->label_html %> + </TD> +% $count++; +% if ( $count % $columns == 0 ) { + </TR> + <TR> +% } +% } + </TR> +</TABLE><BR><BR> +For the selected table, you can give fields default or fixed (unchangeable) +values, or select an inventory class to manually or automatically fill in +that field. +<& /elements/table-grid.html, cellpadding => 4 &> + <TR> + <TH BGCOLOR="#cccccc">Field</TH> + <TH BGCOLOR="#cccccc">Label</TH> + <TH BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH> + </TR> +% $part_svc->set('svcpart' => $opt{'clone'}) if $opt{'clone'}; # for now +% my $i = 0; +% foreach my $field (@fields) { +% my $def = shift @defs; +% my $part_svc_column = $part_svc->part_svc_column($field); +% my $flag = $part_svc_column->columnflag; +% my $formatter = $def->{'format'} || sub { shift }; +% my $value = &{$formatter}($part_svc_column->columnvalue); + <TR CLASS="row<%$i%>"> + <TD ROWSPAN=2 CLASS="grid" ALIGN="right"> + <% $def->{'label'} || $field %> + </TD> + <TD ROWSPAN=2 CLASS="grid"> + <INPUT NAME="<% $svcdb %>__<% $field %>_label" + STYLE="text-align: right" + VALUE="<% $part_svc_column->columnlabel || $def->{'label'} |h %>"> + </TD> + + <TD ROWSPAN=1 CLASS="grid"> +% # flag selection +% if ( $def->{'type'} eq 'disabled' ) { +% $flag = ''; + No default +% } else { +% my $name = $svcdb.'__'.$field.'_flag'; + <SELECT NAME="<%$name%>" + ID="<%$name%>" + STYLE="width:100%" + onchange="flag_changed(this)"> +% foreach my $f (keys %flag) { +% if ( flag_condition($f, $def, $svcdb, $field) ) { + <OPTION VALUE="<%$f%>"<% $flag eq $f ? ' SELECTED' : ''%>> + <% $flag{$f}->{desc} %> + </OPTION> +% } +% } + </SELECT> +% } # if $def->{'type'} eq 'disabled' + </TD> + <TD CLASS="grid"> +% # value entry/selection +% my $name = $svcdb.'__'.$field; +% # These are all MANDATORY SELECT types. Regardless of the flag value, +% # there will never be a text input (either in svc_* or in part_svc) for +% # these fields. +% if ( $def->{'type'} eq 'checkbox' ) { + <& /elements/checkbox.html, + 'field' => $name, + 'curr_value' => $value, + 'value' => 'Y' &> +% +% } elsif ( $def->{'type'} eq 'select' ) { +% +% if ( $def->{'select_table'} ) { + <& /elements/select-table.html, + 'field' => $name, + 'id' => $name.'_select', + 'table' => $def->{'select_table'}, + 'name_col' => $def->{'select_label'}, + 'value_col' => $def->{'select_key'}, + 'order_by' => dbdef->table($def->{'select_table'})->primary_key, + 'multiple' => $def->{'multiple'}, + 'disable_empty' => 1, + 'curr_value' => $value, + # these can be switched between multiple and singular, + # so put the complete curr_value in an attribute + 'element_etc' => 'default="'.encode_entities($value).'"', + &> +% } else { +% my (@options, %labels); +% if ( $def->{'select_list'} ) { +% @options = @{ $def->{'select_list'} }; +% @labels{@options} = @options; +% } elsif ( $def->{'select_hash'} ) { +% if ( ref($def->{'select_hash'}) eq 'ARRAY' ) { +% tie my %hash, 'Tie::IxHash', @{ $def->{'select_hash'} }; +% $def->{'select_hash'} = \%hash; +% } +% @options = keys( %{ $def->{'select_hash'} } ); +% %labels = %{ $def->{'select_hash'} }; +% } + <& /elements/select.html, + 'field' => $name, + 'id' => $name.'_select', + 'options' => \@options, + 'labels' => \%labels, + 'multiple' => $def->{'multiple'}, + 'curr_value' => $value, + &> +% } +% } elsif ( $def->{'type'} =~ /select-(.*?).html/ ) { + <& '/elements/'.$def->{'type'}, + 'field' => $name, + 'id' => $name.'_select', + 'multiple' => $def->{'multiple'}, + 'curr_value' => $value, + &> +% } elsif ( $def->{'type'} eq 'communigate_pro-accessmodes' ) { + <& /elements/communigate_pro-accessmodes.html, + 'element_name_prefix' => $name.'_', + 'curr_value' => $value, + &> +% } elsif ( $def->{'type'} eq 'textarea' ) { +% # special cases + <TEXTAREA NAME="<%$name%>"><% $value |h %></TEXTAREA> +% } elsif ( $def->{'type'} eq 'disabled' ) { + <INPUT TYPE="hidden" NAME="<%$name%>" VALUE=""> +% } else { +% # the normal case: a text input, and a _select which is an inventory +% # or hardware class + <INPUT TYPE="text" + NAME="<%$name%>" + ID="<%$name%>" + VALUE="<%$value%>"> +% my $mode = 'inventory'; +% my $multiple = 1; +% if ( $def->{'type'} eq 'select-hardware' ) { +% $mode = 'hardware'; +% $multiple = 0; +% } + <& /elements/select-table.html, + 'field' => $name.'_classnum', + 'id' => $name.'_select', + 'table' => $mode.'_class', + 'name_col' => 'classname', + 'curr_value' => $value, + 'empty_label' => "Select $mode class", + 'multiple' => 0, + &> +% } + </TD> + </TR> + <TR CLASS="row<%$i%>"> + <TD COLSPAN=2 CLASS="def_info"> +% if ( $def->{def_info} ) { + (<% $def->{def_info} %>) + </TD> + </TR> +% } +% $i = 1-$i; +% } # foreach my $field +% +% # special case: svc_acct password edit ACL +% if ( $svcdb eq 'svc_acct' ) { +% push @fields, 'restrict_edit_password'; + <TR> + <TD COLSPAN=3 ALIGN="right"> + <% emt('Require "Provision" access right to edit password') %> + </TD> + <TD> + <INPUT TYPE="checkbox" NAME="restrict_edit_password" VALUE="Y" \ + <% $part_svc->restrict_edit_password ? 'CHECKED' : '' %>> + </TD> + </TR> +% } +</TABLE> +<& /elements/progress-init.html, + $svcdb, #form name + [ # form fields to send + qw(svc svcpart classnum selfservice_access disabled preserve exportnum), + @fields + ], + 'process/part_svc.cgi', # target + $p.'browse/part_svc.cgi', # redirect landing + $svcdb, #key +&> +% $svcpart = '' if $opt{clone}; +<BR> +<INPUT NAME="submit" + TYPE="button" + VALUE="<% emt($svcpart ? 'Apply changes' : 'Add service') %>" + onclick="fixup_submit('<%$svcdb%>')" +> +<%init> +my $svcdb = shift; +my %opt = @_; +my $columns = 3; +my $count = 0; +my $communigate = 0; +my $conf = FS::Conf->new; + +my $part_svc = $opt{'part_svc'} || FS::part_svc->new; + +my @part_export; +my $export_info = FS::part_export::export_info($svcdb); +foreach (keys %{ $export_info }) { + push @part_export, qsearch('part_export', { exporttype => $_ }); +} +$communigate = scalar(grep {$_->exporttype =~ /^communigate/} @part_export); + +my $svcpart = $opt{'clone'} || $part_svc->svcpart; +my %has_export_svc; +if ( $svcpart ) { + foreach (qsearch('export_svc', { svcpart => $svcpart })) { + $has_export_svc{$_->exportnum} = 1; + } +} + +my @fields; +if ( defined( dbdef->table($svcdb) ) ) { # when is it ever not defined? + @fields = grep { + $_ ne 'svcnum' + and ( $communigate || ! $communigate_fields{$svcdb}->{$_} ) + and ( !FS::part_svc->svc_table_fields($svcdb)->{$_}->{disable_part_svc_column} + || $part_svc->part_svc_column($_)->columnflag ) + } fields($svcdb); +} +if ( $svcdb eq 'svc_acct' + or ( $svcdb eq 'svc_broadband' and $conf->exists('svc_broadband-radius') ) + ) +{ + push @fields, 'usergroup'; +} + +my @defs = map { FS::part_svc->svc_table_fields($svcdb)->{$_} } @fields; +</%init> diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html index 0d9d36c07..d46d1cb42 100644 --- a/httemplate/edit/elements/svc_Common.html +++ b/httemplate/edit/elements/svc_Common.html @@ -88,30 +88,13 @@ } elsif ( $flag eq 'A' ) { $f->{'type'} = 'hidden'; } elsif ( $flag eq 'M' ) { + $f->{'type'} = 'select-inventory_item'; $f->{'empty_label'} = 'Select inventory item'; - $f->{'type'} = 'select-table'; - $f->{'table'} = 'inventory_item'; - $f->{'name_col'} = 'item'; - $f->{'value_col'} = 'item'; - $f->{'agent_virt'} = 1; - $f->{'agent_null'} = 1; - $f->{'hashref'} = { - 'classnum'=>$columndef->columnvalue, - #'svcnum' => '', - }; - $f->{'extra_sql'} = 'AND ( svcnum IS NULL '; - $f->{'extra_sql'} .= ' OR svcnum = '. $object->svcnum - if $object->svcnum; - $f->{'extra_sql'} .= ' ) '; + $f->{'extra_sql'} = 'WHERE ( svcnum IS NULL ' . + ($object->svcnum && ' OR svcnum = '.$object->svcnum) . + ')'; + $f->{'classnum'} = $columndef->columnvalue; $f->{'disable_empty'} = $object->svcnum ? 1 : 0; - if ( $f->{'field'} eq 'mac_addr' ) { - $f->{'compare_sub'} = sub { - my($a, $b) = @_; - $a =~ s/[-: ]//g; - $b =~ s/[-: ]//g; - lc($a) eq lc($b); - }; - } } elsif ( $flag eq 'H' ) { $f->{'type'} = 'select-hardware_type'; $f->{'hashref'} = { diff --git a/httemplate/edit/ftp_target.html b/httemplate/edit/ftp_target.html deleted file mode 100755 index aebf9aaed..000000000 --- a/httemplate/edit/ftp_target.html +++ /dev/null @@ -1,46 +0,0 @@ -<& elements/edit.html, - 'post_url' => popurl(1).'process/ftp_target.html', - 'name' => 'FTP target', - 'table' => 'ftp_target', - 'viewall_url' => "${p}browse/ftp_target.html", - 'labels' => { targetnum => 'Target', - hostname => 'Server', - username => 'Username', - password => 'Password', - path => 'Directory', - port => 'Port', - secure => 'Use SFTP', - handling => 'Special handling', - }, - 'fields' => [ - { field => 'hostname', size => 40 }, - { field => 'port', size => 8 }, - { field => 'secure', type => 'checkbox', value => 'Y' }, - 'username', - 'password', - { field => 'path', size => 40 }, - { field => 'handling', - type => 'select', - options => [ FS::ftp_target->handling_types ], - }, - ], - 'menubar' => \@menubar, - 'edit_callback' => $edit_callback, -&> -<%init> - -my $curuser = $FS::CurrentUser::CurrentUser; - -die "access denied" - unless $curuser->access_right('Configuration'); - -my @menubar = ('View all FTP targets' => $p.'browse/ftp_target.html'); -my $edit_callback = sub { - my ($cgi, $object) = @_; - if ( $object->targetnum ) { - push @menubar, 'Delete this target', - $p.'misc/delete-ftp_target.html?'.$object->targetnum; - } -}; - -</%init> diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi index 0407ee77b..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,10 +86,18 @@ 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; + # create 'config_element' to generate the whole layer with a Mason component + if ( my $include = $exports->{$layer}{config_element} ) { + # might need to adjust the scope of this at some point + return $m->scomp($include, + part_export => $part_export, + layer => $layer, + export_info => $exports->{$layer} + ); + } my $html = qq!<INPUT TYPE="hidden" NAME="exporttype" VALUE="$layer">!. ntable("#cccccc",2); @@ -78,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' ) @@ -88,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 f3ad8f52d..89f16158f 100755 --- a/httemplate/edit/part_pkg.cgi +++ b/httemplate/edit/part_pkg.cgi @@ -1,294 +1,330 @@ -<% include( 'elements/edit.html', - 'post_url' => popurl(1).'process/part_pkg.cgi', - 'name' => "Package definition", - 'table' => 'part_pkg', - - 'agent_virt' => 1, - 'agent_null_right' => $edit_global, - 'agent_clone_extra_sql' => $agent_clone_extra_sql, - #'viewall_dir' => 'browse', - 'viewall_url' => $p.'browse/part_pkg.cgi', - 'html_init' => include('/elements/init_overlib.html'). - $javascript, - 'html_bottom' => $html_bottom, - 'body_etc' => - 'onLoad="agent_changed(document.edit_topform.agentnum)"', - - 'begin_callback' => $begin_callback, - 'end_callback' => $end_callback, - 'new_hashref_callback' => $new_hashref_callback, - 'new_object_callback' => $new_object_callback, - 'new_callback' => $new_callback, - 'clone_callback' => $clone_callback, - 'edit_callback' => $edit_callback, - 'error_callback' => $error_callback, - 'field_callback' => $field_callback, - - 'onsubmit' => 'confirm_submit', - - 'labels' => { - 'pkgpart' => 'Package Definition', - 'pkg' => 'Package (customer-visible)', - 'comment' => 'Comment (customer-hidden)', - 'classnum' => 'Package class', - 'addon_classnum' => 'Restrict additional orders to package class', - 'promo_code' => 'Promotional code', - 'freq' => 'Recurring fee frequency', - 'setuptax' => 'Setup fee tax exempt', - 'recurtax' => 'Recurring fee tax exempt', - 'taxclass' => 'Tax class', - 'taxproduct_select'=> 'Tax products', - 'plan' => 'Price plan', - 'disabled' => 'Disable new orders', - 'disable_line_item_date_ranges' => 'Disable line item date ranges', - 'setup_cost' => 'Setup cost', - 'recur_cost' => 'Recur cost', - 'pay_weight' => 'Payment weight', - 'credit_weight' => 'Credit weight', - 'agentnum' => 'Agent', - 'setup_fee' => 'Setup fee', - 'setup_show_zero' => 'Show zero setup', - 'recur_fee' => 'Recurring fee', - 'recur_show_zero' => 'Show zero recurring', - 'discountnum' => 'Offer discounts for longer terms', - 'bill_dst_pkgpart' => 'Include line item(s) from package', - 'svc_dst_pkgpart' => 'Include services of package', - 'report_option' => 'Report classes', - 'fcc_ds0s' => 'Voice-grade equivalents', - 'fcc_voip_class' => 'Category', - }, - - 'fields' => [ - { field=>'clone', type=>'hidden', - curr_value_callback => - sub { shift->param('clone') }, - }, - { field=>'pkgnum', type=>'hidden', - curr_value_callback => - sub { shift->param('pkgnum') }, - }, - - { field=>'custom', type=>'hidden' }, - { field=>'family_pkgpart', type=>'hidden' }, - { field=>'successor', type=>'hidden' }, - - { type => 'columnstart' }, - - { field => 'pkg', - type => 'text', - size => 40, #32 - maxlength => 50, - }, - {field=>'comment', type=>'text', size=>40 }, #32 - { field => 'agentnum', - type => 'select-agent', - disable_empty => ! $acl_edit_global, - empty_label => '(global)', - onchange => 'agent_changed', - }, - {field=>'classnum', type=>'select-pkg_class' }, - ( $conf->exists('pkg-addon_classnum') - ? ( { field=>'addon_classnum', - type =>'select-pkg_class', - } - ) - : () - ), - {field=>'disabled', type=>$disabled_type, value=>'Y'}, - {field=>'disable_line_item_date_ranges', type=>$disabled_type, value=>'Y'}, - - { type => 'tablebreak-tr-title', - value => 'Pricing', #better name? - }, - { field => 'plan', - type => 'selectlayers-select', - options => [ keys %plan_labels ], - labels => \%plan_labels, - onchange => 'aux_planchanged(what);', - }, - { field => 'setup_fee', - type => 'money', - onchange => 'setup_changed', - }, - { field => 'setup_show_zero', - type => 'checkbox', - value => 'Y', - disabled => sub { $setup_show_zero_disabled }, - }, - { field => 'freq', - type => 'part_pkg_freq', - onchange => 'freq_changed', - }, - { field => 'recur_fee', - type => 'money', - disabled => sub { $recur_disabled }, - onchange => 'recur_changed', - }, - - { field => 'recur_show_zero', - type => 'checkbox', - value => 'Y', - disabled => sub { $recur_show_zero_disabled }, - }, - - #price plan - #setup fee - #recurring frequency - #recurring fee (auto-disable) - - { type => 'columnnext' }, - - {type=>'justtitle', value=>'Taxation' }, - {field=>'setuptax', type=>'checkbox', value=>'Y'}, - {field=>'recurtax', type=>'checkbox', value=>'Y'}, - {field=>'taxclass', type=>'select-taxclass' }, - { field => 'taxproductnums', - type => 'hidden', - value => join(',', @taxproductnums), - }, - { field => 'taxproduct_select', - type => 'selectlayers', - options => [ '(default)', @taxproductnums ], - curr_value => '(default)', - labels => { ( '(default)' => '(default)' ), - map {($_=>$usage_class{$_})} - @taxproductnums - }, - layer_fields => \%taxproduct_fields, - layer_values_callback => $taxproduct_values, - layers_only => !$taxproducts, - cell_style => ( !$taxproducts - ? 'display:none' - : '' - ), - }, - - { type => 'tablebreak-tr-title', - value => 'Promotions', #better name? - }, - { field=>'promo_code', type=>'text', size=>15 }, - - { type => 'tablebreak-tr-title', - value => 'Cost tracking', #better name? - }, - { field=>'setup_cost', type=>'money', }, - { field=>'recur_cost', type=>'money', }, - - { type => 'columnnext' }, - - { field => 'agent_type', - type => 'select-agent_types', - disabled => ! $acl_edit_global, - curr_value_callback => sub { - my($cgi, $object, $field) = @_; - #in the other callbacks..? hmm. - \@agent_type; - }, - }, - - { type => 'tablebreak-tr-title', - value => 'Line-item revenue recogition', #better name? - }, - { field=>'pay_weight', type=>'text', size=>6 }, - { field=>'credit_weight', type=>'text', size=>6 }, - - ( $conf->exists('cust_pkg-show_fcc_voice_grade_equivalent') - ? ( - { type => 'tablebreak-tr-title', - value => 'FCC Form 477 information', - }, - { field=>'fcc_voip_class', - type=>'select-voip_class', - }, - { field=>'fcc_ds0s', type=>'text', size=>6 }, - ) - : () - ), - - - { type => 'columnend' }, - - { 'type' => $report_option ? 'tablebreak-tr-title' - : 'hidden', - 'value' => 'Optional report classes', - 'field' => 'census_title', - }, - { 'field' => 'report_option', - 'type' => $report_option ? 'select-table' - : 'hidden', - 'table' => 'part_pkg_report_option', - 'name_col' => 'name', - 'hashref' => { 'disabled' => '' }, - 'multiple' => 1, - }, - - { 'type' => 'tablebreak-tr-title', - 'value' => 'Term discounts', - }, - { 'field' => 'discountnum', - 'type' => 'select-table', - 'table' => 'discount', - 'name_col' => 'name', - 'hashref' => { %$discountnum_hashref }, - #'extra_sql' => 'AND (months IS NOT NULL OR months != 0)', - 'empty_label'=> 'Select discount', - 'm2_label' => 'Offer discounts for longer terms', - 'm2m_method' => 'part_pkg_discount', - 'm2m_dstcol' => 'discountnum', - 'm2_error_callback' => $discount_error_callback, - }, - - { 'type' => 'tablebreak-tr-title', - 'value' => 'Pricing add-ons', - 'colspan' => 4, - }, - { 'field' => 'bill_dst_pkgpart', - 'type' => 'select-part_pkg', - 'extra_sql' => sub { $pkgpart - ? "AND pkgpart != $pkgpart" - : '' - }, - 'm2_label' => 'Include line item(s) from package', - 'm2m_method' => 'bill_part_pkg_link', - 'm2m_dstcol' => 'dst_pkgpart', - 'm2_error_callback' => - &{$m2_error_callback_maker}('bill'), - 'm2_fields' => [ { 'field' => 'hidden', - 'type' => 'checkbox', - 'value' => 'Y', - 'curr_value' => '', - 'label' => 'Bundle', - }, - ], - }, - - { type => 'tablebreak-tr-title', - value => 'Services', - }, - { type => 'pkg_svc', }, - - { 'field' => 'svc_dst_pkgpart', - 'label' => 'Also include services from package: ', - 'type' => 'select-part_pkg', - 'extra_sql' => sub { $pkgpart - ? "AND pkgpart != $pkgpart" - : '' - }, - 'm2_label' => 'Include services of package: ', - 'm2m_method' => 'svc_part_pkg_link', - 'm2m_dstcol' => 'dst_pkgpart', - 'm2_error_callback' => - &{$m2_error_callback_maker}('svc'), - }, - - { type => 'tablebreak-tr-title', - value => 'Price plan options', - }, - - ], - - ) -%> +<& elements/edit.html, + 'post_url' => popurl(1).'process/part_pkg.cgi', + 'name' => "Package definition", + 'table' => 'part_pkg', + + 'agent_virt' => 1, + 'agent_null_right' => $edit_global, + 'agent_clone_extra_sql' => $agent_clone_extra_sql, + #'viewall_dir' => 'browse', + 'viewall_url' => $p.'browse/part_pkg.cgi', + 'html_init' => include('/elements/init_overlib.html'). + $javascript, + 'html_bottom' => $html_bottom, + 'body_etc' => + 'onLoad="agent_changed(document.edit_topform.agentnum); + aux_planchanged(document.edit_topform.plan)"', + + 'begin_callback' => $begin_callback, + 'end_callback' => $end_callback, + 'new_hashref_callback' => $new_hashref_callback, + 'new_object_callback' => $new_object_callback, + 'new_callback' => $new_callback, + 'clone_callback' => $clone_callback, + 'edit_callback' => $edit_callback, + 'error_callback' => $error_callback, + 'field_callback' => $field_callback, + + 'onsubmit' => 'confirm_submit', + + 'labels' => { + 'pkgpart' => 'Package Definition', + 'pkg' => 'Package', + %locale_field_labels, + 'comment' => 'Comment (customer-hidden)', + 'classnum' => 'Package class', + 'addon_classnum' => 'Restrict additional orders to package class', + 'promo_code' => 'Promotional code', + 'freq' => 'Recurring fee frequency', + 'setuptax' => 'Setup fee tax exempt', + 'recurtax' => 'Recurring fee tax exempt', + 'taxclass' => 'Tax class', + 'taxproduct_select'=> 'Tax products', + 'plan' => 'Price plan', + 'disabled' => 'Disable new orders', + 'disable_line_item_date_ranges' => 'Disable line item date ranges', + 'setup_cost' => 'Setup cost', + 'recur_cost' => 'Recur cost', + 'pay_weight' => 'Payment weight', + 'credit_weight' => 'Credit weight', + 'agentnum' => 'Agent', + 'setup_fee' => 'Setup fee', + 'setup_show_zero' => 'Show zero setup', + 'recur_fee' => 'Recurring fee', + 'recur_show_zero' => 'Show zero recurring', + ( map { ( "setup_fee_$_" => "Setup fee $_", + "recur_fee_$_" => "Recurring fee $_", + ); + } + $conf->config('currencies') + ), + 'discountnum' => 'Offer discounts for longer terms', + 'bill_dst_pkgpart' => 'Include line item(s) from package', + 'svc_dst_pkgpart' => 'Include services of package', + 'supp_dst_pkgpart' => 'Include complete package', + 'report_option' => 'Report classes', + 'fcc_ds0s' => 'Voice-grade equivalents', + 'fcc_voip_class' => 'Category', + }, + + 'fields' => [ + { field=>'clone', type=>'hidden', + curr_value_callback => + sub { shift->param('clone') }, + }, + { field=>'pkgnum', type=>'hidden', + curr_value_callback => + sub { shift->param('pkgnum') }, + }, + + { field=>'custom', type=>'hidden' }, + { field=>'family_pkgpart', type=>'hidden' }, + { field=>'successor', type=>'hidden' }, + + { type => 'columnstart' }, + + { field => 'pkg', + type => 'text', + size => 40, #32 + maxlength => 50, + }, + #@locale_fields, + {field=>'comment', type=>'text', size=>40 }, #32 + { field => 'agentnum', + type => 'select-agent', + disable_empty => ! $acl_edit_global, + empty_label => '(global)', + onchange => 'agent_changed', + }, + {field=>'classnum', type=>'select-pkg_class' }, + ( $conf->exists('pkg-addon_classnum') + ? ( { field=>'addon_classnum', + type =>'select-pkg_class', + } + ) + : () + ), + {field=>'disabled', type=>$disabled_type, value=>'Y'}, + {field=>'disable_line_item_date_ranges', type=>$disabled_type, value=>'Y'}, + + { type => 'tablebreak-tr-title', + value => 'Pricing', #better name? + }, + { field => 'plan', + type => 'selectlayers-select', + options => [ keys %plan_labels ], + labels => \%plan_labels, + onchange => 'aux_planchanged(what);', + }, + { field => 'setup_fee', + type => 'money', + onchange => 'setup_changed', + }, + { field => 'setup_show_zero', + type => 'checkbox', + value => 'Y', + disabled => sub { $setup_show_zero_disabled }, + }, + ( map { +{ field => "setup_fee_$_", + type => 'text', + prefix=> currency_symbol($_, SYM_HTML), + size => 8, + } + } + sort $conf->config('currencies') + ), + { field => 'freq', + type => 'part_pkg_freq', + onchange => 'freq_changed', + }, + { field => 'recur_fee', + type => 'money', + disabled => sub { $recur_disabled }, + onchange => 'recur_changed', + }, + { field => 'recur_show_zero', + type => 'checkbox', + value => 'Y', + disabled => sub { $recur_show_zero_disabled }, + }, + ( map { +{ field => "recur_fee_$_", + type => 'text', + prefix=> currency_symbol($_, SYM_HTML), + size => 8, + } + } + sort $conf->config('currencies') + ), + + #price plan + #setup fee + #recurring frequency + #recurring fee (auto-disable) + + { type => 'columnnext' }, + + {type=>'justtitle', value=>'Taxation' }, + {field=>'setuptax', type=>'checkbox', value=>'Y'}, + {field=>'recurtax', type=>'checkbox', value=>'Y'}, + {field=>'taxclass', type=>'select-taxclass' }, + { field => 'taxproductnums', + type => 'hidden', + value => join(',', @taxproductnums), + }, + { field => 'taxproduct_select', + type => 'selectlayers', + options => [ '(default)', @taxproductnums ], + curr_value => '(default)', + labels => { ( '(default)' => '(default)' ), + map {($_=>$usage_class{$_})} + @taxproductnums + }, + layer_fields => \%taxproduct_fields, + layer_values_callback => $taxproduct_values, + layers_only => !$taxproducts, + cell_style => ( !$taxproducts + ? 'display:none' + : '' + ), + }, + + { type => 'tablebreak-tr-title', + value => 'Promotions', #better name? + }, + { field=>'promo_code', type=>'text', size=>15 }, + + { type => 'tablebreak-tr-title', + value => 'Cost tracking', #better name? + }, + { field=>'setup_cost', type=>'money', }, + { field=>'recur_cost', type=>'money', }, + + { type => 'columnnext' }, + + { field => 'agent_type', + type => 'select-agent_types', + disabled => ! $acl_edit_global, + curr_value_callback => sub { + my($cgi, $object, $field) = @_; + #in the other callbacks..? hmm. + \@agent_type; + }, + }, + + { type => 'tablebreak-tr-title', + value => 'Line-item revenue recogition', #better name? + }, + { field=>'pay_weight', type=>'text', size=>6 }, + { field=>'credit_weight', type=>'text', size=>6 }, + + ( $conf->exists('cust_pkg-show_fcc_voice_grade_equivalent') + ? ( + { type => 'tablebreak-tr-title', + value => 'FCC Form 477 information', + }, + { field=>'fcc_voip_class', + type=>'select-voip_class', + }, + { field=>'fcc_ds0s', type=>'text', size=>6 }, + ) + : () + ), + + + { type => 'columnend' }, + + { 'type' => $report_option ? 'tablebreak-tr-title' + : 'hidden', + 'value' => 'Optional report classes', + 'field' => 'census_title', + }, + { 'field' => 'report_option', + 'type' => $report_option ? 'select-table' + : 'hidden', + 'table' => 'part_pkg_report_option', + 'name_col' => 'name', + 'hashref' => { 'disabled' => '' }, + 'multiple' => 1, + }, + + { 'type' => 'tablebreak-tr-title', + 'value' => 'Term discounts', + }, + { 'field' => 'discountnum', + 'type' => 'select-table', + 'table' => 'discount', + 'name_col' => 'name', + 'hashref' => { %$discountnum_hashref }, + #'extra_sql' => 'AND (months IS NOT NULL OR months != 0)', + 'empty_label'=> 'Select discount', + 'm2_label' => 'Offer discounts for longer terms', + 'm2m_method' => 'part_pkg_discount', + 'm2m_dstcol' => 'discountnum', + 'm2_error_callback' => $discount_error_callback, + }, + + { 'type' => 'tablebreak-tr-title', + 'value' => 'Supplemental packages', + 'colspan' => '4', + }, + { 'field' => 'supp_dst_pkgpart', + 'type' => 'select-part_pkg', + 'm2_label' => 'Include complete package', + 'm2m_method' => 'supp_part_pkg_link', + 'm2m_dstcol' => 'dst_pkgpart', + 'm2_error_callback' => + &{$m2_error_callback_maker}('supp'), + }, + + { 'type' => 'tablebreak-tr-title', + 'value' => 'Pricing add-ons', + 'colspan' => 4, + }, + { 'field' => 'bill_dst_pkgpart', + 'type' => 'select-part_pkg', + 'extra_sql' => sub { $pkgpart + ? "AND pkgpart != $pkgpart" + : '' + }, + 'm2_label' => 'Include line item(s) from package', + 'm2m_method' => 'bill_part_pkg_link', + 'm2m_dstcol' => 'dst_pkgpart', + 'm2_error_callback' => + &{$m2_error_callback_maker}('bill'), + 'm2_fields' => [ { 'field' => 'hidden', + 'type' => 'checkbox', + 'value' => 'Y', + 'curr_value' => '', + 'label' => 'Bundle', + }, + ], + }, + + { type => 'tablebreak-tr-title', + value => 'Services', + }, + { type => 'pkg_svc', }, + + { 'field' => 'svc_dst_pkgpart', + 'label' => 'Also include services from package: ', + 'type' => 'select-part_pkg', + 'extra_sql' => sub { $pkgpart + ? "AND pkgpart != $pkgpart" + : '' + }, + 'm2_label' => 'Include services of package: ', + 'm2m_method' => 'svc_part_pkg_link', + 'm2m_dstcol' => 'dst_pkgpart', + 'm2_error_callback' => + &{$m2_error_callback_maker}('svc'), + }, + + { type => 'tablebreak-tr-title', + value => 'Price plan options', + }, + + ], +&> <%init> my $curuser = $FS::CurrentUser::CurrentUser; @@ -323,6 +359,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; @@ -354,6 +406,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 ) = @_; @@ -392,8 +480,26 @@ my $error_callback = sub { $object->set($_ => scalar($cgi->param($_)) ) foreach (qw( setup_fee recur_fee disable_line_item_date_ranges )); + foreach my $currency ( $conf->config('currencies') ) { + my %part_pkg_currency = $object->part_pkg_currency_options($currency); + foreach (qw( setup_fee recur_fee )) { + my $param = $_.'_'.$currency; + $object->set( $param, $cgi->param($param) ); + } + } + $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' }; }; @@ -409,20 +515,8 @@ my $new_object_callback = sub { }; -my $edit_callback = sub { - my( $cgi, $object, $fields, $opt ) = @_; - - $setup_show_zero_disabled = ($object->option('setup_fee') > 0) ? 1 : 0; - - $recur_disabled = $object->freq ? 0 : 1; - - $recur_show_zero_disabled = - $object->freq - ? $object->option('recur_fee') > 0 ? 1 : 0 - : 1; - - (@agent_type) = - map {$_->typenum} qsearch('type_pkgs', { 'pkgpart' => $object->pkgpart } ); +sub set_report_option { + my($cgi, $object, $fields ) = @_; #, $opt my @report_option = (); foreach ($object->options) { @@ -445,13 +539,52 @@ my $edit_callback = sub { $field->{value} = join(',', @report_option); } +} + +my $edit_callback = sub { + my( $cgi, $object, $fields, $opt ) = @_; + + $setup_show_zero_disabled = ($object->option('setup_fee') > 0) ? 1 : 0; + + $recur_disabled = $object->freq ? 0 : 1; + + $recur_show_zero_disabled = + $object->freq + ? $object->option('recur_fee') > 0 ? 1 : 0 + : 1; + + (@agent_type) = + map {$_->typenum} qsearch('type_pkgs', { 'pkgpart' => $object->pkgpart } ); + + set_report_option( $cgi, $object, $fields); + %options = $object->options; $object->set($_ => $object->option($_, 1)) foreach (qw( setup_fee recur_fee disable_line_item_date_ranges )); + foreach my $currency ( $conf->config('currencies') ) { + my %part_pkg_currency = $object->part_pkg_currency_options($currency); + $object->set( $_.'_'.$currency, $part_pkg_currency{$_} ) + foreach keys %part_pkg_currency; + } + $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 { @@ -466,6 +599,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 { @@ -484,19 +619,37 @@ my $clone_callback = sub { $object->disabled('Y'); - } else { #not when cloning... + } else { #when explicitly cloning, not customizing (@agent_type) = map {$_->typenum} qsearch('type_pkgs',{ 'pkgpart' => $object->pkgpart } ); } + set_report_option( $cgi, $object, $fields); + %options = $object->options; $object->set($_ => $options{$_}) foreach (qw( setup_fee recur_fee disable_line_item_date_ranges )); + foreach my $currency ( $conf->config('currencies') ) { + my %part_pkg_currency = $object->part_pkg_currency_options($currency); + $object->set( $_.'_'.$currency, $part_pkg_currency{$_} ) + foreach keys %part_pkg_currency; + } + $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 { @@ -603,16 +756,28 @@ my $javascript = <<'END'; function aux_planchanged(what) { //? - alert('called!'); var plan = what.options[what.selectedIndex].value; - var table = document.getElementById('TableNumber7') // XXX NOT ROBUST + var term_table = document.getElementById('TableNumber7') // XXX NOT ROBUST if ( plan == 'flat' || plan == 'prorate' || plan == 'subscription' ) { - //table.disabled = false; - table.style.visibility = ''; + //term_table.disabled = false; + term_table.style.visibility = ''; } else { - //table.disabled = true; - table.style.visibility = 'hidden'; + //term_table.disabled = true; + term_table.style.visibility = 'hidden'; + } + + var currency_regex = /^(setup|recur)_fee_[A-Z]{3}$/; + + var form = what.form + for ( var i=0; i < form.length; i++ ) { + if ( currency_regex.test(form[i].name) ) { + if ( plan == 'currency_fixed' ) { + form[i].disabled = false; + } else { + form[i].disabled = true; + } + } } } @@ -622,23 +787,23 @@ END my $warning = 'Changing the setup or recurring fee will create a new package definition. '. 'Continue?'; - + +$javascript .= "function confirm_submit(f) {"; if ( $conf->exists('part_pkg-lineage') ) { $javascript .= " - function confirm_submit(f) { - - var fields = Array('setup_fee','recur_fee'); - for(var i=0; i < fields.length; i++) { - if ( f[fields[i]].value != f[fields[i]].defaultValue ) { - return confirm('$warning'); - } - } - return true; + + var fields = Array('setup_fee','recur_fee'); + for(var i=0; i < fields.length; i++) { + if ( f[fields[i]].value != f[fields[i]].defaultValue ) { + return confirm('$warning'); + } } "; } - -$javascript .= '</SCRIPT>'; +$javascript .= " + return true; +} +</SCRIPT>"; tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() }; @@ -786,6 +951,7 @@ my $html_bottom = sub { labels => \%plan_labels, curr_value => $object->plan, layer_callback => $layer_callback, + onchange => 'aux_planchanged(what);', ); my $return = diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi index 007c24629..2ec024269 100755 --- a/httemplate/edit/part_svc.cgi +++ b/httemplate/edit/part_svc.cgi @@ -1,11 +1,122 @@ -<& /elements/header.html, "$action Service Definition", - menubar('View all service definitions' => "${p}browse/part_svc.cgi"), +<& /elements/header.html, "$action Service Definition" &> +<& /elements/menubar.html, + 'View all service definitions' => "${p}browse/part_svc.cgi" #" onLoad=\"visualize()\"" &> <& /elements/init_overlib.html &> -<BR> +<BR><BR> + +<STYLE TYPE="text/css"> +.disabled { + background-color: #dddddd; +} +.hidden { + display: none; +} +.enabled { + background-color: #ffffff; +} +.row0 TD { + background-color: #eeeeee; +} +.row1 TD { + background-color: #ffffff; +} +.def_info { + text-align: center; + padding: 0px; + border-top: none; + font-size: smaller; + font-style: italic; +} +</STYLE> +<SCRIPT TYPE="text/javascript"> +function fixup_submit(layer) { + document.forms[layer].submit.disabled = true; + fixup(document.forms[layer]); + window[layer+'process'].call(); +} + +function flag_changed(obj) { + var newflag = obj.value; + var a = obj.name.match(/(.*)__(.*)_flag/); + var layer = a[1]; + var field = a[2]; + var input = document.getElementById(layer + '__' + field); + // for fields that have both 'input' and 'select', 'select' is 'select from + // inventory class'. + var select = document.getElementById(layer + '__' + field + '_select'); + if (newflag == "" || newflag == "X") { // disable + if ( input ) { + input.disabled = true; + input.className = 'disabled'; + } + if ( select ) { + select.disabled = true; + select.className = 'hidden'; + } + } else if ( newflag == 'D' || newflag == 'F' || newflag == 'S' ) { + if ( input ) { + // enable text box, disable inventory select + input.disabled = false; + input.className = 'enabled'; + if ( select ) { + select.disabled = false; + select.className = 'hidden'; + } + } else if ( select ) { + // enable select + select.disabled = false; + select.className = 'enabled'; + if ( newflag == 'S' || select.getAttribute('should_be_multiple') ) { + select.multiple = true; + var defaults = select.getAttribute('default'); + if ( defaults ) { + defaults = defaults.split(','); + for (var i = 0; i < defaults.length; i++) { + for (j = 0; j < select.options.length; j++ ) { + if ( defaults[i] == select.options[j].value ) { + select.options[j].selected = true; + } + } + } + } + } else { + select.multiple = false; + } + } + } else if ( newflag == 'M' || newflag == 'A' || newflag == 'H' ) { + // these all require a class selection + if ( select ) { + select.disabled = false; + select.className = 'enabled'; + if ( input ) { + input.disabled = false; + input.className = 'hidden'; + } + } + } +} + +window.onload = function() { + var selects = document.getElementsByTagName('SELECT'); + for(i = 0; i < selects.length; i++) { + var obj = selects[i]; + if ( obj.multiple ) { + obj.setAttribute('should_be_multiple', true); + } + } + for(i = 0; i < selects.length; i++) { + var obj = selects[i]; + if ( obj.name.match(/_flag$/) ) { + flag_changed(obj); + } + } +}; + +</SCRIPT> <FORM NAME="dummy"> @@ -53,386 +164,6 @@ <BR> -% my %vfields; -% #code duplication w/ edit/part_svc.cgi, should move this hash to part_svc.pm -% # and generalize the subs -% # condition sub is tested to see whether to disable display of this choice -% # params: ( $def, $layer, $field ) (see SUB below) -% my $inv_sub = sub { -% $_[0]->{disable_inventory} -% || $_[0]->{'type'} ne 'text' -% }; -% tie my %flag, 'Tie::IxHash', -% '' => { 'desc' => 'No default', }, -% 'D' => { 'desc' => 'Default', -% 'condition' => -% sub { $_[0]->{disable_default} }, -% }, -% 'F' => { 'desc' => 'Fixed (unchangeable)', -% 'condition' => -% sub { $_[0]->{disable_fixed} }, -% }, -% 'S' => { 'desc' => 'Selectable Choice', -% 'condition' => -% sub { !ref($_[0]) || $_[0]->{disable_select} }, -% }, -% 'M' => { 'desc' => 'Manual selection from inventory', -% 'condition' => $inv_sub, -% }, -% 'A' => { 'desc' => 'Automatically fill in from inventory', -% 'condition' => $inv_sub, -% }, -% 'H' => { 'desc' => 'Select from hardware class', -% 'condition' => sub { $_[0]->{type} ne 'select-hardware' }, -% }, -% 'X' => { 'desc' => 'Excluded', -% 'condition' => -% sub { ! $vfields{$_[1]}->{$_[2]} }, -% -% }, -% ; -% -% my @dbs = $hashref->{svcdb} -% ? ( $hashref->{svcdb} ) -% : FS::part_svc->svc_tables(); -% -% my $help = ''; -% unless ( $hashref->{svcpart} ) { -% $help = ' '. -% include('/elements/popup_link.html', -% 'action' => $p.'docs/part_svc-table.html', -% 'label' => 'help', -% 'actionlabel' => 'Service table help', -% 'width' => 763, -% #'height' => 400, -% ); -% } -% -% tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs; -% my $widget = new HTML::Widgets::SelectLayers( -% #'selected_layer' => $p_svcdb, -% 'selected_layer' => $hashref->{svcdb} || 'svc_acct', -% 'options' => \%svcdb, -% 'form_name' => 'dummy', -% #'form_action' => 'process/part_svc.cgi', -% 'form_action' => 'part_svc.cgi', #self -% 'form_elements' => [qw( svc svcpart classnum selfservice_access -% disabled preserve -% )], -% 'html_between' => $help, -% 'layer_callback' => sub { -% my $layer = shift; -% -% my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!; -% -% #$html .= $svcdb_info; -% -% my $columns = 3; -% my $count = 0; -% my $communigate = 0; -% my @part_export = -% map { qsearch( 'part_export', {exporttype => $_ } ) } -% keys %{FS::part_export::export_info($layer)}; -% $html .= '<BR><BR>'. include('/elements/table.html') . -% "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>"; -% foreach my $part_export ( @part_export ) { -% $communigate++ if $part_export->exporttype =~ /^communigate/; -% $html .= '<TD><INPUT TYPE="checkbox"'. -% ' NAME="exportnum'. $part_export->exportnum. '" VALUE="1" '; -% $html .= 'CHECKED' -% if ( $clone || $part_svc->svcpart ) #null svcpart search causing error -% && qsearchs( 'export_svc', { -% exportnum => $part_export->exportnum, -% svcpart => $clone || $part_svc->svcpart }); -% $html .= '>'. $part_export->label_html. '</TD>'; -% $count++; -% $html .= '</TR><TR>' unless $count % $columns; -% } -% $html .= '</TR></TABLE><BR><BR>'. $mod_info; -% -% $html .= include('/elements/table-grid.html', 'cellpadding' => 4 ). -% '<TR>'. -% '<TH CLASS="grid" BGCOLOR="#cccccc">Field</TH>'. -% '<TH CLASS="grid" BGCOLOR="#cccccc">Label</TH>'. -% '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'. -% '</TR>'; -% -% my $bgcolor1 = '#eeeeee'; -% my $bgcolor2 = '#ffffff'; -% my $bgcolor; -% -% #yucky kludge -% my @fields = (); -% if ( defined( dbdef->table($layer) ) ) { -% @fields = grep { -% $_ ne 'svcnum' -% && ( $communigate || !$communigate_fields{$layer}->{$_} ) -% && ( !FS::part_svc->svc_table_fields($layer) -% ->{$_}->{disable_part_svc_column} -% || $part_svc->part_svc_column($_)->columnflag -% ) -% } fields($layer); -% } -% push @fields, 'usergroup' -% if $layer eq 'svc_acct' -% or ( $layer eq 'svc_broadband' and -% $conf->exists('svc_broadband-radius') ); # double kludge -% # (but we do want to check the config, right?) -% $part_svc->svcpart($clone) if $clone; #haha, undone below -% -% -% foreach my $field (@fields) { -% -% #a few lines of false laziness w/browse/part_svc.cgi -% my $def = FS::part_svc->svc_table_fields($layer)->{$field}; -% my $def_info = $def->{'def_info'}; -% my $formatter = $def->{'format'} || sub { shift }; -% -% my $part_svc_column = $part_svc->part_svc_column($field); -% my $label = $part_svc_column->columnlabel || $def->{'label'}; -% my $value = &$formatter($part_svc_column->columnvalue); -% my $flag = $part_svc_column->columnflag; -% -% if ( $bgcolor eq $bgcolor1 ) { -% $bgcolor = $bgcolor2; -% } else { -% $bgcolor = $bgcolor1; -% } -% -% $html .= qq!<TR><TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!. -% ( $def->{'label'} || $field ). -% "</TD>"; -% -% $html .= qq!<TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor"><INPUT NAME="${layer}__${field}_label" VALUE="!. encode_entities($label). '" STYLE="text-align:right"></TD>'; -% -% $flag = '' if $def->{type} eq 'disabled'; -% -% $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!; -% -% if ( $def->{type} eq 'disabled' ) { -% -% $html .= 'No default'; -% -% } else { -% -% $html .= qq!<SELECT NAME="${layer}__${field}_flag"!. -% qq! onChange="${layer}__${field}_flag_changed(this)">!; -% -% foreach my $f ( keys %flag ) { -% -% # need to template-ize more httemplate/edit/svc_* first -% next if $f eq 'M' and $layer !~ /^svc_(broadband|external|phone|dish)$/; -% -% #here is where the SUB from above is called, to skip some choices -% next if $flag{$f}->{condition} -% && &{ $flag{$f}->{condition} }( $def, $layer, $field ); -% -% $html .= qq!<OPTION VALUE="$f"!. -% ' SELECTED'x($flag eq $f ). -% '>'. $flag{$f}->{desc}; -% -% } -% -% $html .= '</SELECT>'; -% -% $html .= join("\n", -% '<SCRIPT>', -% " function ${layer}__${field}_flag_changed(what) {", -% ' var f = what.options[what.selectedIndex].value;', -% ' if ( f == "" || f == "X" ) { //disable', -% " what.form.${layer}__${field}.disabled = true;". -% " what.form.${layer}__${field}.style.backgroundColor = '#dddddd';". -% " if ( what.form.${layer}__${field}_classnum ) {". -% " what.form.${layer}__${field}_classnum.disabled = true;". -% " what.form.${layer}__${field}_classnum.style.backgroundColor = '#dddddd';". -% " }". -% ' } else if ( f == "D" || f == "F" || f =="S" ) { //enable, text box', -% " what.form.${layer}__${field}.disabled = false;". -% " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';". -% " if ( f == 'S' || '${field}' == 'usergroup' ) {". # kludge -% " what.form.${layer}__${field}.multiple = true;". -% " } else {". -% " what.form.${layer}__${field}.multiple = false;". -% " }". -% " what.form.${layer}__${field}.style.display = '';". -% " if ( what.form.${layer}__${field}_classnum ) {". -% " what.form.${layer}__${field}_classnum.disabled = false;". -% " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';". -% " what.form.${layer}__${field}_classnum.style.display = 'none';". -% " }". -% ' } else if ( f == "M" || f == "A" || f == "H" ) { '. -% '//enable, inventory', -% " what.form.${layer}__${field}.disabled = false;". -% " what.form.${layer}__${field}.style.backgroundColor = '#ffffff';". -% " what.form.${layer}__${field}.style.display = 'none';". -% " if ( what.form.${layer}__${field}_classnum ) {". -% " what.form.${layer}__${field}_classnum.disabled = false;". -% " what.form.${layer}__${field}_classnum.style.backgroundColor = '#ffffff';". -% " what.form.${layer}__${field}_classnum.style.display = '';". -% " }". -% ' }', -% ' }', -% '</SCRIPT>', -% ); -% -% } -% -% $html .= qq!</TD><TD CLASS="grid" BGCOLOR="$bgcolor">!; -% -% my $disabled = $flag ? '' -% : 'DISABLED STYLE="background-color: #dddddd"'; -% my $nodisplay = ' STYLE="display:none"'; -% -% if ( !$def->{type} || $def->{type} eq 'text' ) { -% -% my $is_inv = ( $flag =~ /^[MA]$/ ); -% -% $html .= -% qq!<INPUT TYPE="text" NAME="${layer}__${field}" VALUE="$value" !. -% $disabled. -% ( $is_inv ? $nodisplay : $disabled ). -% '>'; -% -% $html .= include('/elements/select-table.html', -% 'element_name' => "${layer}__${field}_classnum", -% 'id' => "${layer}__${field}_classnum", -% 'element_etc' => ( $is_inv -% ? $disabled -% : $nodisplay -% ), -% 'table' => 'inventory_class', -% 'name_col' => 'classname', -% 'value' => $value, -% 'empty_label' => 'Select inventory class', -% ); -% -% } elsif ( $def->{type} eq 'checkbox' ) { -% -% $html .= include('/elements/checkbox.html', -% 'field' => $layer.'__'.$field, -% 'curr_value' => $value, -% 'value' => 'Y', -% ); -% -% } elsif ( $def->{type} eq 'select' ) { -% -% $html .= qq!<SELECT NAME="${layer}__${field}" $disabled!; -% $html .= ' MULTIPLE' if $flag eq 'S'; -% $html .= '>'; -% $html .= '<OPTION> </OPTION>' unless $value; -% if ( $def->{select_table} ) { -% foreach my $record ( qsearch( $def->{select_table}, {} ) ) { -% my $rvalue = $record->getfield($def->{select_key}); -% my $select_label = $def->{select_label}; -% $html .= qq!<OPTION VALUE="$rvalue"!. -% (grep(/^$rvalue$/, split(',',$value)) ? ' SELECTED>' : '>' ). -% $record->$select_label(). '</OPTION>'; -% } #next $record -% } elsif ( $def->{select_list} ) { -% foreach my $item ( @{$def->{select_list}} ) { -% $html .= qq!<OPTION VALUE="$item"!. -% (grep(/^$item$/, split(',',$value)) ? ' SELECTED>' : '>' ). -% $item. '</OPTION>'; -% } #next $item -% } elsif ( $def->{select_hash} ) { -% if ( ref($def->{select_hash}) eq 'ARRAY' ) { -% tie my %hash, 'Tie::IxHash', @{ $def->{select_hash} }; -% $def->{select_hash} = \%hash; -% } -% foreach my $key ( keys %{$def->{select_hash}} ) { -% $html .= qq!<OPTION VALUE="$key"!. -% (grep(/^$key$/, split(',',$value)) ? ' SELECTED>' : '>' ). -% $def->{select_hash}{$key}. '</OPTION>'; -% } #next $key -% } #endif -% $html .= '</SELECT>'; -% -% } elsif ( $def->{type} eq 'textarea' ) { -% -% $html .= -% qq!<TEXTAREA NAME="${layer}__${field}">!. encode_entities($value). -% '</TEXTAREA>'; -% -% } elsif ( $def->{type} =~ /select-(.*?).html/ ) { -% -% $html .= include("/elements/".$def->{type}, -% 'curr_value' => $value, -% 'element_name' => "${layer}__${field}", -% 'element_etc' => $disabled, -% 'multiple' => ($def->{multiple} || -% $flag eq 'S'), -% # allow the table def to force 'multiple' -% ); -% -% } elsif ( $def->{type} eq 'communigate_pro-accessmodes' ) { -% -% $html .= include('/elements/communigate_pro-accessmodes.html', -% 'element_name_prefix' => "${layer}__${field}_", -% 'curr_value' => $value, -% #doesn't work#'element_etc' => $disabled, -% ); -% -% } elsif ( $def->{type} eq 'select-hardware' ) { -% -% $html .= qq!<INPUT TYPE="text" NAME="${layer}__${field}" $disabled>!; -% $html .= include('/elements/select-hardware_class.html', -% 'curr_value' => $value, -% 'element_name' => "${layer}__${field}_classnum", -% 'id' => "${layer}__${field}_classnum", -% 'element_etc' => $flag ne 'H' && $nodisplay, -% 'empty_label' => 'Select hardware class', -% ); -% -% } elsif ( $def->{type} eq 'disabled' ) { -% -% $html .= -% qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!; -% -% } else { -% -% $html .= '<font color="#ff0000">unknown type '. $def->{type}; -% -% } -% -% $html .= "</TD></TR>\n"; - -% $def_info = "($def_info)" if $def_info; -% $html .= -% qq!<TR>!. -% qq! <TD COLSPAN=2 BGCOLOR="$bgcolor" ALIGN="center" !. -% qq! STYLE="padding:0; border-top: none">!. -% qq! <FONT SIZE="-1"><I>$def_info</I></FONT>!. -% qq! </TD>!. -% qq!</TR>\n!; -% -% } #foreach my $field (@fields) { -% -% $part_svc->svcpart('') if $clone; #undone -% $html .= "</TABLE>"; -% -% $html .= include('/elements/progress-init.html', -% $layer, #form name -% [ qw(svc svcpart classnum selfservice_access -% disabled preserve -% exportnum), -% @fields ], -% 'process/part_svc.cgi', -% $p.'browse/part_svc.cgi', -% $layer, -% ); -% $html .= '<BR><INPUT NAME="submit" TYPE="button" VALUE="'. -% ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '" '. -% ' onClick="document.'. "$layer.submit.disabled=true; ". -% "fixup(document.$layer); $layer". 'process();">'; -% -% #$html .= '<BR><INPUT TYPE="submit" VALUE="'. -% # ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">'; -% -% $html; -% -% }, -% ); - <BR> Table <% $widget->html %> @@ -465,28 +196,43 @@ my $action = $part_svc->svcpart ? 'Edit' : 'Add'; my $hashref = $part_svc->hashref; # my $p_svcdb = $part_svc->svcdb || 'svc_acct'; -my %communigate_fields = ( - 'svc_acct' => { map { $_=>1 } - qw( file_quota file_maxnum file_maxsize - password_selfchange password_recover - ), - grep /^cgp_/, fields('svc_acct') - }, - 'svc_domain' => { map { $_=>1 } - qw( max_accounts trailer parent_svcnum ), - grep /^(cgp|acct_def)_/, fields('svc_domain') - }, - #'svc_forward' => { map { $_=>1 } qw( ) }, - #'svc_mailinglist' => { map { $_=>1 } qw( ) }, - #'svc_cert' => { map { $_=>1 } qw( ) }, -); -my $mod_info = ' -For the selected table, you can give fields default or fixed (unchangable) -values, or select an inventory class to manually or automatically fill in -that field. -'; +my @dbs = $hashref->{svcdb} + ? ( $hashref->{svcdb} ) + : FS::part_svc->svc_tables(); + +my $help = ''; +unless ( $hashref->{svcpart} ) { + $help = ' '. + include('/elements/popup_link.html', + 'action' => $p.'docs/part_svc-table.html', + 'label' => 'help', + 'actionlabel' => 'Service table help', + 'width' => 763, + #'height' => 400, + ); +} +tie my %svcdb, 'Tie::IxHash', map { $_=>$_ } grep dbdef->table($_), @dbs; +my $widget = new HTML::Widgets::SelectLayers( + #'selected_layer' => $p_svcdb, + 'selected_layer' => $hashref->{svcdb} || 'svc_acct', + 'options' => \%svcdb, + 'form_name' => 'dummy', + #'form_action' => 'process/part_svc.cgi', + 'form_action' => 'part_svc.cgi', #self + 'form_elements' => [qw( svc svcpart classnum selfservice_access + disabled preserve + )], + 'html_between' => $help, + 'layer_callback' => sub { + include('elements/part_svc_column.html', + shift, + 'part_svc' => $part_svc, + 'clone' => $clone + ) + } +); </%init> diff --git a/httemplate/edit/part_tag.html b/httemplate/edit/part_tag.html index 5712560c1..2cf34c6e8 100644 --- a/httemplate/edit/part_tag.html +++ b/httemplate/edit/part_tag.html @@ -8,7 +8,7 @@ { field=>'by_default', type=>'checkbox', value=>'Y' }, $tagcolor, ], - 'labels' => { 'tagnum' => 'Tag #', + 'labels' => { 'tagnum' => 'Tag', 'tagname' => 'Tag', 'tagdesc' => 'Message', 'tagcolor' => 'Highlight Color', diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html index dfe52f109..37618d677 100644 --- a/httemplate/edit/payment_gateway.html +++ b/httemplate/edit/payment_gateway.html @@ -13,15 +13,16 @@ 'gateway_action' => 'Action', 'gateway_options' => 'Options (Name/Value pairs, <BR>one element per line)', 'gateway_callback_url' => 'Callback URL', + 'gateway_cancel_url' => 'Cancel URL', }, ) %> <SCRIPT TYPE="text/javascript"> - var modulesForNamespace = <% to_json(\%modules_for_namespace, {canonical=>1}) %>; - function changeNamespace(what) { - var ns = what.value; + var modulesForNamespace = <% $json->encode(\%modules) %>; + function changeNamespace() { + var ns = document.getElementById('gateway_namespace').value; var select_module = document.getElementById('gateway_module'); select_module.options.length = 0; for (var x in modulesForNamespace[ns]) { @@ -30,6 +31,7 @@ select_module.add(o, null); } } + window.onload = changeNamespace; </SCRIPT> <%init> @@ -37,69 +39,72 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); -my %modules = ( - '2CheckOut' => 'Business::OnlinePayment', - 'AuthorizeNet' => 'Business::OnlinePayment', - 'BankOfAmerica' => 'Business::OnlinePayment', #deprecated? - 'Beanstream' => 'Business::OnlinePayment', - 'Capstone' => 'Business::OnlinePayment', - 'Cardstream' => 'Business::OnlinePayment', - 'CashCow' => 'Business::OnlinePayment', - 'CyberSource' => 'Business::OnlinePayment', - 'eSec' => 'Business::OnlinePayment', - 'eSelectPlus' => 'Business::OnlinePayment', - 'eWayShared' => 'Business::OnlineThirdPartyPayment', - 'ElavonVirtualMerchant' => 'Business::OnlinePayment', - 'Exact' => 'Business::OnlinePayment', - 'iAuthorizer' => 'Business::OnlinePayment', - 'Ingotz' => 'Business::OnlinePayment', - 'InternetSecure' => 'Business::OnlinePayment', - 'Interswitchng' => 'Business::OnlineThirdPartyPayment', - 'IPaymentTPG' => 'Business::OnlinePayment', - 'IPPay' => 'Business::OnlinePayment', - 'Iridium' => 'Business::OnlinePayment', - 'Jettis' => 'Business::OnlinePayment', - 'Jety' => 'Business::OnlinePayment', - 'LinkPoint' => 'Business::OnlinePayment', - 'MerchantCommerce' => 'Business::OnlinePayment', - 'Network1Financial' => 'Business::OnlinePayment', - 'OCV' => 'Business::OnlinePayment', - 'OpenECHO' => 'Business::OnlinePayment', - 'PayConnect' => 'Business::OnlinePayment', - 'PayflowPro' => 'Business::OnlinePayment', - 'PaymenTech' => 'Business::OnlinePayment', - 'PaymentsGateway' => 'Business::OnlinePayment', - 'PayPal' => 'Business::OnlinePayment', - #'PaySystems' => 'Business::OnlinePayment', - 'PlugnPay' => 'Business::OnlinePayment', - 'PPIPayMover' => 'Business::OnlinePayment', - 'Protx' => 'Business::OnlinePayment', #now SagePay - 'PXPost' => 'Business::OnlinePayment', - 'SagePay' => 'Business::OnlinePayment', - 'SecureHostingUPG' => 'Business::OnlinePayment', - 'Skipjack' => 'Business::OnlinePayment', - 'StGeorge' => 'Business::OnlinePayment', - 'SurePay' => 'Business::OnlinePayment', - 'TCLink' => 'Business::OnlinePayment', - 'TransactionCentral' => 'Business::OnlinePayment', - 'TransFirsteLink' => 'Business::OnlinePayment', - 'Vanco' => 'Business::OnlinePayment', - 'viaKLIX' => 'Business::OnlinePayment', - 'VirtualNet' => 'Business::OnlinePayment', - 'WesternACH' => 'Business::OnlinePayment', - 'WorldPay' => 'Business::OnlinePayment', - - 'KeyBank' => 'Business::BatchPayment', - 'Paymentech' => 'Business::BatchPayment', - 'TD_EFT' => 'Business::BatchPayment', +my $json = JSON::XS->new; +$json->canonical(1); +my %modules = ( + 'Business::OnlinePayment' => [ + '2CheckOut', + 'AuthorizeNet', + 'BankOfAmerica', #deprecated? + 'Beanstream', + 'Capstone', + 'Cardstream', + 'CashCow', + 'CyberSource', + 'eSec', + 'eSelectPlus', + 'ElavonVirtualMerchant', + 'Exact', + 'iAuthorizer', + 'Ingotz', + 'InternetSecure', + 'IPaymentTPG', + 'IPPay', + 'Iridium', + 'Jettis', + 'Jety', + 'LinkPoint', + 'MerchantCommerce', + 'Network1Financial', + 'OCV', + 'OpenECHO', + 'PayConnect', + 'PayflowPro', + 'PaymenTech', + 'PaymentsGateway', + 'PayPal', + #'PaySystems', + 'PlugnPay', + 'PPIPayMover', + 'Protx', #now SagePay + 'PXPost', + 'SagePay', + 'SecureHostingUPG', + 'Skipjack', + 'StGeorge', + 'SurePay', + 'TCLink', + 'TransactionCentral', + 'TransFirsteLink', + 'Vanco', + 'viaKLIX', + 'VirtualNet', + 'WesternACH', + 'WorldPay', + ], + 'Business::OnlineThirdPartyPayment' => [ + #'eWayShared', support currently broken + #'Interswitchng', + 'PayPal', + 'FCMB', + ], + 'Business::BatchPayment' => [ + 'KeyBank', + 'Paymentech', + 'TD_EFT', + ], ); -my %modules_for_namespace; -for (keys %modules) { - $modules_for_namespace{$modules{$_}} ||= []; - push @{ $modules_for_namespace{$modules{$_}} }, $_; -} - my @actions = ( 'Normal Authorization', 'Authorization Only', @@ -125,7 +130,9 @@ my $fields = [ { field => 'gateway_module', type => 'select', - options => [ sort { lc($a) cmp lc ($b) } keys %modules ], + # does it even make sense to list all modules here? + options => [ sort { lc($a) cmp lc ($b) } + map { @$_ } values %modules ], }, 'gateway_username', 'gateway_password', @@ -140,6 +147,11 @@ my $fields = [ size => 40, }, { + field => 'gateway_cancel_url', + type => 'text', + size => 40, + }, + { field => 'gateway_options', type => 'textarea', rows => '12', diff --git a/httemplate/edit/phone_device.html b/httemplate/edit/phone_device.html index 4aec63e5a..7bc88a8c7 100644 --- a/httemplate/edit/phone_device.html +++ b/httemplate/edit/phone_device.html @@ -32,12 +32,11 @@ %> <%init> -my @deviceparts_with_inventory; -my @part_device = qsearch('part_device', {} ); -foreach my $part_device ( @part_device ) { - push @deviceparts_with_inventory, $part_device->devicepart - if $part_device->inventory_classnum; -} +my @deviceparts_with_inventory = + map $_->devicepart, + qsearch({ 'table' => 'part_device', + 'extra_sql' => 'WHERE inventory_classnum IS NOT NULL', + }); my $html_foot = sub { my $js = " @@ -72,9 +71,9 @@ my $html_foot = sub { var devicepart = what.options[what.selectedIndex].value; - var deviceparts_with_inventory = new Array(\""; -$js .= join("\",\"",@deviceparts_with_inventory); -$js .= "\"); + var deviceparts_with_inventory = new Array("; +$js .= join(',', map qq("$_"), @deviceparts_with_inventory); +$js .= "); var hasInventory = false; for ( i = 0; i < deviceparts_with_inventory.length; i++ ) { diff --git a/httemplate/edit/process/REAL_cust_pkg.cgi b/httemplate/edit/process/REAL_cust_pkg.cgi deleted file mode 100755 index 3e0ef59c1..000000000 --- a/httemplate/edit/process/REAL_cust_pkg.cgi +++ /dev/null @@ -1,54 +0,0 @@ -%if ( $error ) { -% $cgi->param('error', $error); -<% $cgi->redirect(popurl(2). "REAL_cust_pkg.cgi?". $cgi->query_string ) %> -%} else { -% my $custnum = $new->custnum; -% my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/ -% ? '' -% : ';show=packages'; -% my $frag = "cust_pkg$pkgnum"; #hack for IE ignoring real #fragment -<% $cgi->redirect(popurl(3). "view/cust_main.cgi?custnum=$custnum$show;fragment=$frag#$frag" ) %> -%} -<%init> - -my $curuser = $FS::CurrentUser::CurrentUser; - -die "access denied" - unless $curuser->access_right('Edit customer package dates'); - -my $pkgnum = $cgi->param('pkgnum') or die; -my $old = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); -my %hash = $old->hash; -$hash{$_}= $cgi->param($_) ? parse_datetime($cgi->param($_)) : '' - foreach qw( start_date setup bill last_bill contract_end ); - # adjourn, expire, resume not editable this way - -my @errors = (); - -push @errors, '_bill_areyousure' - if $hash{'bill'} != $old->bill # if the next bill date was changed - && $hash{'bill'} < time # to a date in the past - && ! $cgi->param('bill_areyousure'); # and it wasn't confirmed - -push @errors, '_setup_areyousure' - if ! $hash{'setup'} && $old->setup # if the setup date was removed - && ! $cgi->param('setup_areyousure'); # and it wasn't confirmed - -push @errors, '_setupadd_areyousure' - if $hash{'setup'} && ! $old->setup # if the setup date was added - && ! $cgi->param('setupadd_areyousure'); # and it wasn't confirmed - -push @errors, '_start' - if $hash{'start_date'} && !$old->start_date # if a start date was added - && $hash{'setup'}; # but there's a setup date - -my $new; -my $error; -if ( @errors ) { - $error = join(',', @errors); -} else { - $new = new FS::cust_pkg \%hash; - $error = $new->replace($old); -} - -</%init> diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html index 8e7e70a06..7fc7c25e1 100644 --- a/httemplate/edit/process/access_user.html +++ b/httemplate/edit/process/access_user.html @@ -3,14 +3,15 @@ % print $cgi->redirect(popurl(2) . "access_user.html?" . $cgi->query_string); % } else { <% include( 'elements/process.html', - 'table' => 'access_user', - 'viewall_dir' => 'browse', - 'copy_on_empty' => [ '_password' ], + 'table' => 'access_user', + 'viewall_dir' => 'browse', + 'copy_on_empty' => [ '_password', '_password_encoding' ], 'clear_on_error' => [ '_password', '_password2' ], - 'process_m2m' => { 'link_table' => 'access_usergroup', - 'target_table' => 'access_group', - }, - 'precheck_callback'=> \&precheck_callback, + 'process_m2m' => { 'link_table' => 'access_usergroup', + 'target_table' => 'access_group', + }, + 'precheck_callback' => \&precheck_callback, + 'post_new_object_callback' => \&post_new_object_callback, ) %> % } @@ -26,11 +27,24 @@ if ( FS::Conf->new->exists('disable_acl_changes') ) { sub precheck_callback { my $cgi = shift; + my $o = FS::access_user->new({username => $cgi->param('username')}); if( $o->is_system_user and !$cgi->param('usernum') ) { $cgi->param('username',''); return "username '".$o->username."' reserved for system account." } + return ''; } + +sub post_new_object_callback { + my( $cgi, $access_user ) = @_; + + if ( length($cgi->param('_password')) ) { + my $password = scalar($cgi->param('_password')); + $access_user->change_password_fields($password); + } + +} + </%init> diff --git a/httemplate/edit/process/agent.cgi b/httemplate/edit/process/agent.cgi index 034c4cc50..554992958 100755 --- a/httemplate/edit/process/agent.cgi +++ b/httemplate/edit/process/agent.cgi @@ -5,6 +5,12 @@ 'process_m2m' => { 'link_table' => 'access_groupagent', 'target_table' => 'access_group', }, + 'process_m2name' => { + 'link_table' => 'agent_currency', + 'name_col' => 'currency', + 'names_list' => [ $conf->config('currencies') ], + 'param_style' => 'link_table.value checkboxes', + }, 'edit_ext' => 'cgi', 'noerror_callback' => $process_agent_pkg_class, ) @@ -14,7 +20,9 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); -if ( FS::Conf->new->exists('disable_acl_changes') ) { +my $conf = new FS::Conf; + +if ( $conf->exists('disable_acl_changes') ) { errorpage('ACL changes disabled in public demo.'); die "shouldn't be reached"; } diff --git a/httemplate/edit/process/bulk-cust_svc-pkgnum.html b/httemplate/edit/process/bulk-cust_svc-pkgnum.html new file mode 100644 index 000000000..f5cf7dd07 --- /dev/null +++ b/httemplate/edit/process/bulk-cust_svc-pkgnum.html @@ -0,0 +1,39 @@ +% if ($error) { +% #$cgi->param('error', $error); +% #$cgi->redirect(popurl(3). 'misc/detach_pkg.html?'. $cgi->query_string ); +% #XXX actually redirect back and display the error instead +% errorpage_popup($error); +% } else { + + <% header(emt("Services moved")) %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY> + </HTML> + +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" unless $curuser->access_right('Bulk move customer services'); + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum'; +my $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'; + +my @svcnum = (); +foreach my $param (grep /^svcnum\d+$/, $cgi->param) { + $param =~ /^svcnum(\d+)$/ or die "guru meditation #309"; + push @svcnum, $1 if $cgi->param($param); +} + +my $error = $cust_pkg->grab_svcnums(@svcnum); + +</%init> diff --git a/httemplate/edit/process/bulk-part_pkg.html b/httemplate/edit/process/bulk-part_pkg.html new file mode 100644 index 000000000..4775a9334 --- /dev/null +++ b/httemplate/edit/process/bulk-part_pkg.html @@ -0,0 +1,30 @@ +% if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(3).'/edit/bulk-part_pkg.cgi?', $cgi->query_string) %> +% } else { +<% $cgi->redirect(popurl(3).'/browse/part_pkg.cgi') %> +% } +<%init> +die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Bulk edit package definitions'); + +my @pkgparts = $cgi->param('pkgpart') + or die "no package definitions selected"; + +my %changes; +foreach my $param (grep { /^report_option_\d+$/ } $cgi->param) { + if ( length($cgi->param($param)) ) { + if ( $cgi->param($param) == 1 ) { + $changes{$param} = 1; + } else { + $changes{$param} = ''; + } + } +} + +my $error; +foreach my $pkgpart (@pkgparts) { + my $part_pkg = FS::part_pkg->by_key($pkgpart); + my %options = ( $part_pkg->options, %changes ); + $error ||= $part_pkg->replace( options => \%options ); +} +</%init> diff --git a/httemplate/edit/process/cable_device.html b/httemplate/edit/process/cable_device.html new file mode 100644 index 000000000..97b4f81d9 --- /dev/null +++ b/httemplate/edit/process/cable_device.html @@ -0,0 +1,23 @@ +<% include( 'elements/process.html', + 'table' => 'cable_device', + 'redirect' => sub { + my( $cgi, $cable_device ) = @_; + #popurl(3).'view/svc_cable.html?'. + popurl(3).'view/svc_Common.html?svcdb=svc_cable;'. + 'svcnum='. $cable_device->svcnum. + ';devicenum='; + }, + ) +%> +<%init> + +if($cgi->param('sel_mac_addr') && !$cgi->param('mac_addr')) { + $cgi->param('mac_addr',$cgi->param('sel_mac_addr')); +} + +# :/ needs agent-virt so you can't futz with arbitrary devices + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +</%init> diff --git a/httemplate/edit/process/cdr_carrier.html b/httemplate/edit/process/cdr_carrier.html new file mode 100644 index 000000000..72f018609 --- /dev/null +++ b/httemplate/edit/process/cdr_carrier.html @@ -0,0 +1,10 @@ +<& elements/process.html, + 'table' => 'cdr_carrier', + 'viewall_dir' => 'browse', +&> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/cdr_type.cgi b/httemplate/edit/process/cdr_type.cgi index b661de75d..ba9881dc4 100644 --- a/httemplate/edit/process/cdr_type.cgi +++ b/httemplate/edit/process/cdr_type.cgi @@ -10,7 +10,6 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); my %vars = $cgi->Vars; -warn Dumper(\%vars)."\n"; my %old = map { $_->cdrtypenum => $_ } qsearch('cdr_type', {}); diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html index 2770f3283..c893f13a2 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({ '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/credit-cust_bill_pkg.html b/httemplate/edit/process/credit-cust_bill_pkg.html new file mode 100644 index 000000000..8e66368d4 --- /dev/null +++ b/httemplate/edit/process/credit-cust_bill_pkg.html @@ -0,0 +1,45 @@ +%if ($error) { +% errorpage_popup($error); #XXX redirect back for correction... +%} else { +<& /elements/header-popup.html, 'Credit successful' &> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Credit line items'); + +my @billpkgnum_setuprecurs = + map { $_ =~ /^billpkgnum(\d+\-\w*)$/ or die 'gm#23'; $1; } + grep { $_ =~ /^billpkgnum\d+\-\w*$/ && $cgi->param($_) } $cgi->param; + +my @billpkgnums = (); +my @setuprecurs = (); +my @amounts = (); +foreach my $billpkgnum_setuprecur (@billpkgnum_setuprecurs) { + my $amount = $cgi->param("billpkgnum$billpkgnum_setuprecur-amount"); + my( $billpkgnum, $setuprecur ) = split('-', $billpkgnum_setuprecur); + push @billpkgnums, $billpkgnum; + push @setuprecurs, $setuprecur; + push @amounts, $amount; +} + +my $error = FS::cust_credit->credit_lineitems( + #the lineitems to credit + 'billpkgnums' => \@billpkgnums, + 'setuprecurs' => \@setuprecurs, + 'amounts' => \@amounts, + 'apply' => ( $cgi->param('apply') eq 'yes' ), + + #the credit + 'newreasonnum' => scalar($cgi->param('newreasonnum')), + 'newreasonnum_type' => scalar($cgi->param('newreasonnumT')), + map { $_ => scalar($cgi->param($_)) } + #fields('cust_credit') + qw( custnum _date amount reason reasonnum addlinfo ), #pkgnum eventnum +); + +</%init> diff --git a/httemplate/edit/process/currency_exchange.html b/httemplate/edit/process/currency_exchange.html new file mode 100644 index 000000000..1f6852299 --- /dev/null +++ b/httemplate/edit/process/currency_exchange.html @@ -0,0 +1,36 @@ +%if ( $error ) { +% errorpage($error); #also not super ideal +%} else { #or this +<% include('/elements/header.html', 'Exchange rates updated') %> +<% include('/elements/footer.html') %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; + +my $to_currency = $conf->config('currency') || 'USD'; + +my @currencies = sort { $a cmp $b } $conf->config('currencies'); + +#in the best of all possible worlds, i would be a single database transaction +# but here it isn't terribly important other than offending my sense of elegance +my $error = ''; +foreach my $currency (@currencies) { + + my %hash = ( 'from_currency' => $currency, + 'to_currency' => $to_currency, + ); + + my $currency_exchange = qsearchs('currency_exchange', \%hash) + || new FS::currency_exchange \%hash; + + $currency_exchange->rate( $cgi->param("$currency-$to_currency") ); + + my $method = $currency_exchange->currencyratenum ? 'replace' : 'insert'; + $error = $currency_exchange->$method() and last; +} + +</%init> diff --git a/httemplate/edit/process/cust_credit.cgi b/httemplate/edit/process/cust_credit.cgi index 776112ac0..245f31af7 100755 --- a/httemplate/edit/process/cust_credit.cgi +++ b/httemplate/edit/process/cust_credit.cgi @@ -15,7 +15,7 @@ % % $dbh->commit or die $dbh->errstr if $oldAutoCommit; % -<% header(emt('Credit sucessful')) %> +<% header(emt('Credit successful')) %> <SCRIPT TYPE="text/javascript"> window.top.location.reload(); </SCRIPT> @@ -27,7 +27,7 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Post credit'); -$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!"; +$cgi->param('custnum') =~ /^(\d+)$/ or die "Illegal custnum!"; my $custnum = $1; $cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum"; diff --git a/httemplate/edit/process/cust_location.cgi b/httemplate/edit/process/cust_location.cgi index b9f93db8b..fd1b8740e 100644 --- a/httemplate/edit/process/cust_location.cgi +++ b/httemplate/edit/process/cust_location.cgi @@ -31,10 +31,9 @@ die "unknown locationnum $locationnum" unless $cust_location; my $new = FS::cust_location->new({ 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); +my $error = $new->find_or_insert; +$error ||= $cust_location->move_to($new); </%init> diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi index 31ec4ab12..621de24bf 100755 --- a/httemplate/edit/process/cust_main.cgi +++ b/httemplate/edit/process/cust_main.cgi @@ -16,8 +16,8 @@ my $DEBUG = 0; </%once> <%init> -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Edit customer'); +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" unless $curuser->access_right('Edit customer'); my $conf = new FS::Conf; @@ -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)) { @@ -71,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(\%hash); } if ( ($cgi->param('same') || '') eq 'Y' ) { @@ -156,9 +165,14 @@ foreach my $dfield (qw( $new->setfield('paid', $cgi->param('paid') ) if $cgi->param('paid'); -my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); -my @tax_exempt = grep { $cgi->param("tax_$_") eq 'Y' } @exempt_groups; -my %tax_exempt = map { $_ => scalar($cgi->param("tax_$_".'_num')) } @tax_exempt; +my %options = (); +if ( $curuser->access_right('Edit customer tax exemptions') ) { + my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); + my @tax_exempt = grep { $cgi->param("tax_$_") eq 'Y' } @exempt_groups; + $options{'tax_exemption'} = { + map { $_ => scalar($cgi->param("tax_$_".'_num')) } @tax_exempt + }; +} #perhaps this stuff should go to cust_main.pm if ( $new->custnum eq '' or $duplicate_of ) { @@ -266,8 +280,8 @@ if ( $new->custnum eq '' or $duplicate_of ) { else { # create the customer $error ||= $new->insert( \%hash, \@invoicing_list, - 'tax_exemption'=> \%tax_exempt, - 'prospectnum' => scalar($cgi->param('prospectnum')), + %options, + prospectnum => scalar($cgi->param('prospectnum')), ); my $conf = new FS::Conf; @@ -328,7 +342,7 @@ if ( $new->custnum eq '' or $duplicate_of ) { warn Dumper({ new => $new, old => $old }) if $DEBUG; $error ||= $new->replace( $old, \@invoicing_list, - 'tax_exemption' => \%tax_exempt, + %options, ); warn "$me returned from replace" if $DEBUG; diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi index ce0ec3212..a002fa181 100755 --- a/httemplate/edit/process/cust_pay.cgi +++ b/httemplate/edit/process/cust_pay.cgi @@ -57,6 +57,8 @@ my $new = new FS::cust_pay ( { bank depositor account teller ) #} fields('cust_pay') + # gatewaynum, processor, auth, order_number + # are for realtime payments only, and can't be entered manually } ); my @rights = ('Post payment'); 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/cust_svc.cgi b/httemplate/edit/process/cust_svc.cgi index e22cbb201..7cb1d6d8f 100644 --- a/httemplate/edit/process/cust_svc.cgi +++ b/httemplate/edit/process/cust_svc.cgi @@ -6,7 +6,7 @@ %} <%init> -die 'access deined' +die 'access denied' unless $FS::CurrentUser::CurrentUser->access_right('Change customer service'); my $svcnum = $cgi->param('svcnum'); diff --git a/httemplate/edit/process/detach-cust_pkg.html b/httemplate/edit/process/detach-cust_pkg.html new file mode 100644 index 000000000..ab87eb536 --- /dev/null +++ b/httemplate/edit/process/detach-cust_pkg.html @@ -0,0 +1,47 @@ +% if ($error) { +% $cgi->param('error', $error); +% $cgi->redirect(popurl(3). 'misc/detach_pkg.html?'. $cgi->query_string ); +% } else { + + <% header(emt("Package detached")) %> + <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; + +my $cust_location = new FS::cust_location { + map { $_ => scalar($cgi->param($_)) } FS::cust_main->location_fields +}; + +my $cust_main = new FS::cust_main { + ( map { ( $_, scalar($cgi->param($_)) ) } fields('cust_main') ), + ( map { ( "ship_$_", '' ) } FS::cust_main->location_fields ), + 'bill_location' => $cust_location, + 'ship_location' => $cust_location, +}; + +my $pkg_or_error = $cust_pkg->change( { + 'keep_dates' => 1, + 'cust_main' => $cust_main, +} ); + +my $error = ref($pkg_or_error) ? '' : $pkg_or_error; + +</%init> diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html index 2d39e9dce..0439d4e9c 100644 --- a/httemplate/edit/process/elements/process.html +++ b/httemplate/edit/process/elements/process.html @@ -70,6 +70,9 @@ Example: #return an error string or empty for no error 'precheck_callback' => sub { my( $cgi ) = @_; }, + #after the new object is created + 'post_new_object_callback' => sub { my( $cgi, $object ) = @_; }, + #after everything's inserted 'noerror_callback' => sub { my( $cgi, $object ) = @_; }, @@ -201,7 +204,7 @@ my %hash = my @values = ( 1 ); if ( $bfield ) { @values = $cgi->param($bfield); - warn join(',', @values); + #warn join(',', @values); } my $new; @@ -226,6 +229,10 @@ foreach my $value ( @values ) { } } + if ( $opt{'post_new_object_callback'} ) { + &{ $opt{'post_new_object_callback'} }( $cgi, $new ); + } + if ( $opt{'agent_virt'} ) { if ( ! $new->agentnum @@ -263,6 +270,9 @@ foreach my $value ( @values ) { if ( !$error ) { if ( $old_pkey ) { + + &{ $opt{'edit_callback'} }( $new, $old ) if $opt{'edit_callback'}; + $error = $new->replace($old, @args); } else { $error = $new->insert(@args); diff --git a/httemplate/edit/process/elements/svc_Common.html b/httemplate/edit/process/elements/svc_Common.html index 5a8afbd6c..06f4c00b1 100644 --- a/httemplate/edit/process/elements/svc_Common.html +++ b/httemplate/edit/process/elements/svc_Common.html @@ -10,5 +10,10 @@ my %opt = @_; my $table = $opt{'table'}; $opt{'fields'} ||= [ fields($table) ]; push @{ $opt{'fields'} }, qw( pkgnum svcpart ); +foreach (fields($table)) { + if ( $cgi->param($_.'_classnum') ) { + push @{ $opt{'fields'} }, $_.'_classnum'; + } +} </%init> diff --git a/httemplate/edit/process/ftp_target.html b/httemplate/edit/process/ftp_target.html deleted file mode 100644 index 35f56c490..000000000 --- a/httemplate/edit/process/ftp_target.html +++ /dev/null @@ -1,12 +0,0 @@ -<& elements/process.html, - 'table' => 'ftp_target', - 'viewall_dir' => 'browse', - 'agent_null' => 1, -&> -<%init> -my $curuser = $FS::CurrentUser::CurrentUser; - -die "access denied" - unless $curuser->access_right('Configuration'); - -</%init> diff --git a/httemplate/edit/process/part_export.cgi b/httemplate/edit/process/part_export.cgi index 6432d6b15..e0c470675 100644 --- a/httemplate/edit/process/part_export.cgi +++ b/httemplate/edit/process/part_export.cgi @@ -13,15 +13,40 @@ my $exportnum = $cgi->param('exportnum'); my $old = qsearchs('part_export', { 'exportnum'=>$exportnum } ) if $exportnum; +my %vars = $cgi->Vars; #fixup options #warn join('-', split(',',$cgi->param('options'))); my %options = map { - my @values = $cgi->param($_); - my $value = scalar(@values) > 1 ? join (' ', @values) : $values[0]; + my $value = $vars{$_}; + $value =~ s/\0/ /g; # deal with multivalued options $value =~ s/\r\n/\n/g; #browsers? (textarea) $_ => $value; } split(',', $cgi->param('options')); +# deal with multiline options +# %vars should never contain incomplete rows, but just in case it does, +# we make a list of all the row indices that contain values, and +# then write a line in each option for each row, even if it's empty. +# This ensures that all values with the same row index line up. +my %optionrows; +foreach my $option (split(',', $cgi->param('multi_options'))) { + $optionrows{$option} = {}; + my %values; # bear with me + for (keys %vars) { + /^$option(\d+)/ or next; + $optionrows{$option}{$1} = $vars{$option.$1}; + $optionrows{_ALL_}{$1} = 1 if length($vars{$option.$1}); + } +} +foreach my $option (split(',', $cgi->param('multi_options'))) { + my $value = ''; + foreach my $row (sort keys %{$optionrows{_ALL_}}) { + $value .= ($optionrows{$option}{$row} || '') . "\n"; + } + chomp($value); + $options{$option} = $value; +} + my $new = new FS::part_export ( { map { $_, scalar($cgi->param($_)); @@ -31,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 c388676df..3b6562f13 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> @@ -114,6 +115,19 @@ my $args_callback = sub { push @args, 'options' => \%options; ### + #part_pkg_currency + ### + + my %part_pkg_currency = ( + map { $_ => scalar($cgi->param($_)) } + #grep /._[A-Z]{3}$/, #support other options + grep /^(setup|recur)_fee_[A-Z]{3}$/, + $cgi->param + ); + + push @args, 'part_pkg_currency' => \%part_pkg_currency; + + ### #pkg_svc ### @@ -185,6 +199,15 @@ my @process_m2m = ( grep /^svc_dst_pkgpart/, $cgi->param ], }, + { 'link_table' => 'part_pkg_link', + 'target_table' => 'part_pkg', + 'base_field' => 'src_pkgpart', + 'target_field' => 'dst_pkgpart', + 'hashref' => { 'link_type' => 'supp', 'hidden' => '' }, + 'params' => [ map $cgi->param($_), + grep /^supp_dst_pkgpart/, $cgi->param + ], + }, map { my $hidden = $_; { 'link_table' => 'part_pkg_link', @@ -235,4 +258,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/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/edit/process/payment_gateway.html b/httemplate/edit/process/payment_gateway.html index 812c988c5..157449e89 100644 --- a/httemplate/edit/process/payment_gateway.html +++ b/httemplate/edit/process/payment_gateway.html @@ -15,6 +15,7 @@ my $args_callback = sub { my @options = split(/\r?\n/, $cgi->param('gateway_options') ); pop @options if scalar(@options) % 2 && $options[-1] =~ /^\s*$/; + @options = ( {} ) if !@options; (@options) }; diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi index 2dadbccdc..fe5ee5e9e 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,12 +146,22 @@ 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({ 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; + } else { + $opt{'locationnum'} = $locationnum; } $error = $cust_main->order_pkg( \%opt ); diff --git a/httemplate/edit/process/svc_acct.cgi b/httemplate/edit/process/svc_acct.cgi index 41aca65ee..d4bcd35ed 100755 --- a/httemplate/edit/process/svc_acct.cgi +++ b/httemplate/edit/process/svc_acct.cgi @@ -31,6 +31,11 @@ foreach (map { $_,$_."_threshold" } qw( upbytes downbytes totalbytes )) { $cgi->param($_, FS::UI::bytecount::parse_bytecount($cgi->param($_)) ); } +#for slipip, convert '(automatic)' to null +my $ip_addr = $cgi->param('slipip'); +$ip_addr =~ s/[^\d\.]//g; +$cgi->param('slipip', $ip_addr); + #unmunge cgp_accessmodes (falze laziness-ish w/part_svc.pm::process &svc_domain) unless ( $cgi->param('cgp_accessmodes') ) { $cgi->param('cgp_accessmodes', diff --git a/httemplate/edit/process/svc_phone.html b/httemplate/edit/process/svc_phone.html index 7a3b43d32..09398fdfb 100644 --- a/httemplate/edit/process/svc_phone.html +++ b/httemplate/edit/process/svc_phone.html @@ -2,6 +2,7 @@ 'table' => 'svc_phone', 'args_callback' => $args_callback, 'value_callback' => $value_callback, + 'edit_callback' => $edit_callback, %opt, &> <%init> @@ -28,6 +29,9 @@ my $right = $opt{'bulk'} ? 'Bulk provision customer service' die "access denied" unless $FS::CurrentUser::CurrentUser->access_right($right); +$cgi->param('phonenum', $cgi->param('phonenum_manual') ) + if $cgi->param('phonenum_which') eq 'phonenum_manual'; + my $tollfreephonenum = $cgi->param('tollfreephonenum'); $cgi->param('phonenum',$tollfreephonenum) if $tollfreephonenum =~ /^\d+$/; @@ -36,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({ map { $_ => scalar($cgi->param($_)) } qw( custnum address1 address2 city county state zip country ) - }; + }); $opt{'cust_location'} = $cust_location; } @@ -48,8 +52,13 @@ my $args_callback = sub { }; my $value_callback = sub { - my ($field, $value) = @_; - ($field =~ /_date$/) ? parse_datetime($value) : $value; + my ($field, $value) = @_; + ($field =~ /_date$/) ? parse_datetime($value) : $value; +}; + +my $edit_callback = sub { + my( $new, $old ) = @_; + $new->sip_password( $old->sip_password ) if $new->sip_password eq '*HIDDEN*'; }; </%init> diff --git a/httemplate/edit/process/upload_target.html b/httemplate/edit/process/upload_target.html new file mode 100644 index 000000000..8755bed56 --- /dev/null +++ b/httemplate/edit/process/upload_target.html @@ -0,0 +1,25 @@ +<& elements/process.html, + 'table' => 'upload_target', + 'viewall_dir' => 'browse', + 'agent_null' => 1, + 'precheck_callback'=> \&precheck, +&> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Configuration'); + +sub precheck { + my $cgi = shift; + my $protocol = $cgi->param('protocol'); + # promote whatever set of fields was selected to the "real" values + my $params = $cgi->Vars; + foreach ( keys %$params ) { + if ( $_ =~ /^${protocol}_(\w+)/ ) { + $cgi->param($1, $cgi->param($_)); + } + } +} + +</%init> diff --git a/httemplate/edit/quick-charge.html b/httemplate/edit/quick-charge.html index 1d9647f2f..466091dfa 100644 --- a/httemplate/edit/quick-charge.html +++ b/httemplate/edit/quick-charge.html @@ -145,7 +145,6 @@ function bill_now_changed (what) { <% mt('with terms') |h %> <& /elements/select-terms.html, 'curr_value' => scalar($cgi->param('invoice_terms')), - 'empty_value' => $default_terms, 'disabled' => ( $cgi->param('bill_now') ? 0 : 1 ), &> </TD> diff --git a/httemplate/edit/rate_region.cgi b/httemplate/edit/rate_region.cgi index 367bbafb6..a1c1bcb7d 100644 --- a/httemplate/edit/rate_region.cgi +++ b/httemplate/edit/rate_region.cgi @@ -33,6 +33,14 @@ </TD> </TR> + <& /elements/tr-checkbox.html, + label => 'Exact match', + field => 'exact_match', + cell_style => 'font-weight: bold', + value => 'Y', + curr_value => $rate_region->exact_match + &> + </TABLE> <BR> diff --git a/httemplate/edit/rate_time.cgi b/httemplate/edit/rate_time.cgi index 7ee39efca..9e6b8736c 100644 --- a/httemplate/edit/rate_time.cgi +++ b/httemplate/edit/rate_time.cgi @@ -15,12 +15,34 @@ <TD><INPUT TYPE="text" NAME="ratetimename" VALUE="<% $rate_time ? $rate_time->ratetimename : '' %>"></TD> </TR> </TABLE> -<% include('/elements/auto-table.html', - 'header' => [ '', 'Start','','', '','End','','' ], - 'fields' => [ qw(sd sh sm sa ed eh em ea) ], - 'select' => [ ($day, $hour, $min, $ampm) x 2 ], - 'data' => \@data, - ) %> +<TABLE> + <TR> + <TH COLSPAN=4 ALIGN="center">Start</TH> + <TH COLSPAN=4 ALIGN="center">End</TH> + </TR> + <TR id="mytemplate"> +% for my $pre (qw(s e)) { +% for my $f (qw(d h m a)) { # day, hour, minute, am/pm + <TD> + <SELECT NAME="<%$pre.$f%>"> +% my $i = 0; +% while ($i < @{ $choices{$f} }) { + <OPTION VALUE="<%$choices{$f}[$i]%>"> +% $i++; + <%$choices{$f}[$i]%></OPTION> +% $i++; +% } + </SELECT> + </TD> +% } #$f +% } #$pre + </TR> +<& /elements/auto-table.html, + 'template_row' => 'mytemplate', + 'data' => \@data, + 'fieldorder' => [qw(sd sh sm sa ed eh em ea)], +&> +</TABLE> <INPUT TYPE="submit" VALUE="<% $rate_time ? 'Apply changes' : 'Add period'%>"> </FORM> <BR> @@ -42,7 +64,12 @@ my $day = [ 0 => 'Sun', my $hour = [ map( {$_, sprintf('%02d',$_) } 12, 1..11 )]; my $min = [ map( {$_, sprintf('%02d',$_) } 0,30 )]; my $ampm = [ 0 => 'AM', 1 => 'PM' ]; - +my %choices = ( + 'd' => $day, + 'h' => $hour, + 'm' => $min, + 'a' => $ampm, +); if($ratetimenum) { $action = 'Edit'; $rate_time = qsearchs('rate_time', {ratetimenum => $ratetimenum}) diff --git a/httemplate/edit/router.cgi b/httemplate/edit/router.cgi index fdcd7b3b3..0df9b457e 100755 --- a/httemplate/edit/router.cgi +++ b/httemplate/edit/router.cgi @@ -29,8 +29,15 @@ die "access denied" unless $curuser->access_right('Broadband configuration') || $curuser->access_right('Broadband global configuration'); +my @svc_x = 'svc_broadband'; +if ( FS::Conf->new->exists('svc_acct-ip_addr') ) { + push @svc_x, 'svc_acct'; +} + my $callback = sub { my ($cgi, $object, $fields) = (shift, shift, shift); + + my $extra_sql = ' AND svcdb IN(' . join(',', map { "'$_'" } @svc_x) . ')'; unless ($object->svcnum) { push @{$fields}, { 'type' => 'tablebreak-tr-title', @@ -41,7 +48,8 @@ my $callback = sub { 'target_table' => 'part_svc', 'link_table' => 'part_svc_router', 'name_col' => 'svc', - 'hashref' => { 'svcdb' => 'svc_broadband', 'disabled' => '' }, + 'hashref' => { 'disabled' => '' }, + 'extra_sql' => $extra_sql, }; } }; diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi index 142c11150..574fb51eb 100755 --- a/httemplate/edit/svc_acct.cgi +++ b/httemplate/edit/svc_acct.cgi @@ -9,19 +9,6 @@ <BR> % } -<SCRIPT TYPE="text/javascript"> -function randomPass() { - var i=0; - var pw_set='<% join('', 'a'..'z', 'A'..'Z', '0'..'9' ) %>'; - var pass=''; - while(i < 8) { - i++; - pass += pw_set.charAt(Math.floor(Math.random() * pw_set.length)); - } - document.OneTrueForm.clear_password.value = pass; -} -</SCRIPT> - <FORM NAME="OneTrueForm" ACTION="<% $p1 %>process/svc_acct.cgi" METHOD=POST> <INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>"> <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> @@ -57,10 +44,11 @@ function randomPass() { %if ( $part_svc->part_svc_column('_password')->columnflag ne 'F' ) { <TR> +% #XXX eventually should require "Edit Password" ACL <TD ALIGN="right"><% mt('Password') |h %></TD> <TD> - <INPUT TYPE="text" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>> - <INPUT TYPE="button" VALUE="<% mt('Generate') |h %>" onclick="randomPass();"> + <INPUT TYPE="text" ID="clear_password" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>> + <& /elements/random_pass.html, 'clear_password' &> </TD> </TR> %}else{ @@ -276,14 +264,26 @@ function randomPass() { 'communigate' => $communigate, &> -% if ( $part_svc->part_svc_column('slipip')->columnflag =~ /^[FA]$/ ) { - <INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>"> -% } else { - <TR> - <TD ALIGN="right"><% mt('IP') |h %></TD> - <TD><INPUT TYPE="text" NAME="slipip" VALUE="<% $svc_acct->slipip %>"></TD> - </TR> -% } +% if ( $conf->exists('svc_acct-ip_addr') ) { +% # router/block selection UI +% # (should we show this if slipip is fixed?) +<& /elements/tr-select-router_block_ip.html, + 'object' => $svc_acct, + 'ip_field' => 'slipip' +&> +% } else { +% # don't expose these to the user--they're only useful in the other case + <INPUT TYPE="hidden" NAME="routernum" VALUE="<% $svc_acct->routernum %>"> + <INPUT TYPE="hidden" NAME="blocknum" VALUE="<% $svc_acct->blocknum %>"> +% if ( $part_svc->part_svc_column('slipip')->columnflag =~ /^[FA]$/ ) { + <INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>"> +% } else { + <TR> + <TD ALIGN="right"><% mt('IP') |h %></TD> + <TD><INPUT TYPE="text" NAME="slipip" VALUE="<% $svc_acct->slipip %>"></TD> + </TR> +% } +% } % my %label = ( seconds => 'Time', % upbytes => 'Upload bytes', @@ -482,8 +482,6 @@ my $action = $svcnum ? 'Edit' : 'Add'; my $svc = $part_svc->getfield('svc'); -my $otaker = getotaker; - my $username = $svc_acct->username; my $password = ''; 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/edit/svc_cert.cgi b/httemplate/edit/svc_cert.cgi index 93194228e..dc2cc3200 100644 --- a/httemplate/edit/svc_cert.cgi +++ b/httemplate/edit/svc_cert.cgi @@ -185,8 +185,6 @@ my $action = $svcnum ? 'Edit' : 'Add'; my $svc = $part_svc->getfield('svc'); -#my $otaker = getotaker; - my $p1 = popurl(1); my $link_query = "?svcnum=$svcnum;pkgnum=$pkgnum;svcpart=$svcpart"; diff --git a/httemplate/edit/svc_domain.cgi b/httemplate/edit/svc_domain.cgi index c3307fa8c..417b1b4c5 100755 --- a/httemplate/edit/svc_domain.cgi +++ b/httemplate/edit/svc_domain.cgi @@ -148,8 +148,6 @@ my $export = $exports[0]; # If we have a domain registration export, get the registrar object my $registrar = $export ? $export->registrar : ''; -my $otaker = getotaker; - my $domain = $svc_domain->domain; my $p1 = popurl(1); diff --git a/httemplate/edit/svc_phone.cgi b/httemplate/edit/svc_phone.cgi index 9647b6887..d48e6353b 100644 --- a/httemplate/edit/svc_phone.cgi +++ b/httemplate/edit/svc_phone.cgi @@ -6,6 +6,11 @@ my( $cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt ) = @_; $svc_x->locationnum($cust_pkg->locationnum) if $cust_pkg; }, + 'svc_edit_callback' => sub { + my( $cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt) = @_; + my $conf = new FS::Conf; + $svc_x->sip_password('*HIDDEN*') unless $conf->exists('showpasswords'); + }, &> <%init> @@ -28,6 +33,11 @@ my $begin_callback = sub { type => 'select-did', label => 'Phone number', multiple => $bulk, + }, + { field => 'sim_imsi', + type => 'text', + size => 15, + maxlength => 15, }; push @$fields, { field => 'domsvc', @@ -111,6 +121,25 @@ my $begin_callback = sub { ; } + if ( ! $bulk ) { + + push @$fields, + { + type => 'tablebreak-tr-title', + value => 'Carrier Information', + colspan => 8, + }, + { field => 'sms_carrierid', + label => 'SMS Carrier', + type => 'select-cdr_carrier', + }, + 'sms_account', + 'max_simultaneous', + ; + + } + }; + </%init> diff --git a/httemplate/edit/upload_target.html b/httemplate/edit/upload_target.html new file mode 100755 index 000000000..47fea78a0 --- /dev/null +++ b/httemplate/edit/upload_target.html @@ -0,0 +1,82 @@ +<& elements/edit.html, + 'post_url' => popurl(1).'process/upload_target.html', + 'name' => 'Upload target', + 'table' => 'upload_target', + 'viewall_url' => "${p}browse/upload_target.html", + 'labels' => { targetnum => 'Target', + protocol => 'Protocol', + handling => 'Special handling', + }, + 'fields' => [ + { field => 'protocol', + type => 'selectlayers', + options => [ '', 'sftp', 'ftp', 'email' ], + labels => { '' => '', + 'email' => 'Email', + 'sftp' => 'SFTP', + 'ftp' => 'FTP', + }, + layer_fields => \%protocol_fields, + layer_values_callback => \&values_callback, + }, + { field => 'handling', + type => 'select', + options => [ FS::upload_target->handling_types ], + }, + ], + 'menubar' => \@menubar, + 'edit_callback' => $edit_callback, +&> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Configuration'); + +my @menubar = ('View all FTP targets' => $p.'browse/upload_target.html'); +my $edit_callback = sub { + my ($cgi, $object) = @_; + if ( $object->targetnum ) { + push @menubar, 'Delete this target', + $p.'misc/delete-upload_target.html?'.$object->targetnum; + } +}; + +my %protocol_fields = ( + '' => [], + 'sftp' => [ + 'hostname' => { label => 'Server' }, + 'username' => { label => 'Username' }, + 'password' => { label => 'Password' }, + 'port' => { label => 'Port', size => 8 }, + 'path' => { label => 'Path', size => 30 }, + ], + 'email' => [ + 'username' => { label => 'To:' }, + 'hostname' => { label => '@' }, + 'subject' => { label => 'Subject:' }, + ], +); +$protocol_fields{'ftp'} = [ @{ $protocol_fields{'sftp'} } ]; +foreach my $k (keys %protocol_fields) { + # disambiguate the field names + foreach (@{ $protocol_fields{$k} }) { + $_ = $k.'_'.$_ unless ref $_; + } +} + +sub values_callback { + my ($cgi, $object) = @_; + my $layer_values; + # really simple, the interpretation of the fields is the same for all + # three layers + foreach my $l (qw(email ftp sftp)) { + $layer_values->{$l} = { map { $l.'_'.$_ => ($cgi->param($l.'_'.$_) || + $object->get($_) ) } + $object->fields }; + } + $layer_values; +} + +</%init> |