diff options
Diffstat (limited to 'httemplate/edit')
184 files changed, 16836 insertions, 0 deletions
diff --git a/httemplate/edit/REAL_cust_pkg.cgi b/httemplate/edit/REAL_cust_pkg.cgi new file mode 100755 index 000000000..77ab1fe67 --- /dev/null +++ b/httemplate/edit/REAL_cust_pkg.cgi @@ -0,0 +1,220 @@ +<% 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> + +% 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 &> + <& .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=>'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="Apply Changes"> +</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{'_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 adjourn expire )) { + 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_group.html b/httemplate/edit/access_group.html new file mode 100644 index 000000000..1eed26dfe --- /dev/null +++ b/httemplate/edit/access_group.html @@ -0,0 +1,80 @@ +<% include( 'elements/edit.html', + 'name' => 'Employee Group', + 'table' => 'access_group', + 'labels' => { + 'groupnum' => 'Group number', + 'groupname' => 'Group name', + }, + + 'viewall_dir' => 'browse', + + 'html_bottom' => $html_bottom_sub, + ) +%> +<%once> + +tie my %rights, 'Tie::IxHash', FS::AccessRight->rights_info; + +</%once> +<%init> + +my $html_bottom_sub = sub { + my $access_group = shift; + + #some false laziness w/browse/access_group.html + my $columns = 3; + my $count = 0; + + '<BR>'. + '<FONT SIZE="+1">Group limited to these agent(s)</FONT><BR>'. + 'Employees in this group will only see customers of the selected agents in the system and reports.<BR>'. + ntable("#cccccc",2). + '<TR><TD>'. + include( '/elements/checkboxes-table.html', + 'source_obj' => $access_group, + 'link_table' => 'access_groupagent', + 'target_table' => 'agent', + 'name_col' => 'agent', + 'target_link' => $p.'edit/agent.cgi?', + 'disable-able' => 1, + ). + '</TD></TR></TABLE>'. + + '<BR><FONT SIZE="+1">Group access rights</FONT><BR>'. + include('/elements/table-grid.html', bgcolor=>'#cccccc' ). + '<TR>'. join( '', map { + '<TD CLASS="inv" VALIGN="top"><TABLE BGCOLOR="#cccccc" WIDTH=100%>'. + '<TR><TH BGCOLOR="#dcdcdc">'. $_. '</TH></TR>'. + '<TR><TD>'. + include( '/elements/checkboxes-table-name.html', + 'source_obj' => $access_group, + 'link_table' => 'access_right', + 'link_static' => { 'righttype' => + 'FS::access_group', + }, + 'num_col' => 'rightobjnum', + 'name_col' => 'rightname', + 'names_list' => [ map { + my $rn = + ref($_) ? $_->{'rightname'} : $_; + my %hash = (); + $hash{'note'} = ' *' + if ref($_) && $_->{'global'}; + $hash{'desc'} = $_->{'desc'} + if ref($_) && $_->{'desc'}; + [ $rn => \%hash ]; + } + @{ $rights{$_} } + ], + ). + '<BR>'. + '</TD></TR></TABLE></TD>'. + ( ++$count % $columns ? '' : '</TR><TR>') + + } keys %rights ). '</TR></TABLE>'. + + '* Global rights. These rights provide access to global data which is shared among all agents. Their use is not recommended for groups which are limited to a subset of agents.<BR>'; + +}; + +</%init> diff --git a/httemplate/edit/access_user.html b/httemplate/edit/access_user.html new file mode 100644 index 000000000..22cf89621 --- /dev/null +++ b/httemplate/edit/access_user.html @@ -0,0 +1,65 @@ +<% include( 'elements/edit.html', + 'name' => 'Employee', + 'table' => 'access_user', + 'fields' => [ + 'username', + { field=>'_password', type=>'password' }, + { field=>'_password2', type=>'password' }, + 'last', + 'first', + { field=>'user_custnum', type=>'search-cust_main', }, + { field=>'disabled', type=>'checkbox', value=>'Y' }, + ], + 'labels' => { + 'usernum' => 'User number', + 'username' => 'Username', + '_password' => 'Password', + '_password2' => 'Re-enter Password', + 'last' => 'Last name', + 'first' => 'First name', + 'user_custnum' => 'Customer (optional)', + 'disabled' => 'Disable employee', + }, + 'edit_callback' => sub { my( $c, $o ) = @_; + $o->set('_password', ''); + }, + 'viewall_dir' => 'browse', + 'html_bottom' => + sub { + my $access_user = shift; + + '<BR>Employee Groups<BR>'. + ntable("#cccccc",2). + '<TR><TD>'. + include( '/elements/checkboxes-table.html', + 'source_obj' => $access_user, + 'link_table' => 'access_usergroup', + 'target_table' => 'access_group', + 'name_col' => 'groupname', + 'target_link' => $p.'edit/access_group.html?', + #'disable-able' => 1, + ). + '</TR></TD></TABLE>' + ; + }, + 'onsubmit' => 'check_user_custnum_search', + 'html_foot' => $check_user_custnum_search, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $check_user_custnum_search = <<END; + <SCRIPT TYPE="text/javascript"> + function check_user_custnum_search(what) { + while ( user_custnum_search_active ) { + // javascript needs ambien + } + return true; + } + </SCRIPT> +END + +</%init> diff --git a/httemplate/edit/agent.cgi b/httemplate/edit/agent.cgi new file mode 100755 index 000000000..30a0865d3 --- /dev/null +++ b/httemplate/edit/agent.cgi @@ -0,0 +1,144 @@ +<% include("/elements/header.html","$action Agent", menubar( + 'View all agents' => $p. 'browse/agent.cgi', +)) %> + +<% include('/elements/error.html') %> + +<FORM METHOD = POST + ACTION = "<%popurl(1)%>process/agent.cgi" + onSubmit = "return check_agent_custnum_search(this)" +> + +<SCRIPT TYPE="text/javascript"> + function check_agent_custnum_search(what) { + while ( agent_custnum_search_active ) { + // javascript needs ambien + } + return true; + } +</SCRIPT> + +<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>"> +Agent #<% $agent->agentnum ? $agent->agentnum : "(NEW)" %> + +<% &ntable("#cccccc", 2, '') %> + + <TR> + <TH ALIGN="right">Agent</TH> + <TD><INPUT TYPE="text" NAME="agent" SIZE=32 VALUE="<% $agent->agent %>"></TD> + </TR> + + <TR> + <TH ALIGN="right">Agent type</TH> + <TD> + <SELECT NAME="typenum" SIZE=1> +% foreach my $agent_type (qsearch('agent_type',{})) { + + <OPTION VALUE="<% $agent_type->typenum %>"<% ( $agent->typenum && ( $agent->typenum == $agent_type->typenum ) ) ? ' SELECTED' : '' %>> + <% $agent_type->getfield('typenum') %>: <% $agent_type->getfield('atype') %> +% } + + </SELECT> + </TD> + </TR> + + <TR> + <TH ALIGN="right">Master customer</TH> + <TD> + <% include('/elements/search-cust_main.html', + 'field_name' => 'agent_custnum', + 'curr_value' => $agent->agent_custnum, + 'find_button' => 1, + ) + %> + </TD> + </TR> + + <TR> + <TD ALIGN="right">Disable</TD> + <TD><INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $agent->disabled eq 'Y' ? ' CHECKED' : '' %>></TD> + </TR> + +% if ( $conf->exists('agent-invoice_template') ) { + + <% include('/elements/tr-select-invoice_template.html', + 'label' => 'Invoice template', + 'field' => 'invoice_template', + 'curr_value' => $agent->invoice_template, + ) + %> + +% } else { + + <INPUT TYPE="hidden" NAME="invoice_template" VALUE="<% $agent->invoice_template %>"> + +% } + +% if ( $conf->config('ticket_system') ) { +% my $default_queueid = $conf->config('ticket_system-default_queueid'); +% my $default_queue = FS::TicketSystem->queue($default_queueid); +% $default_queue = "(default) $default_queueid: $default_queue" +% if $default_queueid; +% my %queues = FS::TicketSystem->queues(); +% my @queueids = sort { $a <=> $b } keys %queues; +% + + <TR> + <TD ALIGN="right">Ticketing queue</TD> + <TD> + <SELECT NAME="ticketing_queueid"> + <OPTION VALUE=""><% $default_queue %> +% foreach my $queueid ( @queueids ) { + + <OPTION VALUE="<% $queueid %>" <% $agent->ticketing_queueid == $queueid ? ' SELECTED' : '' %>><% $queueid %>: <% $queues{$queueid} %> +% } + + </SELECT> + </TD> + </TR> +% } + + <TR> + <TD ALIGN="right">Access Groups</TD> + <TD><% include('/elements/checkboxes-table.html', + 'source_obj' => $agent, + 'link_table' => 'access_groupagent', + 'target_table' => 'access_group', + 'name_col' => 'groupname', + 'target_link' => $p. 'edit/access_group.html?', + ) + %> + </TD> + </TR> + +</TABLE> + +<BR> +<INPUT TYPE="submit" VALUE="<% $agent->agentnum ? "Apply changes" : "Add agent" %>"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $agent; +if ( $cgi->param('error') ) { + $agent = new FS::agent ( { + map { $_, scalar($cgi->param($_)) } fields('agent') + } ); +} elsif ( $cgi->keywords ) { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/; + $agent = qsearchs( 'agent', { 'agentnum' => $1 } ); +} else { #adding + $agent = new FS::agent {}; +} +my $action = $agent->agentnum ? 'Edit' : 'Add'; + +my $conf = new FS::Conf; + +</%init> diff --git a/httemplate/edit/agent_payment_gateway.html b/httemplate/edit/agent_payment_gateway.html new file mode 100644 index 000000000..4a7cedf79 --- /dev/null +++ b/httemplate/edit/agent_payment_gateway.html @@ -0,0 +1,68 @@ +<% include("/elements/header.html","$action payment gateway override for ". $agent->agent, menubar( + #'View all payment gateways' => $p. 'browse/payment_gateway.html', + 'View all agents' => $p. 'browse/agent.html', +)) %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%popurl(1)%>process/agent_payment_gateway.html" METHOD=POST> +<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>"> + +Use gateway <SELECT NAME="gatewaynum"> +% foreach my $payment_gateway ( +% qsearch('payment_gateway', { 'disabled' => '' } ) +% ) { +% + + <OPTION VALUE="<% $payment_gateway->gatewaynum %>"><% $payment_gateway->gateway_module %> (<% $payment_gateway->gateway_username %>) +% } + +</SELECT> +<BR><BR> + +for <SELECT NAME="cardtype" MULTIPLE> +% foreach my $cardtype ( +% "", +% "VISA card", +% "MasterCard", +% "Discover card", +% "American Express card", +% "Diner's Club/Carte Blanche", +% "enRoute", +% "JCB", +% "BankCard", +% "Switch", +% "Solo", +% 'ACH', +%) { + + <OPTION VALUE="<% $cardtype %>"><% $cardtype || '(Default fallback)' %> +% } + +</SELECT> +<BR><BR> + +(optional) when invoice contains only items of taxclass <INPUT TYPE="text" NAME="taxclass"> +<BR><BR> + +<INPUT TYPE="submit" VALUE="Add gateway override"> +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +$cgi->param('agentnum') =~ /(\d+)$/ or die "illegal agentnum"; +my $agent = qsearchs('agent', { 'agentnum' => $1 } ); +die "agentnum $1 not found" unless $agent; + +#my @agent_payment_gateway; +if ( $cgi->param('error') ) { +} + +my $action = 'Add'; + +</%init> diff --git a/httemplate/edit/agent_type.cgi b/httemplate/edit/agent_type.cgi new file mode 100755 index 000000000..8a6fbc255 --- /dev/null +++ b/httemplate/edit/agent_type.cgi @@ -0,0 +1,57 @@ +<% include("/elements/header.html","$action Agent Type", menubar( + 'View all agent types' => "${p}browse/agent_type.cgi", +)) +%> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% popurl(1) %>process/agent_type.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="typenum" VALUE="<% $agent_type->typenum %>"> +Agent Type #<% $agent_type->typenum || "(NEW)" %> +<BR> + +Agent Type +<INPUT TYPE="text" NAME="atype" SIZE=32 VALUE="<% $agent_type->atype %>"> +<BR><BR> + +Select which packages agents of this type may sell to customers<BR> +<% ntable("#cccccc", 2) %><TR><TD> +<% include('/elements/checkboxes-table.html', + 'source_obj' => $agent_type, + 'link_table' => 'type_pkgs', + 'target_table' => 'part_pkg', + 'name_callback' => sub { $_[0]->pkg_comment(nopkgpart => 1); }, + 'target_link' => $p.'edit/part_pkg.cgi?', + 'disable-able' => 1, + + ) +%> +</TD></TR></TABLE> +<BR> + +<INPUT TYPE="submit" VALUE="<% $agent_type->typenum ? "Apply changes" : "Add agent type" %>"> + + </FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my($agent_type); +if ( $cgi->param('error') ) { + $agent_type = new FS::agent_type ( { + map { $_, scalar($cgi->param($_)) } fields('agent') + } ); +} elsif ( $cgi->keywords ) { #editing + my( $query ) = $cgi->keywords; + $query =~ /^(\d+)$/; + $agent_type=qsearchs('agent_type',{'typenum'=>$1}); +} else { #adding + $agent_type = new FS::agent_type {}; +} +my $action = $agent_type->typenum ? 'Edit' : 'Add'; + +</%init> diff --git a/httemplate/edit/allocate.html b/httemplate/edit/allocate.html new file mode 100644 index 000000000..8d1347df2 --- /dev/null +++ b/httemplate/edit/allocate.html @@ -0,0 +1,33 @@ +<% include('elements/edit.html', + 'name' => 'Allocation', + 'table' => 'addr_block', + 'labels' => { 'NetAddr' => 'Block', + 'routernum' => 'Router', + }, + 'fields' => [ { 'field' => 'NetAddr', + 'type' => 'fixed', + }, + { 'field' => 'routernum', + 'type' => 'select-table', + 'table' => 'router', + 'name_col' => 'routername', + 'disable_empty' => 1, + 'agent_virt' => 1, + 'agent_null_right' => + 'Broadband global configuration', + }, + ], + 'post_url' => "process/addr_block/allocate.cgi", + 'popup' => 1, + 'agent_virt' => 1, + 'agent_null_right' => 'Broadband global configuration', + ) +%> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +</%init> diff --git a/httemplate/edit/bulk-cust_main_county.html b/httemplate/edit/bulk-cust_main_county.html new file mode 100644 index 000000000..8e447e54f --- /dev/null +++ b/httemplate/edit/bulk-cust_main_county.html @@ -0,0 +1,128 @@ +<% include('/elements/header-popup.html', $title ) %> + +<FORM ACTION="<% popurl(1)."process/bulk-cust_main_county.html" %>" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="action" VALUE="<% $action %>"> +<INPUT TYPE="hidden" NAME="taxnum" VALUE="<% join(',', @taxnum) %>"> + +<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + +<% include('/elements/tr-td-label.html', 'label' => 'Country' ) %> + <TD BGCOLOR="#dddddd"><% $countries %> + </TD> +</TR> + +<% include('/elements/tr-td-label.html', 'label' => 'State' ) %> + <TD BGCOLOR="#dddddd"><% $states %> + </TD> +</TR> + +% if ( $counties ) { + <% include('/elements/tr-td-label.html', 'label' => 'County' ) %> + <TD BGCOLOR="#dddddd"><% $counties %> + </TD> + </TR> +% } + +% if ( $conf->exists('enable_taxclasses') && $taxclasses ) { + <% include('/elements/tr-td-label.html', 'label' => 'Tax Class' ) %> + <TD BGCOLOR="#dddddd"><% $taxclasses %> + </TD> + </TR> +% } + +<% include('/elements/tr-input-text.html', + 'field' => 'taxname', + 'label' => 'Tax name' + ) +%> + +<% include('/elements/tr-input-percentage.html', + 'field' => 'tax', + 'label' => 'Tax rate', + ) +%> + +<% include('/elements/tablebreak-tr-title.html', value=>'Exemptions' ) %> + +<% include('/elements/tr-checkbox.html', + 'field' => 'setuptax', + 'value' => 'Y', + 'label' => 'This tax not applicable to setup fees', + ) +%> + +<% include('/elements/tr-checkbox.html', + 'field' => 'recurtax', + 'value' => 'Y', + 'label' => 'This tax not applicable to recurring fees', + ) +%> + +<% include('/elements/tr-input-money.html', + 'field' => 'exempt_amount', + 'label' => 'Monthly exemption per customer ($25 "Texas tax")', + ) +%> + +</TABLE> + +<BR> + +<INPUT TYPE="submit" VALUE="Bulk <% $action %> tax"> + +<%init> + +my $conf = new FS::Conf; + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my @taxnum; +$cgi->param('taxnum') =~ /^([\d,]+)$/ + or $m->comp('/elements/errorpage-popup.html', $cgi->param('error') || 'Nothing selected'); +my @taxnum = split(',', $1); + +$cgi->param('action') =~ /^(add|edit)$/ or die "unknown action"; +my $action = $1; +my $title = "Bulk $action tax rate"; + +my @cust_main_county = + map { + qsearchs('cust_main_county', { 'taxnum' => $_ }) + or die "unknown taxnum $1"; + } + @taxnum; + +my %seen_country = {}; +my @countries = map code2country($_)." ($_)", + grep !$seen_country{$_}++, + map $_->country, + @cust_main_county; +my $countries = join(', ', @countries); + +my %seen_state = {}; +my @states = map state_label($_->[0], $_->[1]), + grep !$seen_state{$_->[0]}++, + map [ $_->state, $_->country ], + @cust_main_county; +my $states = join(', ', @states); + +my %seen_county = {}; +my @counties = grep !$seen_county{$_}++, map $_->county, @cust_main_county; +my $counties = join(', ', @counties); + +my %seen_taxclass = {}; +my @taxclasses = grep !$seen_taxclass{$_}++, map $_->taxclass, @cust_main_county; +my $taxclasses = join(', ', @taxclasses); + +#my @fields = ( +# { field=>'country', type=>'fixed-country', }, +# { field=>'state', type=>'fixed-state', }, +# { field=>'county', type=>'fixed', }, +#); + +#push @fields, { field=>'taxclass', type=>'fixed', } +# if $conf->exists('enable_taxclasses'); + +</%init> diff --git a/httemplate/edit/bulk-cust_svc.html b/httemplate/edit/bulk-cust_svc.html new file mode 100644 index 000000000..a3c21b112 --- /dev/null +++ b/httemplate/edit/bulk-cust_svc.html @@ -0,0 +1,95 @@ +<% include('/elements/header.html', 'Bulk customer service change') %> + +<% include('/elements/init_overlib.html') %> + +<% include('/elements/progress-init.html', + 'OneTrueForm', + [qw( old_svcpart new_svcpart pkgpart )], + 'process/bulk-cust_svc.cgi', + $p.'browse/part_svc.cgi', + ) +%> + +<FORM NAME="OneTrueForm"> +% +% $cgi->param('svcpart') =~ /^(\d+)$/ +% or die "illegal svcpart: ". $cgi->param('svcpart'); +% +% my $old_svcpart = $1; +% my $src_part_svc = qsearchs('part_svc', { 'svcpart' => $old_svcpart } ) +% or die "unknown svcpart: $old_svcpart"; +% + + +<INPUT NAME="old_svcpart" TYPE="hidden" VALUE="<% $old_svcpart %>"> +Change <!-- customer +<B><% $src_part_svc->svcpart %>: <% $src_part_svc->svc %></B> services +<BR> +--> + +<SELECT NAME="pkgpart"> +% my $num_cust_svc = $src_part_svc->num_cust_svc; +% if ( $num_cust_svc > 1 ) { + + <OPTION VALUE="">all <% $num_cust_svc %> <% $src_part_svc->svc %> services +% } else { + + <OPTION VALUE="">the <% $num_cust_svc %> <% $src_part_svc->svc %> service +% } +% +% my $num_unlinked = $src_part_svc->num_cust_svc(0); +% if ( $num_unlinked ) { +% + + <OPTION VALUE="0">the <% $num_unlinked %> unlinked <% $src_part_svc->svc %> services +% } +% foreach my $schwartz ( +% grep { $_->[1] } +% map { [ $_, $src_part_svc->num_cust_svc($_->pkgpart) ] } +% qsearch('part_pkg', {} ) +% ) { +% my( $part_pkg, $num_cust_svc ) = @$schwartz; +% + + <OPTION VALUE="<% $part_pkg->pkgpart %>">the <% $num_cust_svc %> + <% $src_part_svc->svc %> service<% $num_cust_svc > 1 ? 's in' : ' in a' %> + <% $part_pkg->pkg %> package<% $num_cust_svc > 1 ? 's' : '' %> +% } + +</SELECT> +<BR> + +to new service definition +<SELECT NAME="new_svcpart"> +% foreach my $dest_part_svc ( +% grep { $_->svcpart != $old_svcpart +% && $_->svcdb eq $src_part_svc->svcdb +% } +% qsearch('part_svc', { 'disabled' => '' } ) +% ) { +% + + <OPTION VALUE="<% $dest_part_svc->svcpart %>"><% $dest_part_svc->svcpart %>: <% $dest_part_svc->svc %> +% } + +</SELECT> +<BR> + +<BR> + +<SCRIPT TYPE="text/javascript"> +var confirm_change = '<P ALIGN="center"><B>Bulk customer service change - Are you sure?</B><BR><P ALIGN="CENTER" <INPUT TYPE="button" VALUE="Yes, make changes" onClick="process();"> <INPUT TYPE="BUTTON" VALUE="Cancel" onClick="cClick()">'; +</SCRIPT> + +<INPUT TYPE="button" VALUE="Bulk change customer services" onClick="overlib(confirm_change, CAPTION, 'Confirm bulk customer service change', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 128, TEXTSIZE, 3, BGCOLOR, '#ff0000', CGCOLOR, '#ff0000' );"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/cgp_rule-redirect_all.html b/httemplate/edit/cgp_rule-redirect_all.html new file mode 100644 index 000000000..898eef8fc --- /dev/null +++ b/httemplate/edit/cgp_rule-redirect_all.html @@ -0,0 +1,57 @@ +<% include('/elements/header-popup.html', 'Redirect all mail') %> + +<FORM NAME="RedirectAllForm" ACTION="process/cgp_rule-redirect_all.html" METHOD=POST> + +%# XXX upstream Redirect 1 + +<% ntable("#cccccc", 2) %> + +<TR> + <TD ALIGN="right">Redirect all mail to</TD> + <TD><textarea name="RedirectText" rows="5" cols="50"></textarea></TD> +</TR> + +<% include('/elements/tr-checkbox.html', + 'name' => 'RedirKeep', + 'label' => 'Keep a copy', + 'value' => 1, + 'curr_value' => '', #XXX + ) +%> + +<% include('/elements/tr-checkbox.html', + 'name' => 'RedirHuman', + 'label' => 'Do not redirect automatic messages', + 'value' => 1, + 'curr_value' => '', #XXX + ) +%> + +<% include('/elements/tr-checkbox.html', + 'name' => 'KeepToAndCc', + 'label' => 'Preserve To/Cc fields', + 'value' => 1, + 'curr_value' => '', #XXX + ) +%> + +</TABLE> + +<BR> +<INPUT TYPE="submit" VALUE="Redirect all mail"> +%#XXX Add/Edit + +</FORM> + +</BODY> +</HTML> +<%init> + +my %opt = @_; + +my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } ) + or die "unknown svcnum"; + +#XXX look for existing redirect all rule + +</%init> diff --git a/httemplate/edit/cgp_rule-vacation.html b/httemplate/edit/cgp_rule-vacation.html new file mode 100644 index 000000000..efdc5417e --- /dev/null +++ b/httemplate/edit/cgp_rule-vacation.html @@ -0,0 +1,47 @@ +<% include('/elements/header-popup.html', 'Vacation rule') %> + +<FORM NAME="VacationForm" ACTION="process/cgp_rule-vacation.html" METHOD=POST> + +%# XXX upstream Vacation 1 + +<% ntable("#cccccc", 2) %> + +<TR> + <TD ALIGN="right">Vacation message</TD> + <TD><textarea name="VacationText" rows="5" cols="50"></textarea></TD> +</TR> + +<% include('/elements/tr-input-date-field.html', { + 'label' => 'Ends', + 'name' => 'vacationTill', + 'value' => '', #XXX + }) +%> +%# XXX upstream: +%# VacationTill 1 +%# vacationDay +%# vacationMonth +%# vacationYear + +%#XXX Clear 'Replied Addresses' List + +</TABLE> + +<BR> +<INPUT TYPE="submit" VALUE="Add vacation message"> +%#XXX Add/Edit + +</FORM> + +</BODY> +</HTML> +<%init> + +my %opt = @_; + +my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } ) + or die "unknown svcnum"; + +#XXX look for existing vacation rule + +</%init> diff --git a/httemplate/edit/cgp_rule.html b/httemplate/edit/cgp_rule.html new file mode 100644 index 000000000..41275aba7 --- /dev/null +++ b/httemplate/edit/cgp_rule.html @@ -0,0 +1,102 @@ +<% include('elements/edit.html', + 'name_singular' => 'rule', + 'table' => 'cgp_rule', + 'labels' => { 'rulenum' => 'Rule', + 'name' => 'Name', + 'comment' => 'Comment', + 'priority' => 'Priority', + 'ruleconditionnum' => 'Condition', + 'ruleactionnum' => 'Action', + }, + 'fields' => [ 'name', + 'comment', + { 'field' => 'priority', + 'type' => 'select', + 'options' => [ 0 .. 10 ], + 'labels' => { 0 => 'Inactive' }, + }, + { 'field' => 'svcnum', 'type' => 'hidden', }, + { 'type' => 'tablebreak-tr-title', + 'value' => 'Conditions', + }, + { 'field' => 'ruleconditionnum', + 'type' => 'select-cgp_rule_condition', + 'o2m_table' => 'cgp_rule_condition', + 'm2_label' => 'Condition', + 'm2_error_callback' => $m2_error_callback_cond, + }, + { 'type' => 'tablebreak-tr-title', + 'value' => 'Actions', + }, + { 'field' => 'ruleactionnum', + 'type' => 'select-cgp_rule_action', + 'o2m_table' => 'cgp_rule_action', + 'm2_label' => 'Action', + 'm2_error_callback' => $m2_error_callback_action, + }, + ], + 'new_callback' => sub { my( $cgi, $cgp_rule ) = @_; + $cgp_rule->svcnum( $cgi->param('svcnum') ); + }, + #'viewall_url' => $viewall_url, + 'menubar' => [], + ) +%> +<%init> + +my %opt = @_; + +#my $viewall_url = $p. "browse/$table.html?svcnum=$svcnum"; + +my $m2_error_callback_cond = sub { + my($cgi, $object) = @_; + + my @fields = qw( conditionname op params ); + my @gfields = ( '', map "_$_", @fields ); + + map { + if ( /^ruleconditionnum(\d+)$/ ) { + my $num = $1; + if ( grep $cgi->param("ruleconditionnum$num$_"), @gfields ) { + my $x = new FS::cgp_rule_condition { + 'ruleconditionnum' => $cgi->param("ruleconditionnum$num"), + map { $_ => scalar($cgi->param("ruleconditionnum${num}_$_")) } @fields, + }; + $x; + } else { + (); + } + } else { + (); + } + } + $cgi->param; +}; + +my $m2_error_callback_action = sub { + my($cgi, $object) = @_; + + my @fields = qw( action params ); + my @gfields = ( '', map "_$_", @fields ); + + map { + if ( /^ruleactionnum(\d+)$/ ) { + my $num = $1; + if ( grep $cgi->param("ruleactionnum$num$_"), @gfields ) { + my $x = new FS::cgp_rule_action { + 'ruleactionnum' => $cgi->param("ruleactionnum$num"), + map { $_ => scalar($cgi->param("ruleactionnum${num}_$_")) } @fields, + }; + $x; + } else { + (); + } + } else { + (); + } + } + $cgi->param; + +}; + +</%init> diff --git a/httemplate/edit/cust_bill_pay.cgi b/httemplate/edit/cust_bill_pay.cgi new file mode 100755 index 000000000..532db6a6e --- /dev/null +++ b/httemplate/edit/cust_bill_pay.cgi @@ -0,0 +1,14 @@ +<% include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_bill_pay.cgi', + 'src_table' => 'cust_pay', + 'src_thing' => 'payment', + 'dst_table' => 'cust_bill', + 'dst_thing' => 'invoice', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/cust_category.html b/httemplate/edit/cust_category.html new file mode 100644 index 000000000..18a6189bc --- /dev/null +++ b/httemplate/edit/cust_category.html @@ -0,0 +1,5 @@ +<% include( 'elements/category_Common.html', + 'name' => 'Customer Category', + 'table' => 'cust_category', + ) +%> diff --git a/httemplate/edit/cust_class.html b/httemplate/edit/cust_class.html new file mode 100644 index 000000000..fdb58e687 --- /dev/null +++ b/httemplate/edit/cust_class.html @@ -0,0 +1,5 @@ +<% include( 'elements/class_Common.html', + 'name' => 'Customer Class', + 'table' => 'cust_class', + ) +%> diff --git a/httemplate/edit/cust_credit.cgi b/httemplate/edit/cust_credit.cgi new file mode 100755 index 000000000..dc80847de --- /dev/null +++ b/httemplate/edit/cust_credit.cgi @@ -0,0 +1,81 @@ +<% include('/elements/header-popup.html', 'Enter Credit') %> + +<% include('/elements/error.html') %> + +<FORM NAME="credit_popup" ACTION="<% $p1 %>process/cust_credit.cgi" 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="<% $_date %>"> +<INPUT TYPE="hidden" NAME="credited" VALUE=""> +<INPUT TYPE="hidden" NAME="otaker" VALUE="<% $otaker %>"> + +<% ntable("#cccccc", 2) %> + + <TR> + <TD ALIGN="right">Date</TD> + <TD BGCOLOR="#ffffff"><% time2str($date_format, $_date) %></TD> + </TR> + + <TR> + <TD ALIGN="right">Amount</TD> + <TD BGCOLOR="#ffffff">$<INPUT TYPE="text" NAME="amount" VALUE="<% $amount |h %>" SIZE=8 MAXLENGTH=8></TD> + </TR> + +% +%#print qq! <INPUT TYPE="checkbox" NAME="refund" VALUE="$refund">Also post refund!; +% + +<% include( '/elements/tr-select-reason.html', + 'field' => 'reasonnum', + 'reason_class' => 'R', + 'control_button' => "document.getElementById('confirm_credit_button')", + 'cgi' => $cgi, + ) +%> + + <TR> + <TD ALIGN="right">Additional info</TD> + <TD> + <INPUT TYPE="text" NAME="addlinfo" VALUE="<% $cgi->param('addlinfo') |h %>"> + </TD> + + <TR> + <TD ALIGN="right">Auto-apply<BR>to invoices</TD> + <TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED>yes<OPTION>no</SELECT></TD> + </TR> + +% if ( $conf->exists('pkg-balances') ) { + <% include('/elements/tr-select-cust_pkg-balances.html', + 'custnum' => $custnum, + 'cgi' => $cgi + ) + %> +% } else { + <INPUT TYPE="hidden" NAME="pkgnum" VALUE=""> +% } + +</TABLE> + +<BR> + +<CENTER><INPUT TYPE="submit" ID="confirm_credit_button" VALUE="Enter credit" DISABLED></CENTER> + +</FORM> +</BODY> +</HTML> +<%init> + +my $conf = new FS::Conf; +my $date_format = $conf->config('date_format') || '%m/%d/%Y'; + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Post credit'); + +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_credit_bill.cgi b/httemplate/edit/cust_credit_bill.cgi new file mode 100755 index 000000000..e3627ff37 --- /dev/null +++ b/httemplate/edit/cust_credit_bill.cgi @@ -0,0 +1,14 @@ +<% include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_credit_bill.cgi', + 'src_table' => 'cust_credit', + 'src_thing' => 'credit', + 'dst_table' => 'cust_bill', + 'dst_thing' => 'invoice', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply credit'); + +</%init> diff --git a/httemplate/edit/cust_credit_refund.cgi b/httemplate/edit/cust_credit_refund.cgi new file mode 100755 index 000000000..f5bbb5633 --- /dev/null +++ b/httemplate/edit/cust_credit_refund.cgi @@ -0,0 +1,14 @@ +<% include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_credit_refund.cgi', + 'src_table' => 'cust_credit', + 'src_thing' => 'credit', + 'dst_table' => 'cust_refund', + 'dst_thing' => 'refund', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply credit'); + +</%init> diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi new file mode 100755 index 000000000..57dc3597a --- /dev/null +++ b/httemplate/edit/cust_main.cgi @@ -0,0 +1,321 @@ +<% include('/elements/header.html', + $title, + '', + ' onUnload="myclose()"' #hmm, in billing.html +) %> + +<% include('/elements/error.html') %> + +<FORM NAME = "CustomerForm" + METHOD = "POST" + ACTION = "<% popurl(1) %>process/cust_main.cgi" +%# STYLE = "margin-bottom: 0" +%# STYLE="margin-top: 0; margin-bottom: 0"> +> + +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> + +% if ( $custnum ) { + Customer #<B><% $cust_main->display_custnum %></B> - + <B><FONT COLOR="#<% $cust_main->statuscolor %>"> + <% ucfirst($cust_main->status) %> + </FONT></B> + <BR><BR> +% } + +%# agent, agent_custid, refnum (advertising source), referral_custnum +<% include('cust_main/top_misc.html', $cust_main, 'custnum' => $custnum ) %> + +%# birthdate +% if ( $conf->exists('cust_main-enable_birthdate') ) { + <BR> + <% include('cust_main/birthdate.html', $cust_main) %> +% } + +%# latitude and longitude +% if ( $conf->exists('cust_main-require_censustract') ) { +% my ($latitude, $longitude) = $cust_main->service_coordinates; +% $latitude ||= $conf->config('company_latitude') || ''; +% $longitude ||= $conf->config('company_longitude') || ''; + <INPUT NAME="latitude" TYPE="hidden" VALUE="<% $latitude |h %>"> + <INPUT NAME="longitude" TYPE="hidden" VALUE="<% $longitude |h %>"> +% } + +%# contact info + +% my $same_checked = ''; +% my $ship_disabled = ''; +% my @ship_style = (); +% unless ( $cust_main->ship_last && $same ne 'Y' ) { +% $same_checked = 'CHECKED'; +% $ship_disabled = 'DISABLED'; +% push @ship_style, 'background-color:#dddddd'; +% foreach ( +% qw( last first company address1 address2 city county state zip country +% daytime night fax ) +% ) { +% $cust_main->set("ship_$_", $cust_main->get($_) ); +% } +% } + +<BR> +<FONT SIZE="+1"><B>Billing address</B></FONT> + +<% include('cust_main/contact.html', + 'cust_main' => $cust_main, + 'pre' => '', + 'onchange' => 'bill_changed(this)', + 'disabled' => '', + 'ss' => $ss, + 'stateid' => $stateid, + 'same_checked' => $same_checked, #for address2 "Unit #" labeling + ) +%> + +<SCRIPT> +function bill_changed(what) { + if ( what.form.same.checked ) { +% for (qw( last first company address1 address2 city zip daytime night fax )) { + what.form.ship_<%$_%>.value = what.form.<%$_%>.value; +% } + + what.form.ship_country.selectedIndex = what.form.country.selectedIndex; + + function fix_ship_city() { + what.form.ship_city_select.selectedIndex = what.form.city_select.selectedIndex; + what.form.ship_city.style.display = what.form.city.style.display; + what.form.ship_city_select.style.display = what.form.city_select.style.display; + } + + function fix_ship_county() { + what.form.ship_county.selectedIndex = what.form.county.selectedIndex; + ship_county_changed(what.form.ship_county, fix_ship_city ); + } + + function fix_ship_state() { + what.form.ship_state.selectedIndex = what.form.state.selectedIndex; + ship_state_changed(what.form.ship_state, fix_ship_county ); + } + + ship_country_changed(what.form.ship_country, fix_ship_state ); + + } +} +function samechanged(what) { + if ( what.checked ) { + bill_changed(what); + +% my @fields = qw( last first company address1 address2 city city_select county state zip country daytime night fax ); +% for (@fields) { + what.form.ship_<%$_%>.disabled = true; + what.form.ship_<%$_%>.style.backgroundColor = '#dddddd'; +% } + +% if ( $conf->exists('cust_main-require_address2') ) { + document.getElementById('address2_required').style.visibility = ''; + document.getElementById('address2_label').style.visibility = ''; + document.getElementById('ship_address2_required').style.visibility = 'hidden'; + document.getElementById('ship_address2_label').style.visibility = 'hidden'; +% } + + } else { + +% for (@fields) { + what.form.ship_<%$_%>.disabled = false; + what.form.ship_<%$_%>.style.backgroundColor = '#ffffff'; +% } + +% if ( $conf->exists('cust_main-require_address2') ) { + document.getElementById('address2_required').style.visibility = 'hidden'; + document.getElementById('address2_label').style.visibility = 'hidden'; + document.getElementById('ship_address2_required').style.visibility = ''; + document.getElementById('ship_address2_label').style.visibility = ''; +% } + + } +} +</SCRIPT> + +<BR> +<FONT SIZE="+1"><B>Service address</B></FONT> + +(<INPUT TYPE="checkbox" NAME="same" VALUE="Y" onClick="samechanged(this)" <%$same_checked%>>same as billing address) +<% include('cust_main/contact.html', + 'cust_main' => $cust_main, + 'pre' => 'ship_', + 'onchange' => '', + 'disabled' => $ship_disabled, + 'style' => \@ship_style + ) +%> + +%# billing info +<% include( 'cust_main/billing.html', $cust_main, + 'payinfo' => $payinfo, + 'invoicing_list' => \@invoicing_list, + ) +%> + +% my $ro_comments = $conf->exists('cust_main-use_comments')?'':'readonly'; +% if (!$ro_comments || $cust_main->comments) { + + <BR>Comments + <% &ntable("#cccccc") %> + <TR> + <TD> + <TEXTAREA NAME = "comments" + COLS = 80 + ROWS = 5 + WRAP = "HARD" + <% $ro_comments %> + ><% $cust_main->comments %></TEXTAREA> + </TD> + </TR> + </TABLE> + +% } + +% unless ( $custnum ) { + + <% include('cust_main/first_pkg.html', $cust_main, + 'pkgpart_svcpart' => $pkgpart_svcpart, + #svc_acct + 'username' => $username, + 'password' => $password, + 'popnum' => $popnum, + 'saved_domsvc' => $saved_domsvc, + %svc_phone, + ) + %> + +% } + +<INPUT TYPE="hidden" NAME="usernum" VALUE="<% $cust_main->usernum %>"> + +%# cust_main/bottomfixup.js +% foreach my $hidden ( +% 'payauto', +% 'payinfo', 'payinfo1', 'payinfo2', 'paytype', +% 'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv', +% 'paystart_month', 'paystart_year', 'payissue', +% 'payip', +% 'paid', +% ) { + <INPUT TYPE="hidden" NAME="<% $hidden %>" VALUE=""> +% } + +<% include('cust_main/bottomfixup.html') %> + +<BR> +<INPUT TYPE = "button" + NAME = "submitButton" + ID = "submitButton" + VALUE = "<% $custnum ? "Apply Changes" : "Add Customer" %>" + onClick = "this.disabled=true; bottomfixup(this.form);" +> +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +#probably redundant given the checks below... +die "access denied" + unless $curuser->access_right('New customer') + || $curuser->access_right('Edit customer'); + +my $conf = new FS::Conf; + +#get record + +my($custnum, $cust_main, $ss, $stateid, $payinfo, @invoicing_list); +my $same = ''; +my $pkgpart_svcpart = ''; #first_pkg +my($username, $password, $popnum, $saved_domsvc) = ( '', '', 0, 0 ); #svc_acct +my %svc_phone = (); + +if ( $cgi->param('error') ) { + + $cust_main = new FS::cust_main ( { + map { $_, scalar($cgi->param($_)) } fields('cust_main') + } ); + + $custnum = $cust_main->custnum; + + die "access denied" + unless $curuser->access_right($custnum ? 'Edit customer' : 'New customer'); + + @invoicing_list = split( /\s*,\s*/, $cgi->param('invoicing_list') ); + $same = $cgi->param('same'); + $cust_main->setfield('paid' => $cgi->param('paid')) if $cgi->param('paid'); + $ss = $cust_main->ss; # don't mask an entered value on errors + $stateid = $cust_main->stateid; # don't mask an entered value on errors + $payinfo = $cust_main->payinfo; # don't mask an entered value on errors + + $pkgpart_svcpart = $cgi->param('pkgpart_svcpart') || ''; + + #svc_acct + $username = $cgi->param('username'); + $password = $cgi->param('_password'); + $popnum = $cgi->param('popnum'); + $saved_domsvc = $cgi->param('domsvc') || ''; + if ( $saved_domsvc =~ /^(\d+)$/ ) { + $saved_domsvc = $1; + } else { + $saved_domsvc = ''; + } + + #svc_phone + $svc_phone{$_} = $cgi->param($_) + foreach qw( countrycode phonenum sip_password pin phone_name ); + +} elsif ( $cgi->keywords ) { #editing + + die "access denied" + unless $curuser->access_right('Edit customer'); + + my( $query ) = $cgi->keywords; + $query =~ /^(\d+)$/; + $custnum=$1; + $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ); + if ( $cust_main->dbdef_table->column('paycvv') + && length($cust_main->paycvv) ) { + my $paycvv = $cust_main->paycvv; + $paycvv =~ s/./*/g; + $cust_main->paycvv($paycvv); + } + @invoicing_list = $cust_main->invoicing_list; + $ss = $cust_main->masked('ss'); + $stateid = $cust_main->masked('stateid'); + $payinfo = $cust_main->paymask; + +} else { #new customer + + die "access denied" + unless $curuser->access_right('New customer'); + + $custnum=''; + $cust_main = new FS::cust_main ( {} ); + $cust_main->otaker( &getotaker ); + $cust_main->referral_custnum( $cgi->param('referral_custnum') ); + @invoicing_list = (); + push @invoicing_list, 'POST' + unless $conf->exists('disablepostalinvoicedefault'); + $ss = ''; + $stateid = ''; + $payinfo = ''; + +} + +my $error = $cgi->param('error'); +$cgi->delete_all(); +$cgi->param('error', $error); + +my $title = $custnum ? 'Edit Customer' : 'Add Customer'; +$title .= ": ". $cust_main->name if $custnum; + +my $r = qq!<font color="#ff0000">*</font> !; + +</%init> diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html new file mode 100644 index 000000000..ad83778ca --- /dev/null +++ b/httemplate/edit/cust_main/billing.html @@ -0,0 +1,492 @@ +%if ( $payby_default eq 'HIDE' ) { +% +% $cust_main->payby('BILL') unless $cust_main->payby; +% my $payby = $cust_main->payby; + + <INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>"> + + <INPUT TYPE="hidden" NAME="<%$payby%>_payinfo" VALUE="<% $cust_main->paymask %>"> + +% foreach my $field (qw( payname paycvv paystart_month paystart_year payissue payip paytype paystate )) { + + <INPUT TYPE="hidden" NAME="<% $payby.'_'.$field %>" VALUE="<% $cust_main->get($field) %>"> + +% } + +% #false laziness w/elements/select-month_year.html & view/cust_main/billing.html +% my( $mon, $year ); +% my $date = $cust_main->paydate || '12-2037'; +% if ( $date =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format +% ( $mon, $year ) = ( $2, $1 ); +% } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) { +% ( $mon, $year ) = ( $1, $3 ); +% } else { +% die "unrecognized expiration date format: $date"; +% } + + <INPUT TYPE="hidden" NAME="<%$payby%>_exp_month" VALUE="<% $mon %>"> + <INPUT TYPE="hidden" NAME="<%$payby%>_exp_year" VALUE="<% $year %>"> + + <INPUT TYPE="hidden" NAME="tax" VALUE="<% $cust_main->tax %>"> + + <INPUT TYPE="hidden" NAME="invoicing_list" VALUE="<% join(', ', @invoicing_list) %>"> + +% } else { +% +% my $r = qq!<font color="#ff0000">*</font> !; + + <BR><FONT SIZE="+1"><B>Billing information</B></FONT> + <% &ntable("#cccccc") %> + + <TR> + <TD ALIGN="right" WIDTH="200"><%$r%>Billing type</TD> + + <SCRIPT> + + var mywindow = -1; + function myopen(filename,windowname,properties) { + myclose(); + mywindow = window.open(filename,windowname,properties); + } + function myclose() { + if ( mywindow != -1 ) + mywindow.close(); + mywindow = -1; + } + + var achwindow = -1; + function achopen(filename,windowname,properties) { + achclose(); + achwindow = window.open(filename,windowname,properties); + } + function achclose() { + if ( achwindow != -1 ) + achwindow.close(); + achwindow = -1; + } + + function card_changed(what) { + if ( + what.form.payinfo.value.substring(0, 4) == '4093' + || what.form.payinfo.value.substring(0, 4) == '4911' + || what.form.payinfo.value.substring(0, 4) == '4936' + || what.form.payinfo.value.substring(0, 6) == '564132' + || what.form.payinfo.value.substring(0, 2) == '63' + || what.form.payinfo.value.substring(0, 2) == '67' + ) + { + what.form.paystart_month.disabled = false; + what.form.paystart_year.disabled = false; + what.form.payissue.disabled = false; + what.form.paystart_month.style.backgroundColor = '#ffffff'; + what.form.paystart_year.style.backgroundColor = '#ffffff'; + what.form.payissue.style.backgroundColor = '#ffffff'; + document.getElementById('paystart_label').style.color = '#000000'; + document.getElementById('payissue_label').style.color = '#000000'; + } else { + what.form.paystart_month.disabled = true; + what.form.paystart_year.disabled = true; + what.form.payissue.disabled = true; + what.form.paystart_month.style.backgroundColor = '#dddddd'; + what.form.paystart_year.style.backgroundColor = '#dddddd'; + what.form.payissue.style.backgroundColor = '#dddddd'; + document.getElementById('paystart_label').style.color = '#999999'; + document.getElementById('payissue_label').style.color = '#999999'; + } + return true; + } + + </SCRIPT> + + <% include('/elements/init_overlib.html') %> + +% my $payby = $cust_main->payby; +% my $paytype = $cust_main->paytype; +% my( $account, $aba ) = split('@', $payinfo); +% +% my $disabled = 'DISABLED style="background-color: #dddddd"'; +% my $text_disabled = 'style="color: #999999"'; +% +% if ( $payby =~ /^(CARD|DCRD)$/ && cardtype($payinfo) =~ /^(Switch|Solo)$/ ) { +% $disabled = 'style="background-color: #ffffff"'; +% $text_disabled = 'style="color: #000000";' +% } +% +% my %payby = ( +% +% 'CARD' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Card number </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_payinfo" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $payinfo : '' ). qq!" MAXLENGTH=19 onChange="card_changed(this)" onKeyUp="card_changed(this)"></TD></TR>!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Expiration </TD>!. +% '<TD WIDTH="408">'. +% +% include('/elements/select-month_year.html', +% 'prefix' => 'CARD_exp', +% 'selected_date' => +% ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->paydate : '' ), +% ). +% +% '</TD></TR>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">CVV2 !. +% +% qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/cvv2.html', 480, 352, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)!. +% qq!</TD>!. +% '<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_paycvv" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ && !$cust_main->is_encrypted($cust_main->paycvv) ? $cust_main->paycvv : '' ). '" SIZE=4 MAXLENGTH=4>'. +% +% +% qq!<TR><TD ALIGN="right" WIDTH="200"><SPAN ID="paystart_label" $text_disabled>Start date </SPAN></TD>!. +% '<TD WIDTH="408">'. +% +% include('/elements/select-month_year.html', +% 'prefix' => 'CARD_paystart', +% 'disabled' => $disabled, +% 'empty_option' => 1, +% 'start_year' => 2000, +% 'end_year' => (localtime())[5] + 1900, +% 'selected_date' => ( +% ( $payby =~ /^(CARD|DCRD)$/ +% && cardtype($payinfo) =~ /^(Switch|Solo)$/ ) +% ? $cust_main->paystart_month. '-'. +% $cust_main->paystart_year +% : '' +% ) +% ). +% +% qq!<SPAN ID="payissue_label" $text_disabled> or Issue number </SPAN>!. +% '<INPUT TYPE="text" NAME="CARD_payissue" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payissue : '' ). qq!" SIZE=3 MAXLENGTH=2 $disabled></TD></TR>!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Exact name on card </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_payname" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!. +% +% qq!<TR><TD COLSPAN=2 WIDTH="608"><INPUT TYPE="checkbox" NAME="CARD_payauto" !. ( $payby eq 'DCRD' ? '' : 'CHECKED' ). '> Charge future payments to this card automatically</TD></TR>'. +% +% '</TABLE>', +% +% 'CHEK' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Account number </TD>!. +% qq!<TD><INPUT TYPE="text" SIZE=12 NAME="CHEK_payinfo1" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $account : '' ). '"></TD>'. +% qq!<TD ALIGN="right">Type</TD><TD><SELECT NAME="CHEK_paytype">!. +% join('', map { qq!<OPTION VALUE="$_" !.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>" } @FS::cust_main::paytypes). +% qq!</SELECT></TD></TR>!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}ABA/Routing number </TD>!. +% qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" SIZE=10 MAXLENGTH=9 NAME="CHEK_payinfo2" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $aba : '' ). qq!" SIZE=10 MAXLENGTH=9> !. +% qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/ach.html', 380, 240, 'ach_popup' ), CAPTION, 'ACH Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)!. +% qq!</TD></TR>!. +% +% qq!<INPUT TYPE="hidden" NAME="CHEK_exp_month" VALUE="12">!. +% qq!<INPUT TYPE="hidden" NAME="CHEK_exp_year" VALUE="2037">!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Bank name </TD>!. +% qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" NAME="CHEK_payname" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!. +% ( $conf->exists('show_bankstate') ? +% qq!<TR><TD ALIGN="right" WIDTH="200">$paystate_label</TD>!. +% qq!<TD COLSPAN="3" WIDTH="408">!. +% include('/elements/select-state.html', +% 'empty' => '(choose)', +% 'state' => $cust_main->paystate, +% 'country' => $cust_main->country, +% 'prefix' => 'CHEK_pay', +% ). "</TD></TR>" +% : '<INPUT TYPE="hidden" NAME="CHEK_paystate" VALUE="'. +% $cust_main->paystate. '">' +% ). +% +% +% qq!<TR><TD COLSPAN=4 WIDTH="608"><INPUT TYPE="checkbox" NAME="CHEK_payauto" !. ( $payby eq 'DCHK' ? '' : 'CHECKED' ). '> Charge future payments to this electronic check automatically</TD></TR>'. +% +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% +% '</TABLE>', +% +% 'LECB' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Phone number </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="LECB_payinfo" VALUE="!. ( $payby eq 'LECB' ? $cust_main->payinfo : '' ). qq!" MAXLENGTH=15 SIZE=16></TD></TR>!. +% +% qq!<INPUT TYPE="hidden" NAME="LECB_exp_month" VALUE="12">!. +% qq!<INPUT TYPE="hidden" NAME="LECB_exp_year" VALUE="2037">!. +% qq!<INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!. +% +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% +% '</TABLE>', +% +% 'BILL' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">P.O. </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payinfo" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payinfo : '' ). qq!"></TD></TR>!. +% +% qq!<INPUT TYPE="hidden" NAME="BILL_exp_month" VALUE="12">!. +% qq!<INPUT TYPE="hidden" NAME="BILL_exp_year" VALUE="2037">!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">Attention </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payname" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payname : '' ). qq!"></TD></TR>!. +% +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% +% '</TABLE>', +% +% 'COMP' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Approved by </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""></TD></TR>!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Expiration </TD>!. +% '<TD WIDTH="408">'. +% +% include('/elements/select-month_year.html', +% 'prefix' => 'COMP_exp', +% 'selected_date' => +% ( $payby eq 'COMP' ? $cust_main->paydate : '' ), +% ). +% +% '</TD></TR>'. +% +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% +% '</TABLE>', +% +% 'CASH' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CASH_paid" VALUE="!. ( $payby eq 'CASH' ? $cust_main->paid : '' ). qq!"></TD></TR>!. +% +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% +% '</TABLE>', +% +% 'WEST' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="WEST_paid" VALUE="!. ( $payby eq 'WEST' ? $cust_main->paid : '' ). qq!"></TD></TR>!. +% +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% +% '</TABLE>', +% +% 'MCRD' => +% +% '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="MCRD_paid" VALUE="!. ( $payby eq 'MCRD' ? $cust_main->paid : '' ). qq!"></TD></TR>!. +% +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% '<TR><TD> </TD></TR>'. +% +% '</TABLE>', +% +% ); +% +% #this should use FS::payby +% my @allopt = qw( CARD CHEK LECB BILL CASH WEST MCRD COMP ); +% +% my %allopt = map { $_ => FS::payby->shortname($_) } @allopt; +% +% if ( $cust_main->custnum ) { +% #don't offer CASH/WEST/MCRD initial payment types when editing customer +% delete $allopt{$_} for qw(CASH WEST MCRD); +% } +% +% my @options = grep exists( $allopt{$_} ), @payby; +% +% my %payby2option = ( +% ( map { $_ => $_ } @options ), +% 'DCRD' => 'CARD', +% 'DCHK' => 'CHEK', +% ); + + <TD WIDTH="408"> + <% include( '/elements/selectlayers.html', + 'field' => 'payby', + 'curr_value' => $payby2option{$payby || $payby_default || $payby[0] }, + 'options' => \@options, + 'labels' => \%allopt, + 'html_between' => '</TD></TR></TABLE>', + 'layer_callback' => sub { my $layer = shift; $payby{$layer}; }, + ) + %> + + <% &ntable("#cccccc") %> + + <TR><TD> </TD></TR> + +% my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); + + <TR> + <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="tax" VALUE="Y" <% $cust_main->tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt<% @exempt_groups ? ' (all taxes)' : '' %></TD> + </TR> + +% foreach my $exempt_group ( @exempt_groups ) { +% #escape $exempt_group for NAME + <TR> + <TD WIDTH="608" COLSPAN="2"> <INPUT TYPE="checkbox" NAME="tax_<% $exempt_group %>" VALUE="Y" <% $cust_main->tax_exemption($exempt_group) ? 'CHECKED' : '' %>> Tax Exempt (<% $exempt_group %> taxes)<TD> + </TR> +% } + +% unless ( $conf->exists('emailinvoiceonly') ) { + + <TR> + <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST" <% + + ( grep { $_ eq 'POST' } @invoicing_list ) + + ? 'CHECKED' + : '' + + %>> Postal mail invoice + + </TD> + </TR> + + <TR> + <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_FAX" VALUE="FAX" <% + + ( grep { $_ eq 'FAX' } @invoicing_list ) + ? 'CHECKED' + : '' + + %>> Fax invoice + + </TD> + </TR> + +% } + + <TR> + <TD ALIGN="right" WIDTH="200"> + <% $conf->exists('cust_main-require_invoicing_list_email') ? $r : '' %>Email address(es) + </TD> + <TD WIDTH="408"><INPUT TYPE="text" NAME="invoicing_list" VALUE="<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) %>"></TD> + </TR> + + <TR> + <TD ALIGN="right" WIDTH="200">Invoice terms </TD> + <TD WIDTH="408"> + <% include('/elements/select-terms.html', + 'curr_value' => $cust_main->invoice_terms, + ) + %> + </TD> + </TR> + +% if ( $conf->exists('voip-cust_cdr_spools') ) { + <TR> + <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="spool_cdr" VALUE="Y" <% $cust_main->spool_cdr eq "Y" ? 'CHECKED' : '' %>> Spool CDRs</TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="spool_cdr" VALUE="<% $cust_main->spool_cdr %>"> +% } + +% if ( $conf->exists('voip-cust_cdr_squelch') ) { + <TR> + <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="squelch_cdr" VALUE="Y" <% $cust_main->squelch_cdr eq "Y" ? 'CHECKED' : '' %>> Omit CDRs from invoices</TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="squelch_cdr" VALUE="<% $cust_main->squelch_cdr %>"> +% } + +% if ( $conf->exists('voip-cust_email_csv_cdr') ) { + <TR> + <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="email_csv_cdr" VALUE="Y" <% $cust_main->email_csv_cdr eq "Y" ? 'CHECKED' : '' %>> Attach CDRs as CSV to emailed invoices</TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="email_csv_cdr" VALUE="<% $cust_main->email_csv_cdr %>"> +% } + +% if ( $show_term || $cust_main->cdr_termination_percentage ) { + <TR> + <TD ALIGN="right">CDR termination settlement</TD> + <TD><INPUT TYPE = "text" + NAME = "cdr_termination_percentage" + SIZE = 6 + VALUE = "<% $cust_main->cdr_termination_percentage %>" + STYLE = "text-align:right;" + ><B>%</B></TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="cdr_termination_percentage" VALUE="<% $cust_main->cdr_termination_percentage %>"> +% } + + </TABLE> + + <% $r %> required fields +% } + +<%once> + +my $paystate_label = FS::Msgcat::_gettext('paystate'); +$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/; + +</%once> +<%init> + +my( $cust_main, %options ) = @_; +my @invoicing_list = @{ $options{'invoicing_list'} }; +my $payinfo = $options{'payinfo'}; +my $conf = new FS::Conf; +my $payby_default = $conf->config('payby-default'); + +my @payby = grep /\w/, $conf->config('payby'); +#@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP )) +@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP )) + unless @payby; + +my $show_term = ''; +if ( $cust_main->custnum ) { + #false laziness w/view/cust_main/billing.html + my $term_sql = "SELECT COUNT(*) FROM cust_pkg LEFT JOIN part_pkg USING ( pkgpart ) WHERE custnum = ? AND plan = 'cdr_termination' LIMIT 1"; + my $term_sth = dbh->prepare($term_sql) or die dbh->errstr; + $term_sth->execute($cust_main->custnum) or die $term_sth->errstr; + $show_term = $term_sth->fetchrow_arrayref->[0]; +} + +</%init> diff --git a/httemplate/edit/cust_main/birthdate.html b/httemplate/edit/cust_main/birthdate.html new file mode 100644 index 000000000..b4e78e3b9 --- /dev/null +++ b/httemplate/edit/cust_main/birthdate.html @@ -0,0 +1,16 @@ +<% ntable("#cccccc", 2) %> + <% include( '/elements/tr-input-date-field.html', + 'birthdate', + $cust_main->birthdate, + 'Date of Birth', + ( $conf->config('date_format') || "%m/%d/%Y" ), + 1 + ) + %> +</TABLE> +<%init> + +my( $cust_main, %opt ) = @_; +my $conf = new FS::Conf; + +</%init> diff --git a/httemplate/edit/cust_main/bottomfixup.html b/httemplate/edit/cust_main/bottomfixup.html new file mode 100644 index 000000000..1b29c671a --- /dev/null +++ b/httemplate/edit/cust_main/bottomfixup.html @@ -0,0 +1,19 @@ +<% include('/elements/init_overlib.html') %> + +<% include( '/elements/xmlhttp.html', + 'url' => $p.'misc/xmlhttp-cust_main-address_standardize.html', + 'subs' => [ 'address_standardize' ], + #'method' => 'POST', #could get too long? + ) +%> + +<% include( '/elements/xmlhttp.html', + 'url' => $p.'misc/xmlhttp-cust_main-censustract.html', + 'subs' => [ 'censustract' ], + #'method' => 'POST', #could get too long? + ) +%> + +<SCRIPT TYPE="text/javascript"> + <% include('bottomfixup.js') %> +</SCRIPT> diff --git a/httemplate/edit/cust_main/bottomfixup.js b/httemplate/edit/cust_main/bottomfixup.js new file mode 100644 index 000000000..5d06f3c04 --- /dev/null +++ b/httemplate/edit/cust_main/bottomfixup.js @@ -0,0 +1,399 @@ +function bottomfixup(what) { + +%# ../cust_main.cgi + var layervars = new Array( + 'payauto', + 'payinfo', 'payinfo1', 'payinfo2', 'paytype', + 'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv', + 'paystart_month', 'paystart_year', 'payissue', + 'payip', + 'paid' + ); + + var cf = document.CustomerForm; + var payby = cf.payby.options[cf.payby.selectedIndex].value; + for ( f=0; f < layervars.length; f++ ) { + var field = layervars[f]; + copyelement( cf.elements[payby + '_' + field], + cf.elements[field] + ); + } + + //this part does USPS address correction + + // XXX should this be first and should we update the form fields that are + // displayed??? + + var cf = document.CustomerForm; + + var state_el = cf.elements['state']; + var ship_state_el = cf.elements['ship_state']; + + //address_standardize( + var cust_main = new Array( + 'company', cf.elements['company'].value, + 'address1', cf.elements['address1'].value, + 'address2', cf.elements['address2'].value, + 'city', cf.elements['city'].value, + 'state', state_el.options[ state_el.selectedIndex ].value, + 'zip', cf.elements['zip'].value, + + 'ship_company', cf.elements['ship_company'].value, + 'ship_address1', cf.elements['ship_address1'].value, + 'ship_address2', cf.elements['ship_address2'].value, + 'ship_city', cf.elements['ship_city'].value, + 'ship_state', ship_state_el.options[ ship_state_el.selectedIndex ].value, + 'ship_zip', cf.elements['ship_zip'].value + ); + + address_standardize( cust_main, update_address ); + +} + +var standardize_address; + +function update_address(arg) { + + var argsHash = eval('(' + arg + ')'); + + var changed = argsHash['address_standardized']; + var ship_changed = argsHash['ship_address_standardized']; + var error = argsHash['error']; + var ship_error = argsHash['ship_error']; + + + //yay closures + standardize_address = function () { + + var cf = document.CustomerForm; + var state_el = cf.elements['state']; + var ship_state_el = cf.elements['ship_state']; + + if ( changed ) { + cf.elements['company'].value = argsHash['new_company']; + cf.elements['address1'].value = argsHash['new_address1']; + cf.elements['address2'].value = argsHash['new_address2']; + cf.elements['city'].value = argsHash['new_city']; + setselect(cf.elements['state'], argsHash['new_state']); + cf.elements['zip'].value = argsHash['new_zip']; + } + + if ( ship_changed ) { + cf.elements['ship_company'].value = argsHash['new_ship_company']; + cf.elements['ship_address1'].value = argsHash['new_ship_address1']; + cf.elements['ship_address2'].value = argsHash['new_ship_address2']; + cf.elements['ship_city'].value = argsHash['new_ship_city']; + setselect(cf.elements['ship_state'], argsHash['new_ship_state']); + cf.elements['ship_zip'].value = argsHash['new_ship_zip']; + } + + post_standardization(); + + } + + + + if ( changed || ship_changed ) { + +% if ( $conf->exists('cust_main-auto_standardize_address') ) { + + standardize_address(); + +% } else { + + // popup a confirmation popup + + var confirm_change = + '<CENTER><BR><B>Confirm address standardization</B><BR><BR>' + + '<TABLE>'; + + if ( changed ) { + + confirm_change = confirm_change + + '<TR><TH>Entered billing address</TH>' + + '<TH>Standardized billing address</TH></TR>'; + // + '<TR><TD> </TD><TD> </TD></TR>'; + + if ( argsHash['company'] || argsHash['new_company'] ) { + confirm_change = confirm_change + + '<TR><TD>' + argsHash['company'] + + '</TD><TD>' + argsHash['new_company'] + '</TD></TR>'; + } + + confirm_change = confirm_change + + '<TR><TD>' + argsHash['address1'] + + '</TD><TD>' + argsHash['new_address1'] + '</TD></TR>' + + '<TR><TD>' + argsHash['address2'] + + '</TD><TD>' + argsHash['new_address2'] + '</TD></TR>' + + '<TR><TD>' + argsHash['city'] + ', ' + argsHash['state'] + ' ' + argsHash['zip'] + + '</TD><TD>' + argsHash['new_city'] + ', ' + argsHash['new_state'] + ' ' + argsHash['new_zip'] + '</TD></TR>' + + '<TR><TD> </TD><TD> </TD></TR>'; + + } + + if ( ship_changed ) { + + confirm_change = confirm_change + + '<TR><TH>Entered service address</TH>' + + '<TH>Standardized service address</TH></TR>'; + // + '<TR><TD> </TD><TD> </TD></TR>'; + + if ( argsHash['ship_company'] || argsHash['new_ship_company'] ) { + confirm_change = confirm_change + + '<TR><TD>' + argsHash['ship_company'] + + '</TD><TD>' + argsHash['new_ship_company'] + '</TD></TR>'; + } + + confirm_change = confirm_change + + '<TR><TD>' + argsHash['ship_address1'] + + '</TD><TD>' + argsHash['new_ship_address1'] + '</TD></TR>' + + '<TR><TD>' + argsHash['ship_address2'] + + '</TD><TD>' + argsHash['new_ship_address2'] + '</TD></TR>' + + '<TR><TD>' + argsHash['ship_city'] + ', ' + argsHash['ship_state'] + ' ' + argsHash['ship_zip'] + + '</TD><TD>' + argsHash['new_ship_city'] + ', ' + argsHash['new_ship_state'] + ' ' + argsHash['new_ship_zip'] + '</TD></TR>' + + '<TR><TD> </TD><TD> </TD></TR>'; + + } + + var addresses = 'address'; + var height = 268; + if ( changed && ship_changed ) { + addresses = 'addresses'; + height = 396; // #what + } + + confirm_change = confirm_change + + '<TR><TD>' + + '<BUTTON TYPE="button" onClick="post_standardization();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' + + '</TD><TD>' + + '<BUTTON TYPE="button" onClick="standardize_address();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' + + '</TD></TR>' + + '<TR><TD COLSPAN=2 ALIGN="center">' + + '<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>'; + + overlib( confirm_change, CAPTION, 'Confirm address standardization', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, height, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 ); + +% } + + } else { + + post_standardization(); + + } + + +} + +function post_standardization() { + + var cf = document.CustomerForm; + +% if ( $conf->exists('enable_taxproducts') ) { + + if ( new String(cf.elements['<% $taxpre %>zip'].value).length < 10 ) + { + + var country_el = cf.elements['<% $taxpre %>country']; + var country = country_el.options[ country_el.selectedIndex ].value; + var geocode = cf.elements['geocode'].value; + + if ( country == 'CA' || country == 'US' ) { + + var state_el = cf.elements['<% $taxpre %>state']; + var state = state_el.options[ state_el.selectedIndex ].value; + + var url = "cust_main/choose_tax_location.html" + + "?data_vendor=cch-zip" + + ";city=" + cf.elements['<% $taxpre %>city'].value + + ";state=" + state + + ";zip=" + cf.elements['<% $taxpre %>zip'].value + + ";country=" + country + + ";geocode=" + geocode + + ";"; + + // popup a chooser + OLgetAJAX( url, update_geocode, 300 ); + + } else { + + cf.elements['geocode'].value = 'DEFAULT'; + post_geocode(); + + } + + } else { + + cf.elements['geocode'].value = ''; + post_geocode(); + + } + +% } else { + + post_geocode(); + +% } + +} + +function post_geocode() { + +% if ( $conf->exists('cust_main-require_censustract') ) { + + //alert('fetch census tract data'); + var cf = document.CustomerForm; + var state_el = cf.elements['ship_state']; + var census_data = new Array( + 'year', <% $conf->config('census_year') || '2009' %>, + 'address', cf.elements['ship_address1'].value, + 'city', cf.elements['ship_city'].value, + 'state', state_el.options[ state_el.selectedIndex ].value, + 'zip', cf.elements['ship_zip'].value + ); + + censustract( census_data, update_censustract ); + +% }else{ + + document.CustomerForm.submit(); + +% } + +} + +function update_geocode() { + + //yay closures + set_geocode = function (what) { + + var cf = document.CustomerForm; + + //alert(what.options[what.selectedIndex].value); + var argsHash = eval('(' + what.options[what.selectedIndex].value + ')'); + cf.elements['<% $taxpre %>city'].value = argsHash['city']; + setselect(cf.elements['<% $taxpre %>state'], argsHash['state']); + cf.elements['<% $taxpre %>zip'].value = argsHash['zip']; + cf.elements['geocode'].value = argsHash['geocode']; + post_geocode(); + + } + + // popup a chooser + + overlib( OLresponseAJAX, CAPTION, 'Select tax location', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 ); + +} + +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 error = argsHash['error']; + + var newcensus = + new String(statecode) + + new String(countycode) + + new String(tractcode).replace(/\s$/, ''); // JSON 1 workaround + + set_censustract = function () { + + cf.elements['censustract'].value = newcensus + cf.submit(); + + } + + if (error || cf.elements['censustract'].value != newcensus) { + // popup an entry dialog + + if (error) { newcensus = error; } + newcensus.replace(/.*ndefined.*/, 'Not found'); + + 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') || '2008' %>' + + '&latitude=' + cf.elements['latitude'].value + + '&longitude=' + cf.elements['longitude'].value + + '" target="_blank">Map service module location</A><BR>' + + '<A href="http://maps.ffiec.gov/FFIECMapper/TGMapSrv.aspx?' + + 'census_year=<% $conf->config('census_year') || '2008' %>' + + '&zip_code=' + cf.elements['ship_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['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="document.CustomerForm.submit();"><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="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><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 { + + cf.submit(); + + } + +} + +function copyelement(from, to) { + if ( from == undefined ) { + to.value = ''; + } else if ( from.type == 'select-one' ) { + to.value = from.options[from.selectedIndex].value; + //alert(from + " (" + from.type + "): " + to.name + " => (" + from.selectedIndex + ") " + to.value); + } else if ( from.type == 'checkbox' ) { + if ( from.checked ) { + to.value = from.value; + } else { + to.value = ''; + } + } else { + if ( from.value == undefined ) { + to.value = ''; + } else { + to.value = from.value; + } + } + //alert(from + " (" + from.type + "): " + to.name + " => " + to.value); +} + +function setselect(el, value) { + + for ( var s = 0; s < el.options.length; s++ ) { + if ( el.options[s].value == value ) { + el.selectedIndex = s; + } + } + +} +<%init> + +my $conf = new FS::Conf; + +my $taxpre = $conf->exists('tax-ship_address') ? 'ship_' : ''; + +</%init> diff --git a/httemplate/edit/cust_main/choose_tax_location.html b/httemplate/edit/cust_main/choose_tax_location.html new file mode 100644 index 000000000..ac475c54b --- /dev/null +++ b/httemplate/edit/cust_main/choose_tax_location.html @@ -0,0 +1,87 @@ +<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 new file mode 100644 index 000000000..feb61db8d --- /dev/null +++ b/httemplate/edit/cust_main/contact.html @@ -0,0 +1,150 @@ +<% &ntable("#cccccc") %> + +<TR> + <TH ALIGN="right"><%$r%>Contact name<BR>(last, first)</TH> + <TD COLSPAN=5> + <INPUT TYPE="text" NAME="<%$pre%>last" VALUE="<% $cust_main->get($pre.'last') %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>> , + <INPUT TYPE="text" NAME="<%$pre%>first" VALUE="<% $cust_main->get($pre.'first') %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>> + </TD> +% if ( $conf->exists('show_ss') && !$pre ) { + + <TD ALIGN="right">SS#</TD> + <TD><INPUT TYPE="text" NAME="ss" VALUE="<% $opt{ss} %>" SIZE=11></TD> +% } elsif ( !$pre ) { + + <TD><INPUT TYPE="hidden" NAME="ss" VALUE="<% $opt{ss} %>"></TD> +% } + + +</TR> + +<TR> + <TD ALIGN="right">Company</TD> + <TD COLSPAN=7> + <INPUT TYPE="text" NAME="<%$pre%>company" VALUE="<% $cust_main->get($pre.'company') %>" SIZE=70 onChange="<% $onchange %>" <%$disabled%> <%$style%>> + </TD> +</TR> + +<% include('/elements/location.html', + 'prefix' => $pre, + 'object' => $cust_main, + 'onchange' => $onchange, + 'disabled' => $disabled, + 'style' => \@style, + 'same_checked' => $opt{'same_checked'}, + 'geocode' => $opt{'geocode'}, + 'censustract' => $opt{'censustract'}, + ) +%> + +<TR> + <TD ALIGN="right"><% $daytime_label %></TD> + <TD COLSPAN=5> + <INPUT TYPE="text" NAME="<%$pre%>daytime" VALUE="<% $cust_main->get($pre.'daytime') %>" SIZE=18 onChange="<% $onchange %>" <%$disabled%> <%$style%>> + </TD> +</TR> + +<TR> + <TD ALIGN="right"><% $night_label %></TD> + <TD COLSPAN=5> + <INPUT TYPE="text" NAME="<%$pre%>night" VALUE="<% $cust_main->get($pre.'night') %>" SIZE=18 onChange="<% $onchange %>" <%$disabled%> <%$style%>> + </TD> +</TR> + +<TR> + <TD ALIGN="right">Fax</TD> + <TD COLSPAN=5> + <INPUT TYPE="text" NAME="<%$pre%>fax" VALUE="<% $cust_main->get($pre.'fax') %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%> <%$style%>> + </TD> +</TR> + +% if ( $conf->exists('show_stateid') && !$pre ) { + +<TR> + <TD ALIGN="right"><% $stateid_label %></TD> + <TD><INPUT TYPE="text" NAME="stateid" VALUE="<% $opt{stateid} %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%> <%$style%>></TD> + <TD ALIGN="right"><% $stateid_state_label %></TD> + <TD><% include('/elements/select-state.html', + 'state' => $cust_main->stateid_state, + 'country' => $cust_main->country, + 'prefix' => 'stateid_', + 'onchange' => $onchange, + 'disabled' => $disabled, + 'style' => \@style, + ) + %> + </TD> +</TR> +% } elsif ( !$pre ) { + + <TD><INPUT TYPE="hidden" NAME="stateid" VALUE="<% $opt{stateid} %>"></TD> + <TD><INPUT TYPE="hidden" NAME="stateid_state" VALUE="<% $cust_main->stateid_state %>"></TD> +% } + +</TABLE> +<%$r%>required fields<BR> + +<%init> + +#my( $cust_main, $pre, $onchange, $disabled, %opt ) = @_; +my %opt = @_; +my $cust_main = $opt{'cust_main'}; +my $pre = $opt{'pre'}; +my $onchange = $opt{'onchange'}; +my $disabled = $opt{'disabled'}; +my @style = ( $opt{'style'} ? @{ $opt{'style'} } : () ); + +#push @style, 'background-color: #dddddd' if $disabled; +my $style = scalar(@style) ? 'STYLE="'. join(';', @style). '"' : ''; + +my $conf = new FS::Conf; + +foreach (qw(ss stateid)) { + $opt{$_} = $cust_main->masked($_) unless exists $opt{$_}; +} + +#false laziness with ship state +my $countrydefault = $conf->config('countrydefault') || 'US'; +$cust_main->set($pre.'country', $countrydefault ) + unless $cust_main->get($pre.'country'); + +my $statedefault = $conf->config('statedefault') + || ($countrydefault eq 'US' ? 'CA' : ''); +$cust_main->set($pre.'state', $statedefault ) + unless $cust_main->get($pre.'state') + || $cust_main->get($pre.'country') ne $countrydefault; + +$cust_main->set('stateid_state', $cust_main->state ) + unless $pre || $cust_main->get('stateid_state'); + +$opt{geocode} ||= $cust_main->get('geocode'); + +if ( $conf->exists('cust_main-require_censustract') ) { + $opt{censustract} ||= $cust_main->censustract; +} + +#my($county_html, $state_html, $country_html) = +# FS::cust_main_county::regionselector( $cust_main->get($pre.'county'), +# $cust_main->get($pre.'state'), +# $cust_main->get($pre.'country'), +# $pre, +# $onchange, +# $disabled, +# ); + +my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/ + ? 'Day Phone' + : FS::Msgcat::_gettext('daytime'); +my $night_label = FS::Msgcat::_gettext('night') =~/^(night)?$/ + ? 'Night Phone' + : FS::Msgcat::_gettext('night') || 'Night Phone'; +my $stateid_label = FS::Msgcat::_gettext('stateid') =~ /^(stateid)?$/ + ? 'Driver’s License' + : FS::Msgcat::_gettext('stateid') || 'Driver’s License'; +my $stateid_state_label = FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_state)?$/ + ? 'Driver’s License State' + : FS::Msgcat::_gettext('stateid_state') || 'Driver’s License State'; + +my $r = qq!<font color="#ff0000">*</font> !; + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg.html b/httemplate/edit/cust_main/first_pkg.html new file mode 100644 index 000000000..0de33c025 --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg.html @@ -0,0 +1,55 @@ +% if ( @part_pkg ) { + + <BR><BR> + <FONT SIZE="+1"><B>First package</B></FONT> + <% ntable("#cccccc") %> + + <TR> + <TD COLSPAN=2> + <% include('first_pkg/select-part_pkg.html', + 'part_pkg' => \@part_pkg, + %opt, + # map { $_ => $opt{$_} } qw( pkgpart_svcpart saved_domsvc ) + ) + %> + +% } +<%init> + +my( $cust_main, %opt ) = @_; + +# pry the wrong place for this logic. also pretty expensive + +#false laziness, copied from FS::cust_pkg::order +my $pkgpart; +my $agentnum = ''; +my @agents = $FS::CurrentUser::CurrentUser->agents; +if ( scalar(@agents) == 1 ) { + # $pkgpart->{PKGPART} is true iff $custnum may purchase PKGPART + $pkgpart = $agents[0]->pkgpart_hashref; + $agentnum = $agents[0]->agentnum; +} else { + #can't know (agent not chosen), so, allow all + $agentnum = 'all'; + my %typenum; + foreach my $agent ( @agents ) { + next if $typenum{$agent->typenum}++; + $pkgpart->{$_}++ foreach keys %{ $agent->pkgpart_hashref } + } +} +#eslaf + +my @first_svc = ( 'svc_acct', 'svc_phone' ); + +my @part_pkg = + grep { $_->svcpart(\@first_svc) + && ( $pkgpart->{ $_->pkgpart } + || $agentnum eq 'all' + || ( $agentnum ne 'all' && $agentnum && $_->agentnum + && $_->agentnum == $agentnum + ) + ) + } + qsearch( 'part_pkg', { 'disabled' => '' }, '', 'ORDER BY pkg' ); # case? + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg/select-part_pkg.html b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html new file mode 100644 index 000000000..871e1cdee --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html @@ -0,0 +1,170 @@ +<% include('/elements/xmlhttp.html', + 'url' => $url_prefix.'misc/svc_acct-domains.cgi', + 'subs' => [ $opt{'prefix'}. 'get_domains' ], + ) +%> + +<% include('/elements/xmlhttp.html', + 'url' => $url_prefix.'misc/part_svc-columns.cgi', + 'subs' => [ $opt{'prefix'}. 'get_part_svc' ], + ) +%> + +<INPUT TYPE="hidden" NAME="svcdb" VALUE=""> + +<SCRIPT TYPE="text/javascript"> + + function selopt(what,value,text,selected) { + var optionName = new Option(text, value, false, selected); + var length = what.length; + what.options[length] = optionName; + } + + var pkgpart_svcpart2svcdb = { +% foreach my $pkgpart ( map $_->pkgpart, @part_pkg ) { + "<% $pkgpart_svcpart{$pkgpart} %>":"<% $svcdb{$pkgpart} %>", +% } + '':'' + }; + + function <% $opt{'prefix'} %>pkgpart_svcpart_changed_too(what,selected) { + + <% $opt{'onchange'} %>; + + pkgpart_svcpart = what.options[what.selectedIndex].value; + + var svcdb = pkgpart_svcpart2svcdb[pkgpart_svcpart]; + + what.form.svcdb.value = svcdb; + + if ( svcdb == 'svc_acct' ) { + + // go get the new domains + function <% $opt{'prefix'} %>update_domains(domains) { + + // blank the current domain list + for ( var i = what.form.<% $opt{'prefix'} %>domsvc.length; i >= 0; i-- ) + what.form.<% $opt{'prefix'} %>domsvc.options[i] = null; + + // add the new domains + var domainArray = eval('(' + domains + ')' ); + for ( var s = 0; s < domainArray.length; s=s+2 ) { + var domainLabel = domainArray[s+1]; + if ( domainLabel == "" ) + domainLabel = '(n/a)'; + selopt( what.form.<% $opt{'prefix'} %>domsvc, + domainArray[s], + domainLabel, + (domainArray[s] == selected) ? true : false + ); + } + + } + + <% $opt{'prefix'} %>get_domains( pkgpart_svcpart, + <% $opt{'prefix'} %>update_domains + ); + + } else if ( svcdb == 'svc_phone' ) { + + function <% $opt{'prefix'} %>update_svc_phone(part_svc_column) { + var colArray = eval('(' + part_svc_column + ')' ); + for ( var s = 0; s < colArray.length; s=s+3 ) { + var name = colArray[s]; + var flag = colArray[s+1]; + var value = colArray[s+2]; + var td_label = document.getElementById(name+'_label_td'); + var td = document.getElementById(name+'_td'); + var input = document.getElementById(name); + if ( flag == 'D' ) { + if ( ! input.value ) { input.value = value; } + td_label.style.display = '' + td.style.display = '' + } else if ( flag == 'F' ) { + input.value = value; + td_label.style.display = 'none' + td.style.display = 'none' + } else { + td_label.style.display = '' + td.style.display = '' + } + } + } + + <% $opt{'prefix'} %>get_part_svc( pkgpart_svcpart, + <% $opt{'prefix'} %>update_svc_phone + ); + + } + + } + +</SCRIPT> + +<% include( '/elements/selectlayers.html', + 'field' => $opt{'prefix'}. 'pkgpart_svcpart', + 'curr_value' => $opt{pkgpart_svcpart}, + 'options' => \@options, + 'labels' => \%labels, + 'html_between' => '</TD></TR></TABLE>', + #'onchange' => $opt{'prefix'}. 'pkgpart_svcpart_changed(this,0);', + 'onchange' => $opt{'prefix'}. 'pkgpart_svcpart_changed_too(what,0)', + + 'layer_callback' => $layer_callback, + 'layermap' => \%layermap, + ) +%> + +<SCRIPT TYPE="text/javascript"> + pkgpart_svcpart_changed_too( document.CustomerForm.pkgpart_svcpart, + <% $opt{saved_domsvc} %> + ); +</SCRIPT> + +<%init> + +my %opt = @_; + +foreach my $opt (qw( svc_part pkgparts saved_pkgpart saved_domsvc prefix)) { + $opt{$_} = '' unless exists($opt{$_}) && defined($opt{$_}); +} +$opt{saved_domsvc} = 0 unless $opt{saved_domsvc}; + +my $url_prefix = $opt{'relurls'} ? '' : $p; + +my @part_pkg = @{$opt{'part_pkg'}}; + +my @first_svc = ( 'svc_acct', 'svc_phone' ); + +my %pkgpart_svcpart = (); +my %svcdb = (); +my %layermap = (); +foreach my $part_pkg ( @part_pkg ) { + my $pkgpart = $part_pkg->pkgpart; + my $pkgpart_svcpart = $pkgpart. "_". $part_pkg->svcpart(\@first_svc); + $pkgpart_svcpart{$pkgpart} = $pkgpart_svcpart; + $svcdb{$pkgpart} = $part_pkg->part_svc(\@first_svc)->svcdb; + $layermap{$pkgpart_svcpart} = $svcdb{$pkgpart}; +} + +my @options = ( '', map $pkgpart_svcpart{ $_->pkgpart }, @part_pkg ); +my %labels = ( '' => ( $opt{'empty_label'} || '(none)' ), + map { $pkgpart_svcpart{ $_->pkgpart } => $_->pkg_comment } + @part_pkg + ); + +my $layer_callback = sub { + my $layer = shift; + #$layer_fields, $layer_values, $layer_prefix + +# my( $pkgpart, $svcpart ) = split('_', $layer); +# my $svcdb = $svcdb{$pkgpart}; + my $svcdb = $layer; + + return '' unless $svcdb; #'<BR><BR><BR><BR><BR>' + + #full path cause we're being slung around as a coderef (mason closures?) + include("/edit/cust_main/first_pkg/$svcdb.html", %opt, ); +}; + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg/svc_acct.html b/httemplate/edit/cust_main/first_pkg/svc_acct.html new file mode 100644 index 000000000..150d4c043 --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg/svc_acct.html @@ -0,0 +1,88 @@ +<% ntable("#cccccc") %> + + <TR> + <TD ALIGN="right">Username</TD> + <TD> + <INPUT TYPE = "text" + NAME = "username" + VALUE = "<% $opt{'username'} %>" + SIZE = <% $ulen2 %> + MAXLENGTH = <% $ulen %> + > + </TD> + </TR> + + <TR> + <TD ALIGN="right">Domain</TD> + <TD> + <SELECT NAME="domsvc"> + <OPTION>(none)</OPTION> + </SELECT> + </TD> + </TR> + + <TR> + <TD ALIGN="right">Password</TD> + <TD> + <INPUT TYPE = "text" + NAME = "_password" + VALUE = "<% $opt{'password'} %>" + SIZE = <% $pmax2 %> + MAXLENGTH = <% $passwordmax %>> +% unless ( $opt{'password_verify'} ) { + (blank to generate) +% } + </TD> + </TR> + +% if ( $opt{'password_verify'} ) { + <TR> + <TD ALIGN="right">Re-enter Password</TD> + <TD> + <INPUT TYPE = "text" + NAME = "_password2" + VALUE = "<% $opt{'password2'} %>" + SIZE = <% $pmax2 %> + MAXLENGTH = <% $passwordmax %>> + </TD> + </TR> +% } + +% if ( $conf->exists('security_phrase') ) { + <TR> + <TD ALIGN="right">Security Phrase</TD> + <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $opt{'sec_phrase'} %>"> + </TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="sec_phrase" VALUE=""> +% } + +% if ( $conf->exists('svc_acct-disable_access_number') ) { + <INPUT TYPE="hidden" NAME="popnum" VALUE=""> +% } else { + <TR> + <TD ALIGN="right">Access number</TD> +%# XXX should gain "area code" selection and labels on the dropdowns + <TD><% FS::svc_acct_pop::popselector($opt{'popnum'}) %></TD> + </TR> +% } + +</TABLE> + +<%init> + +#use FS::svc_acct_pop; + +my( %opt ) = @_; + +my $conf = new FS::Conf; + +#false laziness: (mostly) copied from edit/svc_acct.cgi +#$ulen = $svc_acct->dbdef_table->column('username')->length; +my $ulen = dbdef->table('svc_acct')->column('username')->length; +my $ulen2 = $ulen+2; +my $passwordmax = $conf->config('passwordmax') || 8; +my $pmax2 = $passwordmax + 2; + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg/svc_phone.html b/httemplate/edit/cust_main/first_pkg/svc_phone.html new file mode 100644 index 000000000..70e013ece --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg/svc_phone.html @@ -0,0 +1,82 @@ +<% ntable("#cccccc") %> + +%#XXX this should be hidden or something in most/all cases + <TR> + <TD ALIGN="right" ID="countrycode_label_td">Country code</TD> + <TD ID="countrycode_td"> + <INPUT TYPE = "text" + NAME = "countrycode" + ID = "countrycode" + VALUE = "<% $opt{'countrycode'} %>" + SIZE = 4 + MAXLENGTH = 3 + > + </TD> + </TR> + +%#we don't know the svcpart until the dropdown is changed :/ +%#<% include('/elements/tr-select-did.html', +%# 'label' => 'Phone number', +%# 'curr_value' => $opt{'phonenum'}, +%# ) +%#%> + <TR> + <TD ALIGN="right" ID="phonenum_label_td">Phone Number</TD> + <TD ID="phonenum_td"> + <INPUT TYPE = "text" + NAME = "phonenum" + ID = "phonenum" + VALUE = "<% $opt{'phonenum'} %>" + SIZE = 21 + MAXLENGTH = 20 + > + </TD> + </TR> + + <TR> + <TD ALIGN="right" ID="sip_password_label_td">SIP password</TD> + <TD ID="sip_password_td"> + <INPUT TYPE = "text" + NAME = "sip_password" + ID = "sip_password" + VALUE = "<% $opt{'sip_password'} %>" + MAXLENGTH = 80 + > + </TD> + </TR> + + <TR> + <TD ALIGN="right" ID="pin_label_td">Voicemail PIN</TD> + <TD ID="pin_td"> + <INPUT TYPE = "text" + NAME = "pin" + ID = "pin" + VALUE = "<% $opt{'pin'} %>" + SIZE = 5 + MAXLENGTH = 4 + > + </TD> + </TR> + +%#XXX this should be hidden or something in most/all cases + <TR> + <TD ALIGN="right" ID="phone_name_label_td">Name</TD> + <TD ID="phone_name_td"> + <INPUT TYPE = "text" + NAME = "phone_name" + ID = "phone_name" + VALUE = "<% $opt{'phone_name'} %>" + MAXLENGTH = 80 + > + </TD> + </TR> + +</TABLE> + +<%init> + +my( %opt ) = @_; + +#my $conf = new FS::Conf; + +</%init> diff --git a/httemplate/edit/cust_main/top_misc.html b/httemplate/edit/cust_main/top_misc.html new file mode 100644 index 000000000..441a36334 --- /dev/null +++ b/httemplate/edit/cust_main/top_misc.html @@ -0,0 +1,117 @@ +<% &ntable("#cccccc") %> + +%# tags +<% include('/elements/tr-select-cust_tag.html', + 'custnum' => $custnum, + 'cgi' => $cgi, + ) +%> + +%# agent +<% include('/elements/tr-select-agent.html', + 'curr_value' => $cust_main->agentnum, + 'label' => "<B>${r}Agent</B>", + 'empty_label' => 'Select agent', + 'disable_empty' => ( $cust_main->agentnum ? 1 : 0 ), + 'viewall_right' => 'None', #override default 'View customers of all agents' + ) +%> + +%# agent_custid +% if ( $conf->exists('cust_main-edit_agent_custid') ) { + + <TR> + <TD ALIGN="right">Customer identifier</TD> + <TD><INPUT TYPE="text" NAME="agent_custid" VALUE="<% $cust_main->agent_custid %>"></TD> + </TR> + +% } else { + + <INPUT TYPE="hidden" NAME="agent_custid" VALUE="<% $cust_main->agent_custid %>"> + +% } + +%# class +<% include('/elements/tr-select-cust_class.html', + 'curr_value' => $cust_main->classnum, + 'label' => "Class", + #'empty_label' => '(none)', + #'disable_empty' => + ) +%> + +%# referral (advertising source) +%my $refnum = $cust_main->refnum || $conf->config('referraldefault') || 0; +%if ( $custnum && ! $conf->exists('editreferrals') ) { + + <INPUT TYPE="hidden" NAME="refnum" VALUE="<% $refnum %>"> + +% } else { + + <% include('/elements/tr-select-part_referral.html', + 'curr_value' => $refnum + ) + %> +% } + + +%# referring customer +%my $referring_cust_main = ''; +%if ( $cust_main->referral_custnum +% and $referring_cust_main = +% qsearchs('cust_main', { custnum => $cust_main->referral_custnum } ) +% and ! $curuser->access_right('Edit referring customer') +%) { + + <TR> + <TD ALIGN="right">Referring customer</TD> + <TD> + <A HREF="<% popurl(1) %>/cust_main.cgi?<% $cust_main->referral_custnum %>"><% $cust_main->referral_custnum %>: <% $referring_cust_main->name %></A> + </TD> + </TR> + <INPUT TYPE="hidden" NAME="referral_custnum" VALUE="<% $cust_main->referral_custnum %>"> + +% } elsif ( ! $conf->exists('disable_customer_referrals') ) { + + <TR> + <TD ALIGN="right">Referring customer</TD> + <TD> + <!-- <INPUT TYPE="text" NAME="referral_custnum" VALUE=""> --> + <% include('/elements/search-cust_main.html', + 'field_name' => 'referral_custnum', + 'curr_value' => $cust_main->referral_custnum, + ) + %> + </TD> + </TR> + +% } else { + <INPUT TYPE="hidden" NAME="referral_custnum" VALUE=""> +% } + +%# signup date +% if ( $conf->exists('cust_main-edit_signupdate') ) { + <% include('/elements/tr-input-date-field.html', { + 'name' => 'signupdate', + 'value' => $cust_main->signupdate, + 'label' => 'Signup date', + 'format' => ( $conf->config('date_format') || "%m/%d/%Y" ), + }) + %> +% } + +</TABLE> + +<%init> + +my( $cust_main, %opt ) = @_; + +my $custnum = $opt{'custnum'}; + +my $conf = new FS::Conf; + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $r = qq!<font color="#ff0000">*</font> !; + +</%init> diff --git a/httemplate/edit/cust_main_attach.cgi b/httemplate/edit/cust_main_attach.cgi new file mode 100755 index 000000000..ebbaf3cf3 --- /dev/null +++ b/httemplate/edit/cust_main_attach.cgi @@ -0,0 +1,70 @@ +<% include('/elements/header-popup.html', "$action File Attachment") %> + +<% include('/elements/error.html') %> + +<FORM NAME="attach_edit" ACTION="<% popurl(1) %>process/cust_main_attach.cgi" METHOD=POST ENCTYPE="multipart/form-data"> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> +<INPUT TYPE="hidden" NAME="attachnum" VALUE="<% $attachnum %>"> + +<BR><BR> + +<TABLE BGCOLOR="#cccccc" CELLSPACING=0> +% if(defined $attach) { +% if($curuser->access_right("Download attachment")) { +<A HREF="<% $p.'view/attachment.html?'.$attachnum %>">Download this file</A><BR> +% } +<TR><TD> Filename </TD> +<TD><INPUT TYPE="text" NAME="filename" SIZE=40 MAXLENGTH=255 VALUE="<% $attach->filename %>"<% $disabled %>></TD></TR> +<TR><TD> Description </TD> +<TD><INPUT TYPE="text" NAME="title" SIZE=40 MAXLENGTH=80 VALUE="<% $attach->title %>"<% $disabled %></TD></TR> +<TR><TD> MIME type </TD> +<TD><INPUT TYPE="text" NAME="mime_type" VALUE="<% $attach->mime_type %>"<% $disabled %></TD></TR> +<TR><TD> Size </TD><TD><% $attach->size %></TD></TR> +% } +% else { # !defined $attach +<TR><TD> Filename </TD><TD><INPUT TYPE="file" SIZE=24 NAME="file"></TD></TR> +<TR><TD> Description </TD><TD><INPUT TYPE="text" NAME="title" SIZE=40 MAXLENGTH=80></TD></TR> +% } +</TABLE> +<BR> +% if(! $disabled) { +<INPUT TYPE="submit" NAME="submit" + VALUE="<% $attachnum ? "Apply Changes" : "Upload File" %>"> +% } +% if(defined $attach and $curuser->access_right('Delete attachment')) { +<BR> +<INPUT TYPE="submit" NAME="delete" value="Delete File" +onclick="return(confirm('Delete this file?'));"> +% } + +</FORM> +</BODY> +</HTML> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $attachnum = ''; +my $attach; +if ( $cgi->param('error') ) { + #$comment = $cgi->param('comment'); +} elsif ( $cgi->param('attachnum') =~ /^(\d+)$/ ) { + $attachnum = $1; + die "illegal query ". $cgi->keywords unless $attachnum; + $attach = qsearchs('cust_attachment', { 'attachnum' => $attachnum }); + die "no such attachment: ". $attachnum unless $attach; +} + +my $action = $attachnum ? 'Edit' : 'Add'; + +my $disabled=''; +if(! $curuser->access_right("$action attachment")) { + $disabled = ' disabled="disabled"'; +} + +$cgi->param('custnum') =~ /^(\d+)$/ or die "illegal custnum"; +my $custnum = $1; + +</%init> + diff --git a/httemplate/edit/cust_main_county-expand.cgi b/httemplate/edit/cust_main_county-expand.cgi new file mode 100755 index 000000000..265dd1dab --- /dev/null +++ b/httemplate/edit/cust_main_county-expand.cgi @@ -0,0 +1,52 @@ +<% include('/elements/header-popup.html', "Enter $title") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/cust_main_county-expand.cgi" METHOD=POST> + +<INPUT TYPE="hidden" NAME="taxnum" VALUE="<% $taxnum %>"> + +<TEXTAREA NAME="expansion" COLS="50" ROWS="16"><% $expansion |h %></TEXTAREA> + +<BR> +<INPUT TYPE="submit" VALUE="Add <% $title %>"> + +</FORM> +</BODY> +</HTML> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my($taxnum, $expansion); +my($query) = $cgi->keywords; +if ( $cgi->param('error') ) { + $taxnum = $cgi->param('taxnum'); + $expansion = $cgi->param('expansion'); +} else { + $query =~ /^(\d+)$/ + or die "Illegal taxnum (query $query)"; + $taxnum = $1; + $expansion = ''; +} + +my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum}) + or die "cust_main_county.taxnum $taxnum not found"; + +my $title; + +die "Can't expand entry!" if $cust_main_county->city; + +if ( $cust_main_county->county ) { + $title = 'Cities'; +} elsif ( $cust_main_county->state ) { + $title = 'Counties'; +} else { + $title = 'States/Provinces'; +} + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/cust_main_county.html b/httemplate/edit/cust_main_county.html new file mode 100644 index 000000000..11b8e43cd --- /dev/null +++ b/httemplate/edit/cust_main_county.html @@ -0,0 +1,64 @@ +<% include('elements/edit.html', + 'popup' => 1, + 'name' => 'Tax rate', #Edit tax rate + 'table' => 'cust_main_county', + 'labels' => { 'taxnum' => 'Tax', + 'country' => 'Country', + 'state' => 'State', + 'county' => 'County', + 'city' => 'City', + 'taxclass' => 'Tax class', + 'taxname' => 'Tax name', + 'tax' => 'Tax rate', + 'setuptax' => 'This tax not applicable to setup fees', + 'recurtax' => 'This tax not applicable to recurring fees', + 'exempt_amount' => 'Monthly exemption per customer ($25 "Texas tax")', + }, + 'fields' => \@fields, + ) +%> +<%once> + +my $conf = new FS::Conf; + +</%once> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $taxnum; +if ( $cgi->param('error') ) { + $cgi->param('taxnum') =~ /^(\d+)$/ + or die "no taxnum, but error: ". $cgi->param('error'); + $taxnum = $1; +} else { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die 'no taxnum'; + $taxnum = $1; +} + +my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum }) + or die "unknown taxnum $1"; + +my @fields = ( + { field=>'country', type=>'fixed-country', }, + { field=>'state', type=>'fixed-state', }, + { field=>'county', type=>'fixed', }, + { field=>'city', type=>'fixed', }, +); + +push @fields, { field=>'taxclass', type=>'fixed', } + if $conf->exists('enable_taxclasses'); + +push @fields, + 'taxname', + { field=>'tax', type=>'percentage', }, + + { type=>'tablebreak-tr-title', value=>'Exemptions' }, + { field=>'setuptax', type=>'checkbox', value=>'Y', }, + { field=>'recurtax', type=>'checkbox', value=>'Y', }, + { field=>'exempt_amount', type=>'money', }, +; + +</%init> diff --git a/httemplate/edit/cust_main_note.cgi b/httemplate/edit/cust_main_note.cgi new file mode 100755 index 000000000..439c84414 --- /dev/null +++ b/httemplate/edit/cust_main_note.cgi @@ -0,0 +1,51 @@ +<% include('/elements/header-popup.html', "$action Customer Note") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% popurl(1) %>process/cust_main_note.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> +<INPUT TYPE="hidden" NAME="notenum" VALUE="<% $notenum %>"> + +% if( $FS::CurrentUser::CurrentUser->option('disable_html_editor') ) { + <TEXTAREA NAME="comment_plain" ROWS="12" COLS="60"><% + join '', split /<br \/>| /, $comment + %></TEXTAREA> +% } +% else { +<% include('/elements/htmlarea.html', 'field' => 'comment_html', + 'curr_value' => $comment) %> +% } + +<BR><BR> +<INPUT TYPE="submit" VALUE="<% $notenum ? "Apply Changes" : "Add Note" %>"> + +</FORM> +</BODY> +</HTML> + +<%init> + +my $comment; +my $notenum = ''; +if ( $cgi->param('error') ) { + $comment = $cgi->param('comment'); +} elsif ( $cgi->param('notenum') =~ /^(\d+)$/ ) { + $notenum = $1; + die "illegal query ". $cgi->keywords unless $notenum; + my $note = qsearchs('cust_main_note', { 'notenum' => $notenum }); + die "no such note: ". $notenum unless $note; + $comment = $note->comments; +} + +$comment =~ s/\r//g; # remove weird line breaks to protect FCKeditor + +$cgi->param('custnum') =~ /^(\d+)$/ or die "illeagl custnum"; +my $custnum = $1; + +my $action = $notenum ? 'Edit' : 'Add'; + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right("$action customer note"); + +</%init> + diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi new file mode 100755 index 000000000..cc4ec605d --- /dev/null +++ b/httemplate/edit/cust_pay.cgi @@ -0,0 +1,149 @@ +% if ( $link eq 'popup' ) { + <% include('/elements/header-popup.html', $title ) %> +% } else { + <% include("/elements/header.html", $title, '') %> +% } + +<% include('/elements/init_calendar.html') %> + +<% include('/elements/error.html') %> + +% unless ( $link eq 'popup' ) { + <% small_custview($custnum, $conf->config('countrydefault')) %> +% } + +<FORM NAME="PaymentForm" ACTION="<% popurl(1) %>process/cust_pay.cgi" METHOD=POST onSubmit="document.PaymentForm.submit.disabled=true"> +<INPUT TYPE="hidden" NAME="link" VALUE="<% $link %>"> +<INPUT TYPE="hidden" NAME="linknum" VALUE="<% $linknum %>"> +<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>"> +<INPUT TYPE="hidden" NAME="paybatch" VALUE="<% $paybatch %>"> + +<BR><BR> + +Payment +<% ntable("#cccccc", 2) %> + +<TR> + <TD ALIGN="right">Date</TD> + <TD COLSPAN=2> + <INPUT TYPE="text" NAME="_date" ID="_date_text" VALUE="<% time2str($date_format.' %r',$_date) %>"> + <IMG SRC="../images/calendar.png" ID="_date_button" STYLE="cursor: pointer" TITLE="Select date"> + </TD> +</TR> + +<SCRIPT TYPE="text/javascript"> + Calendar.setup({ + inputField: "_date_text", + ifFormat: "<% $date_format %>", + button: "_date_button", + align: "BR" + }); +</SCRIPT> + +<TR> + <TD ALIGN="right">Amount</TD> + <TD BGCOLOR="#ffffff" ALIGN="right"><% $money_char %></TD> + <TD><INPUT TYPE="text" NAME="paid" VALUE="<% $paid %>" SIZE=8 MAXLENGTH=8> by <B><% FS::payby->payname($payby) %></B></TD> +</TR> + +% if ( $payby eq 'BILL' ) { + <TR> + <TD ALIGN="right">Check #</TD> + <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD> + </TR> +% } + +<TR> +% if ( $link eq 'custnum' || $link eq 'popup' ) { + + <TD ALIGN="right">Auto-apply<BR>to invoices</TD> + <TD COLSPAN=2> + <SELECT NAME="apply"> + <OPTION VALUE="yes" SELECTED>yes + <OPTION>no</SELECT> + </TD> + +% } elsif ( $link eq 'invnum' ) { + + <TD ALIGN="right">Apply to</TD> + <TD COLSPAN=2 BGCOLOR="#ffffff">Invoice #<B><% $linknum %></B> only</TD> + <INPUT TYPE="hidden" NAME="apply" VALUE="no"> + +% } +</TR> + +% if ( $conf->exists('pkg-balances') ) { + <% include('/elements/tr-select-cust_pkg-balances.html', + 'custnum' => $custnum, + 'cgi' => $cgi + ) + %> +% } else { + <INPUT TYPE="hidden" NAME="pkgnum" VALUE=""> +% } + +</TABLE> + +<BR> +<INPUT TYPE="submit" VALUE="Post payment"> + +</FORM> + +% if ( $link eq 'popup' ) { + </BODY> + </HTML> +% } else { + <% include('/elements/footer.html') %> +% } + +<%init> + +my $conf = new FS::Conf; + +my $money_char = $conf->config('money_char') || '$'; +my $date_format = $conf->config('date_format') || '%m/%d/%Y'; + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Post payment'); + +my($link, $linknum, $paid, $payby, $payinfo, $_date); +if ( $cgi->param('error') ) { + $link = $cgi->param('link'); + $linknum = $cgi->param('linknum'); + $paid = $cgi->param('paid'); + $payby = $cgi->param('payby'); + $payinfo = $cgi->param('payinfo'); + $_date = $cgi->param('_date') ? parse_datetime($cgi->param('_date')) : time; +} elsif ( $cgi->param('custnum') =~ /^(\d+)$/ ) { + $link = $cgi->param('popup') ? 'popup' : 'custnum'; + $linknum = $1; + $paid = ''; + $payby = $cgi->param('payby') || 'BILL'; + $payinfo = ''; + $_date = time; +} elsif ( $cgi->param('invnum') =~ /^(\d+)$/ ) { + $link = 'invnum'; + $linknum = $1; + $paid = ''; + $payby = $cgi->param('payby') || 'BILL'; + $payinfo = ""; + $_date = time; +} else { + die "illegal query ". $cgi->keywords; +} + +my $paybatch = "webui-$_date-$$-". rand() * 2**32; + +my $title = 'Post '. FS::payby->payname($payby). ' payment'; +$title .= " against Invoice #$linknum" if $link eq 'invnum'; + +my $custnum; +if ( $link eq 'invnum' ) { + my $cust_bill = qsearchs('cust_bill', { 'invnum' => $linknum } ) + or die "unknown invnum $linknum"; + $custnum = $cust_bill->custnum; +} elsif ( $link eq 'custnum' || $link eq 'popup' ) { + $custnum = $linknum; +} + +</%init> diff --git a/httemplate/edit/cust_pay_pending.html b/httemplate/edit/cust_pay_pending.html new file mode 100644 index 000000000..0916a1c0c --- /dev/null +++ b/httemplate/edit/cust_pay_pending.html @@ -0,0 +1,154 @@ +<% include('/elements/header-popup.html', $title ) %> + +% if ( $action eq 'delete' ) { + + <CENTER><FONT SIZE="+1"><B>Are you sure you want to delete this pending payment?</B></FONT></CENTER> + +% } elsif ( $action eq 'complete' ) { + + <CENTER><FONT SIZE="+1"><B>No response was received from <% $cust_pay_pending->processor || 'the payment gateway' %> for this transaction. Check <% $cust_pay_pending->processor || 'the payment gateway' %>'s reporting and determine if this transaction completed successfully.</B></FONT></CENTER> + +% } + +<BR> + +%#false laziness w/view/cust_pay.html +<% include('/elements/small_custview.html', + $cust_pay_pending->custnum, + scalar($conf->config('countrydefault')), + 1, #no balance + ) +%> +<BR> + +<% ntable("#cccccc", 2) %> + +<TR> + <TD ALIGN="right">Pending payment#</TD> + <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->paypendingnum %></B></TD> +</TR> + +<TR> + <TD ALIGN="right">Date</TD> + <TD BGCOLOR="#FFFFFF"><B><% time2str"%a %b %o, %Y %r", $cust_pay_pending->_date %></B></TD> +</TR> + +<TR> + <TD ALIGN="right">Amount</TD> + <TD BGCOLOR="#FFFFFF"><B><% $money_char. $cust_pay_pending->paid %></B></TD> +</TR> + +<TR> + <TD ALIGN="right">Payment method</TD> + <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->payby_name %> #<% $cust_pay_pending->paymask %></B></TD> +</TR> + +% #if ( $cust_pay_pending->payby =~ /^(CARD|CHEK|LECB)$/ && $cust_pay_pending->paybatch ) { + + <TR> + <TD ALIGN="right">Processor</TD> + <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->processor %></B></TD> + </TR> + + <TR> + <TD ALIGN="right">Authorization#</TD> + <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->authorization %></B></TD> + </TR> + +% if ( $cust_pay_pending->order_number ) { + <TR> + <TD ALIGN="right">Order#</TD> + <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->order_number %></B></TD> + </TR> +% } + +% #} + +</TABLE> + +<BR> + +<FORM NAME = "pendingform" + METHOD = "POST" + ACTION = "process/cust_pay_pending.html" +> + +<INPUT TYPE="hidden" NAME="paypendingnum" VALUE="<% $paypendingnum %>"> + +<% itable() %> + +% if ( $action eq 'delete' ) { + + <INPUT TYPE="hidden" NAME="action" VALUE="<% $action %>"> + + <TR> + <TD ALIGN="center"> + <BUTTON TYPE="button" onClick="document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, delete payment</BUTTON> + </TD> + <TD> </TD> + <TD ALIGN="center"> + <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, cancel deletion</BUTTON> + </TD> + </TR> + +% } elsif ( $action eq 'complete' ) { + + <INPUT TYPE="hidden" NAME="action" VALUE=""> + + <TR> + <TD ALIGN="center"> + <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'insert_cust_pay'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, transaction completed sucessfully.</BUTTON> + </TD> + <TD> </TD> + <TD ALIGN="center"> + <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'decline'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was declined</BUTTON> + </TD> + <TD> </TD> + <TD ALIGN="center"> + <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'delete'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was not received</BUTTON> + </TD> + </TR> + + <TR><TD COLSPAN=5></TD></TR> + + <TR> + <TD COLSPAN=5 ALIGN="center"> + <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->Cancel payment completion; transaction status not yet known</BUTTON> + </TD> + </TR> + +% } + +</TABLE> + +</FORM> +</BODY> +</HTML> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Edit customer pending payments'); + +$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action'; +my $action = $1; +my $title = ucfirst($action). ' pending payment'; + +$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum'; +my $paypendingnum = $1; +my $cust_pay_pending = + qsearchs({ + 'select' => 'cust_pay_pending.*', + 'table' => 'cust_pay_pending', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'paypendingnum' => $paypendingnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, + }) + or die 'unknown paypendingnum'; + +my $conf = new FS::Conf; + +my $money_char = $conf->config('money_char') || '$'; + +</%init> diff --git a/httemplate/edit/cust_pay_refund.cgi b/httemplate/edit/cust_pay_refund.cgi new file mode 100755 index 000000000..f82fe36fe --- /dev/null +++ b/httemplate/edit/cust_pay_refund.cgi @@ -0,0 +1,14 @@ +<% include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_pay_refund.cgi', + 'src_table' => 'cust_pay', + 'src_thing' => 'payment', + 'dst_table' => 'cust_refund', + 'dst_thing' => 'refund', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/cust_pkg.cgi b/httemplate/edit/cust_pkg.cgi new file mode 100755 index 000000000..dd1ed335f --- /dev/null +++ b/httemplate/edit/cust_pkg.cgi @@ -0,0 +1,150 @@ +<% include('/elements/header.html', "Add/Edit Packages", '') %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/cust_pkg.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="action" VALUE="bulk"> +<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) + <TABLE> + <TR STYLE="background-color: #cccccc;"> + <TH COLSPAN="2">Pkg #</TH> + <TH>Package description</TH> + </TR> + <BR><BR> +% +% +% foreach ( sort { $all_pkg{ $a->getfield('pkgpart') } +% cmp $all_pkg{ $b->getfield('pkgpart') } +% } +% @cust_pkg +% ) +% { +% my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') ); +% my $checked = $remove_pkg{$pkgnum} ? ' CHECKED' : ''; +% +% + + + <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> + </TR> +% } + + + </TABLE> + <BR><BR> +% } + + +Order new packages +<BR><BR> +% +%my $cust_main = qsearchs('cust_main',{'custnum'=>$custnum}); +%my $agent = qsearchs('agent',{'agentnum'=> $cust_main->agentnum }); +% +%my %agent_pkgs = map { ( $_->pkgpart , $all_pkg{$_->pkgpart} ) } +% qsearch('type_pkgs',{'typenum'=> $agent->typenum }); +% +%my $count = 0; +%my $pkgparts = 0; +% + + +<TABLE> + <TR STYLE="background-color: #cccccc;"> + <TH>Qty.</TH> + <TH COLSPAN="2">Package Description</TH> + </TR> +% +%#foreach my $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) { +%foreach my $pkgpart ( sort { $agent_pkgs{$a} cmp $agent_pkgs{$b} } +% keys(%agent_pkgs) ) { +% $pkgparts++; +% next unless exists $pkg{$pkgpart}; #skip disabled ones +% #print qq!<TR>! if ( $count == 0 ); +% my $value = $cgi->param("pkg$pkgpart") || 0; +% + + + <TR> + <TD> + <INPUT TYPE="text" NAME="<% "pkg$pkgpart" %>" VALUE="<% $value %>" SIZE="2" MAXLENGTH="2"> + </TD> + <TD ALIGN="right"><% $pkgpart %>:</TD> + <TD><% $pkg{$pkgpart} %> - <% $comment{$pkgpart}%></TD> + </TR> +% +% $count ++ ; +% #if ( $count == 2 ) { +% # print qq!</TR>\n! ; +% # $count = 0; +% #} +%} +% + + +</TABLE> +% unless ( $pkgparts ) { +% my $p2 = popurl(2); +% my $typenum = $agent->typenum; +% my $agent_type = qsearchs( 'agent_type', { 'typenum' => $typenum } ); +% my $atype = $agent_type->atype; +% + + + (No <A HREF="<% $p2 %>browse/part_pkg.cgi">package definitions</A>, + or agent type + <A HREF="<% $p2 %>edit/agent_type.cgi?<% $typenum %>"><% $atype %></a> + is not allowed to purchase any packages.) +% } + + +<P><INPUT TYPE="submit" VALUE="Order"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages'); + +my %pkg = (); +my %comment = (); +my %all_pkg = (); +my %all_comment = (); +#foreach (qsearch('part_pkg', { 'disabled' => '' })) { +# $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); +# $comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment'); +#} +foreach (qsearch('part_pkg', {} )) { + $all_pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); + $all_comment{ $_ -> getfield('pkgpart') } = $_->custom_comment; + next if $_->disabled; + $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); + $comment{ $_ -> getfield('pkgpart') } = $_->custom_comment; +} + +my($custnum, %remove_pkg); +if ( $cgi->param('error') ) { + $custnum = $cgi->param('custnum'); + %remove_pkg = map { $_ => 1 } $cgi->param('remove_pkg'); +} else { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/; + $custnum = $1; + %remove_pkg = (); +} + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/cust_pkg_detail.html new file mode 100644 index 000000000..009ed5c6e --- /dev/null +++ b/httemplate/edit/cust_pkg_detail.html @@ -0,0 +1,142 @@ +<% include("/elements/header-popup.html", $title, '', + ( $cgi->param('error') ? '' : 'onload="addRow()"' ), + ) +%> + +%# <% include('/elements/error.html') %> + +<FORM ACTION="process/cust_pkg_detail.html" NAME="DetailForm" ID="DetailForm" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<INPUT TYPE="hidden" NAME="detailtype" VALUE="<% $detailtype %>"> + +<TABLE ID="DetailTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=1 STYLE="background-color: #cccccc"> + +% if ( $curuser->option('show_pkgnum') ) { + + <TR> + <TD ALIGN="right">Package #</TD> + <TD BGCOLOR="#ffffff"><% $pkgnum %></TD> + </TR> + +% } + + <TR> + <TD ALIGN="right">Package</TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD> + </TR> + + <TR> + <TD ALIGN="right">Comment</TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD> + </TR> + + <TR> + <TD ALIGN="right">Status</TD> + <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_pkg->statuscolor %>"><B><% ucfirst($cust_pkg->status) %></B></FONT></TD> + </TR> + + <TR> + <TD COLSPAN=2><% ucfirst($name{$detailtype}) %>: </TD> + </TR> + +% my $row = 0; +% for ( @details ) { + + <TR> + <TD></TD> + <TD> + <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup = "possiblyAddRow;" > + </TD> + </TR> + +% } + +</TABLE> + +<BR> +<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="<% $title %>"> + +</FORM> + +<SCRIPT TYPE="text/javascript"> + + var rownum = <% $row %>; + + function possiblyAddRow() { + if ( ( rownum - this.getAttribute('rownum') ) == 1 ) { + addRow(); + } + } + + function addRow() { + + var table = document.getElementById('DetailTable'); + var tablebody = table.getElementsByTagName('tbody').item(0); + + var row = document.createElement('TR'); + + var empty_cell = document.createElement('TD'); + row.appendChild(empty_cell); + + var detail_cell = document.createElement('TD'); + + var detail_input = document.createElement('INPUT'); + detail_input.setAttribute('name', 'detail'+rownum); + detail_input.setAttribute('id', 'detail'+rownum); + detail_input.setAttribute('size', 60); + detail_input.setAttribute('maxLength', 65); + detail_input.setAttribute('rownum', rownum); + detail_input.onkeyup = possiblyAddRow; + detail_cell.appendChild(detail_input); + + row.appendChild(detail_cell); + + tablebody.appendChild(row); + + rownum++; + + } + +</SCRIPT> + +</BODY> +</HTML> +<%init> + +my %access_right = ( + 'I' => 'Edit customer package invoice details', + 'C' => 'Edit customer package comments', +); + +my %name = ( + 'I' => 'invoice details', + 'C' => 'package comments', +); + +my $curuser = $FS::CurrentUser::CurrentUser; + +$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype'; +my $detailtype = $1; + +my $right = $access_right{$detailtype}; +die "access denied" + unless $curuser->access_right($right); + +$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, +}); + +my $part_pkg = $cust_pkg->part_pkg; + +my @details = $cust_pkg->cust_pkg_detail($detailtype); + +my $title = ( scalar(@details) ? 'Edit ' : 'Add ' ). $name{$detailtype}; + +</%init> diff --git a/httemplate/edit/cust_pkg_discount.html b/httemplate/edit/cust_pkg_discount.html new file mode 100755 index 000000000..0bb84b8f2 --- /dev/null +++ b/httemplate/edit/cust_pkg_discount.html @@ -0,0 +1,76 @@ +<% include('/elements/header-popup.html', "Discount Package") %> + +<SCRIPT TYPE="text/javascript"> + + function enable_discount_pkg () { + if ( document.DiscountPkgForm.discountnum.selectedIndex > 0 ) { + document.DiscountPkgForm.submit.disabled = false; + } else { + document.DiscountPkgForm.submit.disabled = true; + } + } + +</SCRIPT> + +<% include('/elements/error.html') %> + +<FORM NAME="DiscountPkgForm" ACTION="<% $p %>edit/process/cust_pkg_discount.html" METHOD=POST> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> + +<% ntable('#cccccc') %> + + <TR> + <TH ALIGN="right">Current package </TH> + <TD COLSPAN=7> + <% $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : '' %><B><% $part_pkg->pkg |h %></B> - <% $part_pkg->comment |h %> + </TD> + </TR> + +<% include('/elements/tr-select-discount.html', + 'empty_label' => ( $pkgdiscountnum ? '' : 'Select discount' ), + 'onchange' => 'enable_discount_pkg()', + 'cgi' => $cgi, + ) +%> + +</TABLE> + +<BR> +<INPUT NAME="submit" TYPE="submit" VALUE="Discount package" <% $pkgdiscountnum ? '' : 'DISABLED' %>> + +</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('Discount customer package'); + +my $pkgnum = scalar($cgi->param('pkgnum')); +$pkgnum =~ /^(\d+)$/ or die "illegal pkgnum $pkgnum"; +$pkgnum = $1; + +my $pkgdiscountnum = ''; + +my $cust_pkg = + qsearchs({ + 'table' => 'cust_pkg', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'pkgnum' => $pkgnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, + }) or die "unknown pkgnum $pkgnum"; + +#my $cust_main = $cust_pkg->cust_main +# or die "can't get cust_main record for custnum ". $cust_pkg->custnum. +# " ( pkgnum ". cust_pkg->pkgnum. ")"; + +my $part_pkg = $cust_pkg->part_pkg; + +</%init> diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi new file mode 100755 index 000000000..59417b4c4 --- /dev/null +++ b/httemplate/edit/cust_refund.cgi @@ -0,0 +1,167 @@ +% if ( $link eq 'popup' ) { + <% include('/elements/header-popup.html', $title ) %> +% } else { + <% include("/elements/header.html", $title, '') %> +% } + +<% include('/elements/error.html') %> + +% unless ( $link eq 'popup' ) { + <% small_custview($custnum, $conf->config('countrydefault')) %> +% } + +<FORM NAME="RefundForm" ACTION="<% $p1 %>process/cust_refund.cgi" METHOD=POST onSubmit="document.RefundForm.submit.disabled=true"> +<INPUT TYPE="hidden" NAME="popup" VALUE="<% $link %>"> +<INPUT TYPE="hidden" NAME="refundnum" VALUE=""> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> +<INPUT TYPE="hidden" NAME="paynum" VALUE="<% $paynum %>"> +<INPUT TYPE="hidden" NAME="_date" VALUE="<% $_date %>"> +<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>"> +<INPUT TYPE="hidden" NAME="paybatch" VALUE=""> +<INPUT TYPE="hidden" NAME="credited" VALUE=""> + +<BR> + +% if ( $cust_pay ) { +% +% #false laziness w/FS/FS/cust_pay.pm +% my $payby = FS::payby->payname($cust_pay->payby); +% my $paymask = $cust_pay->paymask; +% my $paydate = $cust_pay->paydate; +% if ( $cgi->param('error') ) { +% $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01'; +% $paydate = '' unless ($paydate =~ /^\d{2,4}-\d{1,2}-01$'/); +% } + + <BR>Payment + <% ntable("#cccccc", 2) %> + + <TR> + <TD ALIGN="right">Amount</TD><TD BGCOLOR="#ffffff">$<% $cust_pay->paid %></TD> + </TR> + + <TR> + <TD ALIGN="right">Date</TD><TD BGCOLOR="#ffffff"><% time2str($date_format, $cust_pay->_date) %></TD> + </TR> + + <TR> + <TD ALIGN="right">Method</TD><TD BGCOLOR="#ffffff"><% $payby %> # <% $paymask %></TD> + </TR> + +% unless ( $paydate || $cust_pay->payby ne 'CARD' ) { # possibly other reasons: i.e. card has since expired + <TR> + <TD ALIGN="right">Expiration</TD><TD BGCOLOR="#ffffff"> + <% include( '/elements/select-month_year.html', + 'prefix' => 'exp', + 'selected_date' => $paydate, + 'empty_option' => !$paydate, + ) %> + </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 ); +% + + + <TR> + <TD ALIGN="right">Processor</TD><TD BGCOLOR="#ffffff"><% $processor %></TD> + </TR> +% if ( length($auth) ) { + + <TR> + <TD ALIGN="right">Authorization</TD><TD BGCOLOR="#ffffff"><% $auth %></TD> + </TR> +% } +% if ( length($order_number) ) { + + <TR> + <TD ALIGN="right">Order number</TD><TD BGCOLOR="#ffffff"><% $order_number %></TD> + </TR> +% } +% } + + </TABLE> +% } + + +<BR>Refund +<% ntable("#cccccc", 2) %> + + <TR> + <TD ALIGN="right">Date</TD> + <TD BGCOLOR="#ffffff"><% time2str($date_format, $_date) %></TD> + </TR> + + <TR> + <TD ALIGN="right">Amount</TD> + <TD BGCOLOR="#ffffff">$<INPUT TYPE="text" NAME="refund" VALUE="<% $refund %>" SIZE=8 MAXLENGTH=8> by <B><% FS::payby->payname($payby) %></B></TD> + </TR> + +% if ( $payby eq 'BILL' ) { + <TR> + <TD ALIGN="right">Check #</TD> + <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="payinfo" VALUE=""> +% } + + <TR> + <TD ALIGN="right">Reason</TD> + <TD BGCOLOR="#ffffff"><INPUT TYPE="text" NAME="reason" VALUE="<% $reason %>"></TD> + </TR> +</TABLE> + +<BR> +<INPUT TYPE="submit" NAME="submit" VALUE="Post refund"> + +</FORM> + +% if ( $link eq 'popup' ) { + </BODY> + </HTML> +% } else { + <% include('/elements/footer.html') %> +% } + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Refund payment'); + +my $conf = new FS::Conf; +my $date_format = $conf->config('date_format') || '%m/%d/%Y'; + +my $custnum = $cgi->param('custnum'); +my $refund = $cgi->param('refund'); +my $payby = $cgi->param('payby'); +my $payinfo = $cgi->param('payinfo'); +my $reason = $cgi->param('reason'); +my $link = $cgi->param('popup') ? 'popup' : ''; + +my( $paynum, $cust_pay ) = ( '', '' ); +if ( $cgi->param('paynum') =~ /^(\d+)$/ ) { + $paynum = $1; + $cust_pay = qsearchs('cust_pay', { paynum=>$paynum } ) + or die "unknown payment # $paynum"; + $refund ||= $cust_pay->unrefunded; + if ( $custnum ) { + die "payment # $paynum is not for specified customer # $custnum" + unless $custnum == $cust_pay->custnum; + } else { + $custnum = $cust_pay->custnum; + } +} +die "no custnum or paynum specified!" unless $custnum; + +my $_date = time; + +my $p1 = popurl(1); + +my $title = 'Refund '. FS::payby->payname($payby). ' payment'; + +</%init> diff --git a/httemplate/edit/cust_tax_adjustment.html b/httemplate/edit/cust_tax_adjustment.html new file mode 100644 index 000000000..9d4afbc60 --- /dev/null +++ b/httemplate/edit/cust_tax_adjustment.html @@ -0,0 +1,102 @@ +<% include('/elements/header-popup.html', 'Tax adjustment' ) %> + +<% include('/elements/error.html') %> + +<SCRIPT TYPE="text/javascript"> + +function enable_tax_adjustment () { + if ( document.TaxAdjustmentForm.amount.value + && document.TaxAdjustmentForm.taxname.selectedIndex > 0 ) { + document.TaxAdjustmentForm.submit.disabled = false; + } else { + document.TaxAdjustmentForm.submit.disabled = true; + } +} + +function validate_tax_adjustment () { + var comment = document.TaxAdjustmentForm.comment.value; + var comment_regex = /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ ; + var amount = document.TaxAdjustmentForm.amount.value; + var amount_regex = /^\s*\$?\s*(\d*(\.?\d{1,2}))\s*$/ ; + var rval = true; + + if ( ! amount_regex.test(amount) ) { + alert('Illegal amount - enter the amount of the tax adjustment, for example, "5" or "43" or "21.46".'); + return false; + } + if ( ! comment_regex.test(comment) ) { + alert('Illegal comment - spaces, letters, numbers, and the following punctuation characters are allowed: . , ! ? @ # $ % & ( ) - + ; : ' + "'" + ' " = [ ]' ); + return false; + } + + return true; +} + +</SCRIPT> + +<FORM ACTION="process/cust_tax_adjustment.html" NAME="TaxAdjustmentForm" ID="TaxAdjustmentForm" METHOD="POST" onsubmit="document.TaxAdjustmentForm.submit.disabled=true;return validate_tax_adjustment();"> + +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> + +<TABLE ID="TaxAdjustmentTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 STYLE="background-color: #cccccc"> + +<TR> + <TD ALIGN="right">Tax </TD> + <TD> + <SELECT NAME="taxname" ID="taxname" onChange="enable_tax_adjustment()" onKeyPress="enable_tax_adjustment()"> + <OPTION VALUE=""></OPTION> +% foreach my $taxname (@taxname) { + <OPTION VALUE="<% $taxname %>"><% $taxname %></OPTION> +% } + </SELECT> + </TD> +</TR> + +<TR> + <TD ALIGN="right">Amount </TD> + <TD> + $<INPUT TYPE="text" NAME="amount" SIZE=6 VALUE="<% $amount %>" onChange="enable_tax_adjustment()" onKeyPress="enable_tax_adjustment()"> + </TD> +</TR> + +<TR> + <TD ALIGN="right">Comment </TD> + <TD> + <INPUT TYPE="text" NAME="comment" SIZE="50" MAXLENGTH="50" VALUE="<% $comment %>" onChange="enable_tax_adjustment()" onKeyPress="enable_tax_adjustment()"> + </TD> +</TR> + +</TABLE> + +<BR> +<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="Add tax adjustment" <% $cgi->param('error') ? '' :' DISABLED' %>> + +</FORM> + +</BODY> +</HTML> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Add customer tax adjustment'); + +my $sql = 'SELECT DISTINCT(taxname) FROM cust_main_county'; +my $sth = dbh->prepare($sql) or die dbh->errstr; +$sth->execute() or die $sth->errstr; +my @taxname = map { $_->[0] || 'Tax' } @{ $sth->fetchall_arrayref([]) }; + +my $conf = new FS::Conf; + +$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum'; +my $custnum = $1; + +my $amount = ''; +if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) { + $amount = $1; +} + +$cgi->param('comment') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ + or die 'illegal description'; +my $comment = $1; + +</%init> diff --git a/httemplate/edit/discount.html b/httemplate/edit/discount.html new file mode 100644 index 000000000..6e0d8e1a7 --- /dev/null +++ b/httemplate/edit/discount.html @@ -0,0 +1,139 @@ +<% include( 'elements/edit.html', + 'name' => 'Discount', + 'table' => 'discount', + 'fields' => [ + 'name', + { field => 'disabled', type => 'checkbox', value=>'Y', }, + # a weird kind of false laziness + # w/elements/tr-select-discount.html + { field => '_type', type => 'select', + options => \@_type_options, + onchange => '_type_changed', + }, + { field => 'amount', type => 'money', + default => '0.00', + #cell_style => $amount_style, + }, + { field => 'percent', type => 'percentage', + default => 0, + #cell_style => $percent_style, + }, + { field => 'months', type => 'text', size => 2, + postfix => '<BR><FONT SIZE="-1"><I>(blank for non-expiring discount)</I></FONT>', + }, + ], + 'labels' => { + 'discountnum' => 'Discount #', + 'name' => 'Name ', + 'disabled' => 'Disabled ', + '_type' => 'Type ', + 'amount' => 'Amount ', + 'percent' => 'Percentage ', + 'months' => 'Duration (months)', + }, + 'viewall_dir' => 'browse', + 'new_callback' => $new_callback, + 'edit_callback' => $edit_callback, + 'error_callback' => $error_callback, + 'html_init' => $javascript, + 'body_etc' => 'onLoad="_type_changed(document.edit_topform._type)"', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my @_type_options = ( 'Amount', 'Percentage' ); + +#my $amount_style = ''; +#my $percent_style = ''; + +#my $hide = 'display:none;visibility:hidden'; +my $select = 'Select discount type'; + +my $new_callback = sub { + #my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; + + #$amount_style = $hide; + #$percent_style = $hide; + unshift @_type_options, $select; +}; + +my $edit_callback = sub { + #my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; + my( $cgi, $object ) = @_; + + if ( $object->amount > 0 && $object->percent == 0 ) { + $object->set('_type', 'Amount'); + #$percent_style = $hide; + } elsif ( $object->amount == 0 && $object->percent > 0 ) { + $object->set('_type', 'Percentage'); + #$amount_style = $hide; + } elsif ( $object->amount == 0 && $object->percent == 0 ) { + #$amount_style = $hide; + #$percent_style = $hide; + unshift @_type_options, $select; + } else { + die "discount.amount and discount.percent not yet handled by web UI"; + } + +}; + +my $error_callback = sub { + #my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; + my( $cgi, $object ) = @_; + + if ( $cgi->param('_type') eq 'Amount' ) { + $object->set('_type', 'Amount'); + #A$percent_style = $hide; + } elsif ( $cgi->param('_type') eq 'Percentage' ) { + $object->set('_type', 'Percentage'); + #$amount_style = $hide; + } else { + #$amount_style = $hide; + #$percent_style = $hide; + unshift @_type_options, $select; + } + +}; + +my $javascript = <<END; + <SCRIPT TYPE="text/javascript"> + function _type_changed(what) { + var _type = what.options[what.selectedIndex].value; + + if ( _type == '$select' ) { + document.getElementById('amount_label').style.display = 'none'; + document.getElementById('amount_label').style.visibility = 'hidden'; + document.getElementById('amount_input0').style.display = 'none'; + document.getElementById('amount_input0').style.visibility = 'hidden'; + document.getElementById('percent_label').style.display = 'none'; + document.getElementById('percent_label').style.visibility = 'hidden'; + document.getElementById('percent_input0').style.display = 'none'; + document.getElementById('percent_input0').style.visibility = 'hidden'; + } else if ( _type == 'Amount' ) { + document.getElementById('amount_label').style.display = ''; + document.getElementById('amount_label').style.visibility = ''; + document.getElementById('amount_input0').style.display = ''; + document.getElementById('amount_input0').style.visibility = ''; + document.getElementById('percent_label').style.display = 'none'; + document.getElementById('percent_label').style.visibility = 'hidden'; + document.getElementById('percent_input0').style.display = 'none'; + document.getElementById('percent_input0').style.visibility = 'hidden'; + } else if ( _type == 'Percentage' ) { + document.getElementById('amount_label').style.display = 'none'; + document.getElementById('amount_label').style.visibility = 'hidden'; + document.getElementById('amount_input0').style.display = 'none'; + document.getElementById('amount_input0').style.visibility = 'hidden'; + document.getElementById('percent_label').style.display = ''; + document.getElementById('percent_label').style.visibility = ''; + document.getElementById('percent_input0').style.display = ''; + document.getElementById('percent_input0').style.visibility = ''; + } + + } + </SCRIPT> +END + +</%init> diff --git a/httemplate/edit/elements/ApplicationCommon.html b/httemplate/edit/elements/ApplicationCommon.html new file mode 100644 index 000000000..7b1050ade --- /dev/null +++ b/httemplate/edit/elements/ApplicationCommon.html @@ -0,0 +1,552 @@ +<%doc> + +Examples: + + #cust_bill_pay + include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_bill_pay.cgi', + 'src_table' => 'cust_pay', + 'src_thing' => 'payment', + 'dst_table' => 'cust_bill', + 'dst_thing' => 'invoice', + ) + + #cust_credit_bill + include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_credit_bill.cgi', + 'src_table' => 'cust_credit', + 'src_thing' => 'credit', + 'dst_table' => 'cust_bill', + 'dst_thing' => 'invoice', + ) + + #cust_pay_refund + include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_pay_refund.cgi', + 'src_table' => 'cust_pay', + 'src_thing' => 'payment', + 'dst_table' => 'cust_refund', + 'dst_thing' => 'refund', + ) + + #cust_credit_refund + include('elements/ApplicationCommon.html', + 'form_action' => 'process/cust_credit_refund.cgi', + 'src_table' => 'cust_credit', + 'src_thing' => 'credit', + 'dst_table' => 'cust_refund', + 'dst_thing' => 'refund', + ) + +</%doc> + +<% include('/elements/header-popup.html', "Apply $src_thing$to", '', 'onLoad="myOnLoadFunction();"') %> + +<% include('/elements/error.html') %> + +<P ID="ErrorMessage"></P> +<FORM ACTION="<% $p1. $opt{'form_action'} %>" NAME="ApplicationForm" ID="ApplicationForm" METHOD=POST> + +<% $src_thing %> #<B><% $src_pkeyvalue %></B><BR> +<INPUT TYPE="hidden" NAME="<% $src_pkey %>" VALUE="<% $src_pkeyvalue %>"> + +<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + +<TR> + <TD ALIGN="right">Date: </TD> + <TD><B><% time2str($date_format, $src->_date) %></B></TD> +</TR> + +<TR> + <TD ALIGN="right">Amount: </TD> + <TD ID="original_amount"><B><% $money_char %><% $src_amount %></B> + </TD> + <TD> +% if ($use_sub_dst_thing && $can_change_credit) { + <INPUT TYPE="hidden" NAME="src_amount" VALUE="<% $src_amount %>" > + <BUTTON TYPE="button" NAME="expand_button" ID="expand_button" onClick="do_change_amount(this);">Change</BUTTON> +% } + </TD> + +</TR> + +<TR> + <TD ALIGN="right">Unapplied amount: </TD> + <TD ID="unapplied_amount"><B><% $money_char %><% $unapplied %></B></TD> +</TR> + +% if ( $src_table eq 'cust_credit' ) { + <TR> + <TD ALIGN="right">Reason: </TD> + <TD COLSPAN=2><B><% $src->reason %></B></TD> + </TR> +% } + +</TABLE> +<BR> + +<SCRIPT TYPE="text/javascript"> +function clear_amounts() { + var rownum=0 + var table = document.getElementById('ApplicationTable'); + for (var row = 2; table.rows[row]; row++) + { + var inputs = table.rows[row].getElementsByTagName('input'); + if ( !inputs.length ) { + break; + } + inputs.item(0).value = ''; // amount + } + +} + +function changed(what) { + dst = what.options[what.selectedIndex].value; + + if ( dst == '' ) { + what.form.submit.disabled=true; +%if ($use_sub_dst_thing && $src_pkey eq 'crednum') { + what.form.tax_button.disabled=true; + what.form.clear_button.disabled=true; +%} + return true; + } + + what.form.submit.disabled=false; +%if ($use_sub_dst_thing && $src_pkey eq 'crednum') { + what.form.tax_button.disabled=false; + what.form.clear_button.disabled=false; +%} + +% foreach my $dst ( @dst ) { + + if ( dst == <% $dst->$dst_pkey %> ) { + what.form.amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>"; +% if ($use_sub_dst_thing) { + what.form.display_amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>"; + + var rownum=0 + var table = document.getElementById('ApplicationTable'); + while(table.rows[2]) { + table.deleteRow(2); + } +% my $app_class = "FS::$link_table"; +% my $temp_app = $app_class->new( +% { $src_pkey => $src_pkeyvalue, +% $dst_pkey => $dst->$dst_pkey, +% 'amount' => min($dst->$dst_unapplied, $unapplied), +% } +% ); +% my %apphash = (); +% my $listref_or_error = $temp_app->calculate_applications; +% %apphash = map { &{$key_generator}($_), $_ } @$listref_or_error +% if ref($listref_or_error); +% foreach my $cbp ( $dst->open_cust_bill_pkg ) { +% my $desc = $cbp->desc; +% my $total_owed = $cbp->owed_setup + $cbp->owed_recur; +% my $key = &{$key_generator}([ $cbp, 0, {} ]); +% my $amount = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0; +% unless ( $cbp->pkgnum ) { +% foreach my $taxX ( $cbp->cust_bill_pkg_tax_Xlocation ) { +% my $pkey = $taxX->primary_key; +% my $owed = $taxX->owed; +% my $key = &{$key_generator}([ $cbp, 0, { $pkey => $taxX->$pkey } ]); +% my $toapp = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0; + <% &{$row_generator}( $key, $cbp, $taxX->desc, $owed, $toapp, $taxX->$pkey ) %> +% $total_owed -= $owed; +% $amount -= $toapp; +% } +% $desc .= ' (default)'; +% } +% if ( $total_owed > 0 ) { + <% &{$row_generator}($key, $cbp, $desc, $total_owed, $amount, '') %> +% } +% } +% } + } + +% } + +} + +function sub_changed(what) { + + var amount = 0; + var table = document.getElementById('ApplicationTable'); + var i = table.rows.length; + while(i-- > 2) { + var inputs = table.rows[i].getElementsByTagName('input'); + if (! inputs.length) { + continue; + } + amount += parseFloat( inputs.item(0).value ) || 0; + } + what.form.amount.value = parseFloat(amount).toFixed(2); + what.form.display_amount.value = parseFloat(amount).toFixed(2); + set_amount_color(what); + +} + +function set_amount_color(what) { + if (what.form.src_amount.value < what.form.amount.value) { + what.form.display_amount.style.color = '#ff0000'; + } else { + what.form.display_amount.style.color = '#00ff00'; + } +} + +</SCRIPT> + +Apply to: + +% if ($use_sub_dst_thing && $src_pkey eq 'crednum') { +<CENTER> + <TABLE> + <TR> + <TD> + <BUTTON TYPE="button" NAME="tax_button" ID="tax_button" onClick="do_calculate_tax(this);" DISABLED>Calculate Tax</BUTTON> + </TD> + <TD> + <BUTTON TYPE="button" NAME="clear_button" ID="clear_button" onClick="clear_amounts(this);" DISABLED>Clear Amounts</BUTTON> + </TD> + </TR> + </TABLE> +</CENTER> +<% include( '/elements/xmlhttp.html', + 'url' => $p.'misc/xmlhttp-calculate_taxes.html', + 'subs' => [ 'calculate_taxes' ], + ) + %> +<SCRIPT TYPE="text/javascript"> + +function show_taxes(arg) { + var argsHash = eval('(' + arg + ')'); + + var button = document.getElementById('tax_button'); + button.disabled = false; + button.innerHTML = 'Calculate Tax'; + button = document.getElementById('clear_button'); + button.disabled = false; + + 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']; + + 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)); + } + +} + +function do_calculate_tax (what) { + what.innerHTML = 'Calculating....'; + what.disabled = true; + var button = document.getElementById('clear_button'); + button.disabled = true; + var taxed_items = new Array(); + var table = document.getElementById('ApplicationTable'); + for (var row = 2; table.rows[row]; row++) + { + var inputs = table.rows[row].getElementsByTagName('input'); + if ( !inputs.length ) { + break; + } + var taxed_item = new Array( + inputs.item(1).value, // billpkgnum + inputs.item(3).value, // s_or_r + inputs.item(0).value || 0 // amount + ); + taxed_items.push(taxed_item); + } + + var args = new Array( + 'crednum', '<% $src_pkeyvalue %>', + 'items', taxed_items + ); + calculate_taxes ( args, show_taxes ); +} + +function do_change_amount (what) { + var amount_cell = document.getElementById('original_amount'); + var inputs = amount_cell.getElementsByTagName('input'); + if (inputs.length) { + src_amount_changed(); + amount_cell.innerHTML = '<B><% $money_char %></B>' + inputs.item(0).value; + } else { + amount_cell.innerHTML = '<% $money_char %>'; + var amount_input = document.createElement('INPUT'); + amount_input.setAttribute('name', 'entered_amount'); + amount_input.setAttribute('id', 'entered_amount'); + amount_input.style.textAlign = 'right'; + amount_input.setAttribute('size', 8); + amount_input.setAttribute('maxlength', 8); + amount_input.setAttribute('value', what.form.src_amount.value); + amount_input.setAttribute('onChange', "src_amount_changed(this);"); + amount_cell.appendChild(amount_input); + } +} + +function src_amount_changed () { + //alert('src_amount_changed called'); + var entered_amount = document.getElementById('entered_amount'); + if ( entered_amount ) { + entered_amount.form.src_amount.value = entered_amount.value; + var unapplied_cell = document.getElementById('unapplied_amount'); + unapplied_cell.innerHTML = '<B><% $money_char %>' + entered_amount.value + '</B>'; + set_amount_color(entered_amount); + } + return true; +} + +</SCRIPT> + +%} + +<TABLE ID="ApplicationTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + +<TR> + <TD ALIGN="right"><% $dst_thing %>: </TD> + <TD><SELECT NAME="<% $dst_pkey %>" SIZE=1 onChange="changed(this)"> +<OPTION VALUE="">Select <% $dst_thing %> + +% foreach my $dst ( @dst ) { + <OPTION<% $dst->$dst_pkey eq $dst_pkeyvalue ? ' SELECTED' : '' %> VALUE="<% $dst->$dst_pkey %>">#<% $dst->$dst_pkey %> - <% time2str($date_format, $dst->_date) %> - $<% $dst->$dst_unapplied %> +% } + +</SELECT> + </TD> +</TR> + +<TR> + <TD ALIGN="right">Amount: </TD> + <TD><% $money_char %><INPUT TYPE="text" NAME="<% $use_sub_dst_thing ? 'display_' : '' %>amount" VALUE="<% $amount %>" SIZE=8 MAXLENGTH=8 <% $use_sub_dst_thing ? 'DISABLED' : '' %> STYLE="text-align:right;"></TD> +% if ($use_sub_dst_thing) { + <INPUT TYPE="hidden" NAME="amount" VALUE="<% $amount %>" > +% } +</TR> + +</TABLE> + +<BR> +<CENTER><INPUT TYPE="submit" + VALUE="Apply" + NAME="submit" + ID="submit" +% if ($use_sub_dst_thing && $can_change_credit) { + onClick="src_amount_changed()" +% } + DISABLED +></CENTER> + +</FORM> + +<SCRIPT TYPE="text/javascript"> + +function myOnLoadFunction () { + <% $onload %> +} + +</SCRIPT> + +<% include('/elements/footer.html') %> + +<%init> + +my %opt = @_; + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; +my $date_format = $conf->config('date_format') || '%m/%d/%Y'; + +my $src_thing = ucfirst($opt{'src_thing'}); +my $src_table = $opt{'src_table'}; +my $src_pkey = dbdef->table($src_table)->primary_key; + +my $dst_thing = ucfirst($opt{'dst_thing'}); +my $dst_table = $opt{'dst_table'}; +my $dst_pkey = dbdef->table($dst_table)->primary_key; +my $dst_unapplied = $dst_table eq 'cust_bill' ? 'owed' : 'unapplied'; + +$opt{form_action} =~ /^process\/(.*)\./ or die "bad form action"; +my $link_table = $1; + +my $use_sub_dst_thing = 0; +$use_sub_dst_thing = 1 + if ( $dst_table eq 'cust_bill' && $conf->exists("${link_table}_pkg-manual") ); + +my $can_change_credit = 0; +$can_change_credit = 1 + if ( $src_table eq 'cust_credit' && + $FS::CurrentUser::CurrentUser->access_right('Post credit') && + $FS::CurrentUser::CurrentUser->access_right('Delete credit') + ); + +my $to = $dst_table eq 'cust_refund' ? ' to Refund' : ''; + +my($src_pkeyvalue, $amount, $dst_pkeyvalue, $src_amount); +if ( $cgi->param('error') ) { + $src_pkeyvalue = $cgi->param($src_pkey); + $amount = $cgi->param('amount'); + $dst_pkeyvalue = $cgi->param($dst_pkey); + $src_amount = $cgi->param('src_amount'); +} else { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/; + $src_pkeyvalue = $1; + $amount = ''; + $dst_pkeyvalue = ''; +} + +my $otaker = getotaker; + +my $p1 = popurl(1); + +my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } ); +die "$src_thing $src_pkeyvalue not found!" unless $src; + +$src_amount = $src->amount unless $cgi->param('error'); + +my $unapplied = $src->unapplied; + +my @dst = sort { $a->_date <=> $b->_date + or $a->$dst_pkey <=> $b->$dst_pkey + } + grep { $_->$dst_unapplied != 0 } + qsearch($dst_table, { 'custnum' => $src->custnum } ); + +my $row_generator = sub { + my ($key, $cust_bill_pkg, $desc, $owed, $amount, $taxXnum) = @_; + my ($num, $s_or_r, $taxlinenum) = split(':', $key); + my $id = $cust_bill_pkg->pkgnum || 'Tax'; + my $billpkgnum = $cust_bill_pkg->billpkgnum; + my $s_or_r = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur'; + + $amount = sprintf("%.2f", $amount); + qq! + var tablebody = document.getElementsByTagName('tbody').item(0); + var row = table.insertRow(rownum+2); + var pkg_cell = document.createElement('TD'); + pkg_cell.style.textAlign = 'right'; + pkg_cell.innerHTML = "$id - $desc - $owed:"; + var amount_cell = document.createElement('TD'); + amount_cell.innerHTML = "$money_char"; + var amount_input = document.createElement('INPUT'); + amount_input.setAttribute('name', 'subamount'+rownum); + amount_input.setAttribute('id', 'subamount'+rownum); + amount_input.style.textAlign = 'right'; + amount_input.setAttribute('size', 8); + amount_input.setAttribute('maxlength', 8); + amount_input.setAttribute('rownum', rownum); + amount_input.setAttribute('value', "$amount"); + amount_input.setAttribute('onChange', "sub_changed(this);"); + amount_cell.appendChild(amount_input); + var subnum_input = document.createElement('INPUT'); + subnum_input.setAttribute('name', 'subnum'+rownum); + subnum_input.setAttribute('id', 'subnum'+rownum); + subnum_input.setAttribute('type', 'hidden'); + subnum_input.setAttribute('rownum', rownum); + subnum_input.setAttribute('value', "$billpkgnum"); + amount_cell.appendChild(subnum_input); + var taxnum_input = document.createElement('INPUT'); + taxnum_input.setAttribute('name', 'taxXlocationnum'+rownum); + taxnum_input.setAttribute('id', 'taxXlocationnum'+rownum); + taxnum_input.setAttribute('type', 'hidden'); + taxnum_input.setAttribute('rownum', rownum); + taxnum_input.setAttribute('value', "$taxXnum"); + amount_cell.appendChild(taxnum_input); + var s_or_r_input = document.createElement('INPUT'); + s_or_r_input.setAttribute('name', 's_or_r'+rownum); + s_or_r_input.setAttribute('id', 's_or_r'+rownum); + s_or_r_input.setAttribute('type', 'hidden'); + s_or_r_input.setAttribute('rownum', rownum); + s_or_r_input.setAttribute('value', "$s_or_r"); + amount_cell.appendChild(s_or_r_input); + var itemdesc_input = document.createElement('INPUT'); + itemdesc_input.setAttribute('name', 'itemdesc'+rownum); + itemdesc_input.setAttribute('id', 'itemdesc'+rownum); + itemdesc_input.setAttribute('type', 'hidden'); + itemdesc_input.setAttribute('rownum', rownum); + itemdesc_input.setAttribute('value', "$desc"); + amount_cell.appendChild(itemdesc_input); + row.appendChild(pkg_cell); + row.appendChild(amount_cell); + rownum++; + !; +}; + +my $key_generator = sub { + my $listref = shift; + my ($cust_bill_pkg, $amount, $hashref) = @$listref; + my $setup_or_recur = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur'; + my $taxlinenum = $hashref->{billpkgtaxlocationnum} || + $hashref->{billpkgtaxratelocationnum} || + ''; + + join(':', $cust_bill_pkg->billpkgnum, $setup_or_recur, $taxlinenum); +}; + +my $onload = 'return true;'; + +if ($cgi->param('error')) { + + my $set_sub_amounts = + join(';', map { "myform.subamount$_.value = ". $cgi->param("subamount$_") } + grep { /.+/ } + map { /^subnum(\d+)$/ ? $1 : '' } + $cgi->param + ); + $set_sub_amounts &&= "$set_sub_amounts;sub_changed(myform.subamount0)"; + + $onload = qq! + var myform = document.getElementById('ApplicationForm'); + changed(myform.elements['$dst_pkey']); + $set_sub_amounts; + return true; + !; +} + +</%init> diff --git a/httemplate/edit/elements/category_Common.html b/httemplate/edit/elements/category_Common.html new file mode 100644 index 000000000..8bbdcd15b --- /dev/null +++ b/httemplate/edit/elements/category_Common.html @@ -0,0 +1,24 @@ +<% include( 'edit.html', + 'fields' => [ + 'categoryname', + 'weight', + { field=>'disabled', type=>'checkbox', value=>'Y', }, + ], + 'labels' => { + 'categorynum' => 'Category number', + 'categoryname' => 'Category name', + 'weight' => 'Weight', + 'disabled' => 'Disable category', + }, + 'viewall_dir' => 'browse', + %opt, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %opt = @_; + +</%init> diff --git a/httemplate/edit/elements/class_Common.html b/httemplate/edit/elements/class_Common.html new file mode 100644 index 000000000..b5f493991 --- /dev/null +++ b/httemplate/edit/elements/class_Common.html @@ -0,0 +1,32 @@ +<% include( 'edit.html', + 'fields' => [ + 'classname', + (scalar(@category) + ? { field=>'categorynum', type=>'select-table', 'empty_label'=>'(none)', 'table'=>'pkg_category', 'name_col'=>'categoryname' } + : { field=>'categorynum', type=>'hidden' } + ), + { field=>'disabled', type=>'checkbox', value=>'Y', }, + ], + 'labels' => { + 'classnum' => 'Class number', + 'classname' => 'Class name', + 'categorynum' => 'Category', + 'disabled' => 'Disable class', + }, + 'viewall_dir' => 'browse', + %opt, + ) + +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %opt = @_; + +my $table = $opt{'table'}; +( my $category_table = $table ) =~ s/class/category/ or die; + +my @category = qsearch($category_table, { 'disabled' => '' }); +</%init> diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html new file mode 100644 index 000000000..b19b3618c --- /dev/null +++ b/httemplate/edit/elements/edit.html @@ -0,0 +1,859 @@ +<%doc> + +Example: + + include( 'elements/edit.html', + 'name_singular' => #singular name for the record + # (preferred, will be pluralized automatically) + 'name' => #name for the record + # (deprecated, will be pluralized simplistically) + 'table' => + + #? 'primary_key' => #required when the dbdef doesn't know...??? + 'labels' => { + 'column' => 'Label', + } + + #listref - each item is a literal column name (or method) or hashref + # or (notyet) coderef + #if not specified all columns (except for the primary key) will be editable + 'fields' => [ + 'columname', + { 'field' => 'another_columname', + 'type' => 'text', #text + #password + #money + #percentage + #checkbox + #select + #selectlayers (can now use after a tablebreak-tr-title... but not inside columnstart/columnnext/columnend) + #title + #tablebreak-tr-title + #columnstart + #columnnext + #columnend + #hidden - hidden value from object + #fixed - display fixed value from object or here + #fixed-country + #fixed-state + 'value' => 'Y', #for checkbox, title, fixed, hidden + 'disabled' => 0, + 'onchange' => 'javascript_function', + + 'include_opt_callback' => sub { my $object = @_; + ( 'option' => 'value', ); + }, + + 'm2name_table' => 'table_name', + 'm2name_namecol' => 'name_column', + #OR# + 'm2m_method' => + #'m2m_srccol' => #opt, if not the same as this table + 'm2m_dstcol' => #required for now, eventuaully opt, if not the same as target table + #OR# + 'o2m_table' => + + 'm2_label' => 'Label', # + 'm2_new_default' => \@table_name_objects, #default + #m2 objects for + #new records + 'm2_error_callback' => sub { my($cgi, $object) = @_; }, + 'm2_remove_warnings' => \%warnings, #hashref of warning + #messages for m2 + #removal + 'm2_new_js' => 'function_name', #javascript function called + #on spawned rows (one arg: + #new_element) + 'm2_remove_js' => 'function_name', #js function called when + #a row is deleted (three + #args: value, text, + #'no_match') + #layer_fields & layer_values_callback only for selectlayer + 'layer_fields' => [ + 'fieldname' => 'Label', + 'another_field' => { + label=>'Label', + type =>'text', #text, money + }, + ], + 'layer_values_callback' => + sub { + my( $cgi, $object ) = @_; + { 'layer' => { 'fieldname' => 'current_value', + 'fieldname2' => 'field2value', + ... + }, + 'layer2' => { 'l2fieldname' => 'l2value', + ... + }, + ... + }; + }, + }, + ] + + 'menubar' => '', #menubar arrayref + + #agent virtualization + 'agent_virt' => 1, + 'agent_null' => 1, #if true, always allow no-agentnum globals + 'agent_null_right' => 'Access Right Name', + 'agent_clone_extra_sql' => '', #if provided, this overrides the extra_sql + #implementing agent virt, for clone + #operations. i.e. pass "1=1" to allow + #cloning anything + + 'viewall_dir' => '', #'search' or 'browse', defaults to 'search' + + # overrides default popurl(1)."process/$table.html" + 'post_url' => popurl(1).'process/something', + + #we're in a popup (no title/menu/searchboxes) + 'popup' => 1, + + ### + # HTML callbacks + ### + + 'body_etc' => '', # Additional BODY attributes, i.e. onLoad="" + + 'html_init' => '', #after the header/menubar + + #string or coderef of additional HTML to add before </TABLE> + 'html_table_bottom' => '', + + #after </TABLE> but before the submit + 'html_bottom' => '', #string + 'html_bottom' => sub { + my $object = shift; + # ... + "html_string"; + }, + + #javascript function name, will be called with form name as arg + 'onsubmit' => 'check_form_data', + + #at the very bottom (well, as low as you can go from here) + 'html_foot' => '', + + ### + # initialization callbacks + ### + + ###global callbacks, always run if provided + + #after decoding long CGI "redirect=" responses but + # before object creation/search + # (useful if you have a long form that might trigger redirect= and you need + # to do things with $cgi params - they're not decoded in the calling + # <%init> block yet) + 'begin_callback' = sub { my( $cgi, $fields_listref, $opt_hashref ) = @_; }, + + #after the mode-specific object creation/search + 'end_callback' = sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + ###mode-specific callbacks. one (and only one) of these four is called + + #run when adding + 'new_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + #run when editing + 'edit_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + #run when re-displaying with an error + 'error_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + #run when cloning + 'clone_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + ###callbacks called in new mode only + + # returns a hashref for the new object + 'new_hashref_callback' + + # returns the new object iself (otherwise, ->new is called) + 'new_object_callback' + + ###display callbacks + + #run before display to return a different value + 'value_callback' => sub { my( $columname, $value ) = @_; }, + + #run before display to manipulate element of the 'fields' arrayref + 'field_callback' => sub { my( $cgi, $object, $field_hashref ) = @_; }, + + ); + +</%doc> + +<% include('/elements/header'. ( $opt{popup} ? '-popup' : '' ). '.html', + $title, + include( '/elements/menubar.html', @menubar ), + $opt{'body_etc'}, + ) +%> + +<% defined($opt{'html_init'}) + ? ( ref($opt{'html_init'}) + ? &{$opt{'html_init'}}() + : $opt{'html_init'} + ) + : '' +%> + +<% include('/elements/error.html') %> + +% my $url = $opt{'post_url'} || popurl(1)."process/$table.html"; + +<FORM NAME = "edit_topform" + METHOD = POST + ACTION = "<% $url %>" + <% $opt{onsubmit} ? 'onSubmit="return '.$opt{onsubmit}.'(this)"' : '' %> +> + +<INPUT TYPE="hidden" NAME="svcdb" VALUE="<% $table %>"> +<INPUT TYPE="hidden" NAME="<% $pkey %>" VALUE="<% $clone ? '' : $object->$pkey() %>"> + +<FONT SIZE="+1"><B> +<% ( $opt{labels} && exists $opt{labels}->{$pkey} ) + ? $opt{labels}->{$pkey} + : $pkey +%> +</B></FONT> +#<% ( !$clone && $object->$pkey() ) || "(NEW)" %> + +% my $tablenum = 0; +<TABLE ID="TableNumber<% $tablenum++ %>" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + +% my $g_row = 0; +% my @g_row_stack = (); +% foreach my $f ( map { ref($_) ? $_ : {'field'=>$_} } +% @$fields +% ) { +% +% my $trash = &{ $opt{'field_callback'} }( $cgi, $object, $f ) +% if $opt{'field_callback'}; +% +% my $field = $f->{'field'}; +% my $type = $f->{'type'} ||= 'text'; +% +% my $label = ( $opt{labels} && exists $opt{labels}->{$field} ) +% ? $opt{labels}->{$field} +% : $field; +% +% my $onchange = $f->{'onchange'}; +% +% my $layer_values = {}; +% $layer_values = &{ $f->{'layer_values_callback'} }( $cgi, $object ) +% if $f->{'layer_values_callback'} +% && ! $f->{'m2name_table'} +% && ! $f->{'o2m_table'} +% && ! $f->{'m2m_method'}; +% +% warn "layer values: ". Dumper($layer_values) +% if $opt{'debug'}; +% +% my %include_common = ( +% +% #text and derivitives +% 'size' => $f->{'size'}, +% 'maxlength' => $f->{'maxlength'}, +% 'postfix' => $f->{'postfix'}, +% +% #checkbox, title, fixed, hidden +% #& deprecated weird value hashref used only by reason.html +% 'value' => $f->{'value'}, +% +% #select(-*) +% 'options' => $f->{'options'}, +% 'labels' => $f->{'labels'}, +% 'multiple' => $f->{'multiple'}, +% 'disable_empty' => $f->{'disable_empty'}, +% #select-reason +% 'reason_class' => $f->{'reason_class'}, +% +% #selectlayers +% 'layer_fields' => $f->{'layer_fields'}, +% 'layer_values' => $layer_values, +% 'html_between' => $f->{'html_between'}, +% +% #umm. for select-agent_types at least +% 'disabled' => $f->{'disabled'}, +% ); +% +% #selectlayers, others? +% $include_common{$_} = $f->{$_} +% foreach grep exists($f->{$_}), +% qw( js_only html_only select_only layers_only cell_style); +% +% #select-* +% $include_common{$_} = $f->{$_} +% foreach grep exists($f->{$_}), qw( empty_label ); +% +% #select-table +% $include_common{$_} = $f->{$_} +% foreach grep exists($f->{$_}), qw( value_col extra_sql ); +% +% #select-table, checkboxes-table +% $include_common{$_} = $f->{$_} +% foreach grep exists($f->{$_}), qw( table name_col ); +% +% #checkboxes-table +% $include_common{$_} = $f->{$_} +% foreach grep exists($f->{$_}), qw( target_table link_table ); +% +% #*-table +% $include_common{$_} = $f->{$_} +% foreach grep exists($f->{$_}), +% qw( hashref agent_virt agent_null agent_null_right ); +% +% #htmlarea +% $include_common{$_} = $f->{$_} +% foreach grep exists($f->{$_}), qw( width height ); +% +% +% if ( $type eq 'tablebreak-tr-title' ) { +% $include_common{'table_id'} = 'TableNumber'. $tablenum++; +% } +% if ( $type eq 'tablebreak-tr-title' || $type eq 'title' ) { +% $include_common{'colspan'} = $f->{colspan} if $f->{colspan}; +% } +% +% if ( $f->{include_opt_callback} ) { +% %include_common = ( %include_common, +% &{ $f->{include_opt_callback} }( $object ) +% ); +% } +% +% my $layer_prefix_on = ''; +% +% my $include_sub = sub { +% my %opt = @_; +% +% my $fieldnum = delete $opt{'fieldnum'}; +% +% my $include = $type; +% $include = "input-$include" if $include =~ /^(text|money|percentage)$/; +% $include = "tr-$include" unless $include =~ /^(hidden|tablebreak|column)/; +% +% $include_common{'layer_prefix'} = "$field$fieldnum." +% if $layer_prefix_on; +% +% my @include = +% ( "/elements/$include.html", +% 'field' => "$field$fieldnum", +% 'id' => "$field$fieldnum", #separate? +% 'label_id' => $field."_label$fieldnum", #don't want field0_label0... +% %include_common, +% %opt, +% ); +% @include; +% }; +% +% my $column_sub = sub { +% my %opt = @_; +% +% my $column = delete($opt{field}); +% my $fieldnum = delete($opt{fieldnum}); +% my $include = delete($opt{type}) || 'text'; +% $include = "input-$include" if $include =~ /^(text|money|percentage)$/; +% +% ( "/elements/$include.html", +% 'field' => $field.'__'.$column.$fieldnum, +% 'id' => $field.'__'.$column.$fieldnum, +% 'layer_prefix' => $field.'__'.$column.$fieldnum.".", +% ( $fieldnum +% ? ('cell_style' => 'border-top:1px solid black') +% : () +% ), +% 'cgi' => $cgi, +% %opt, +% ); +% }; +% +% unless ( $type =~ /^column/ ) { +% $g_row = 1 if $type eq 'tablebreak-tr-title'; +% $g_row++; +% $g_row++ if $type eq 'title'; +% } else { +% if ( $type eq 'columnstart' ) { +% push @g_row_stack, $g_row; +% $g_row = 0; +% #} elsif ( $type eq 'columnnext' ) { +% } elsif ( $type eq 'columnend' ) { +% $g_row = pop @g_row_stack; +% } +% +% } +% +% my $fieldnum = ''; +% my $curr_value = ''; +% if ( $f->{'m2name_table'} || $f->{'o2m_table'} || $f->{'m2m_method'} ) { +% +% my($table, $col); +% if ( $f->{'m2name_table'} ) { +% $table = $f->{'m2name_table'}; +% $col = $f->{'m2name_namecol'}; +% } elsif ( $f->{'o2m_table'} ) { +% $table = $f->{'o2m_table'}; +% $col = dbdef->table($f->{'o2m_table'})->primary_key; +% } elsif ( $f->{'m2m_method'} ) { +% $table = $f->{'m2m_method'}; +% $col = $f->{'m2m_dstcol'}; +% } +% $fieldnum = 0; +% $layer_prefix_on = 1; +% #print out the fields for the existing m2s +% my @existing = (); +% if ( $mode eq 'error' ) { +% @existing = &{ $f->{'m2_error_callback'} }( $cgi, $object ); +% } elsif ( $object->$pkey() ) { # $mode eq 'edit'||'clone' +% @existing = $object->$table(); +% warn scalar(@existing). " from $object->$table: ". join('/', @existing) +% if $opt{'debug'}; +% } elsif ( $f->{'m2_new_default'} ) { # && $mode eq 'new' +% @existing = @{ $f->{'m2_new_default'} }; +% } +% foreach my $name_obj ( @existing ) { +% +% my $ex_label = '<INPUT TYPE="button" VALUE="X" TITLE="Remove this '. +% lc($f->{'m2_label'}). +% qq(" onClick="remove_$field($fieldnum);"). +% ' STYLE="color:#ff0000;font-weight:bold;'. +% 'padding-left:2px;padding-right:2px"'. +% '> '. ($f->{'m2_label'} || $field ). ' '; +% +% if ( $f->{'layer_values_callback'} ) { +% my %switches = ( 'mode' => $mode ); +% $layer_values = +% &{ $f->{'layer_values_callback'} }( $cgi, $name_obj, \%switches ); +% } +% warn "layer values: ". Dumper($layer_values) +% if $opt{'debug'}; +% +% my @existing = &{ $include_sub }( +% 'label' => $ex_label, +% 'fieldnum' => $fieldnum, +% 'curr_value' => $name_obj->$col(), +% 'onchange' => $onchange, +% 'layer_values' => $layer_values, +% 'cell_style' => ( $fieldnum ? 'border-top:1px solid black' : '' ), +% ); +% $existing[0] =~ s(^/elements/tr-)(/elements/); +% my @label = @existing; +% $label[0] = '/elements/tr-td-label.html'; + + <% include( @label ) %> + <TD COLSPAN="<% $f->{'colspan'} || 1 %>"> + <% include( @existing ) %> + </TD> + +% if ( $f->{'m2_fields'} ) { +% foreach my $c ( @{ $f->{'m2_fields'} } ) { +% my $column = $c->{field}; +% my @column = &{ $column_sub }( %$c, +% 'fieldnum' => $fieldnum, +% 'curr_value' => $name_obj->$column() +% ); + + <TD id='<% $field %>__<% $column %>_label<% $fieldnum %>' + style='text-align:right;vertical-align:top; + border-top:1px solid black;padding-top:5px;'> + <% $c->{'label'} || '' %> + </TD> + <TD style='border-top:1px solid black;padding-top:3px;'> + <% include( @column ) %> + </TD> +% } +% } + + </TR> + +% $fieldnum++; +% $g_row++; +% } +% #$field .= $fieldnum; +% $onchange .= "\nspawn_$field(what);"; +% } else { +% if ( $f->{curr_value_callback} ) { +% $curr_value = &{ $f->{curr_value_callback} }( $cgi, $object, $field ), +% } else { +% $curr_value = $object->$field(); +% } +% $curr_value = &{ $opt{'value_callback'} }( $f->{'field'}, $curr_value ) +% if $opt{'value_callback'} && $mode ne 'error'; +% } +% +% my @include = &{ $include_sub }( +% 'label' => $label, +% 'fieldnum' => $fieldnum, +% 'curr_value' => $curr_value, +% 'object' => $object, +% 'cgi' => $cgi, +% 'onchange' => $onchange, +% ( $fieldnum ? ('cell_style' => 'border-top:1px solid black') : () ), +% ); +% +% if ( $f->{'m2name_table'} || $f->{'o2m_table'} || $f->{'m2m_method'} ) { +% $include[0] =~ s(^/elements/tr-)(/elements/); +% my @label = @include; +% $label[0] = '/elements/tr-td-label.html'; + + <% include( @label ) %> + <TD COLSPAN="<% $f->{'colspan'} || 1 %>"> + <% include( @include ) %> + </TD> + +% if ( $f->{'m2_fields'} ) { +% foreach my $c ( @{ $f->{'m2_fields'} } ) { +% my $column = $c->{field}; +% my @column = &{ $column_sub }( %$c, 'fieldnum' => $fieldnum ); + + <TD id='<% $field %>__<% $column %>_label<% $fieldnum %>' + style='text-align:right;vertical-align:top; + border-top:1px solid black;padding-top:5px;'> + <% $c->{'label'} || '' %> + </TD> + <TD style='border-top:1px solid black;padding-top:3px;'> + <% include( @column ) %> + </TD> +% } +% } + + </TR> + +% } else { + + <% include( @include ) %> + +% } +% if ( $f->{'m2name_table'} || $f->{'o2m_table'} || $f->{'m2m_method'} ) { + + <SCRIPT TYPE="text/javascript"> + + var <%$field%>_rownum = <% $g_row %>; + var <%$field%>_fieldnum = <% $fieldnum %>; + + function spawn_<%$field%>(what) { + + // only spawn if we're the last element... return if not + + var field_regex = /(\d+)(_[a-z]+)?$/; + var match = field_regex.exec(what.name); + if ( !match ) { + alert(what.name + " didn't match for " + what); + return; + } + if ( match[1] != <%$field%>_fieldnum ) { + return; + } + + // change the label on the last entry & add a remove button + var prev_label = document.getElementById('<% $field %>_label' + <%$field%>_fieldnum ); + prev_label.innerHTML = '<INPUT TYPE="button" VALUE="X" TITLE="Remove this <% lc($f->{'m2_label'}) %>" onClick="remove_<% $field %>(' + <%$field%>_fieldnum + ');" STYLE="color:#ff0000;font-weight:bold;padding-left:2px;padding-right:2px" > <% $f->{'m2_label'} || $field %>'; + + <%$field%>_fieldnum++; + + //get the new widget + +% $include[0] =~ s(^/elements/tr-)(/elements/); +% my @layer_opt = ( @include, +% 'field' => $field."MAGIC_NUMBER", +% 'id' => $field."MAGIC_NUMBER", +% 'layer_prefix' => $field."MAGIC_NUMBER.", +% ); +% warn @layer_opt if $opt{'debug'}; + + var newrow = <% include(@layer_opt, html_only=>1) |js_string %>; + +% #until the rest have html/js_only +% if ( $type eq 'selectlayers' || $type =~ /^select-cgp_rule_/ ) { + var newfunc = <% include(@layer_opt, js_only=>1) |js_string %>; +% } else { + var newfunc = ''; +% } + + // substitute in the new field name + var magic_regex = /MAGIC_NUMBER/g; + newrow = newrow.replace( magic_regex, <%$field%>_fieldnum ); + newfunc = newfunc.replace( magic_regex, <%$field%>_fieldnum ); + + // evaluate new_func + if (window.ActiveXObject) { + window.execScript(newfunc); + } else { /* (window.XMLHttpRequest) */ + //window.eval(newfunc); + setTimeout(newfunc, 0); + } + + // add new row + + //hmm, can't use selectlayers after a tablebreak-title for now + var table = document.getElementById('TableNumber<% $tablenum-1 %>'); + + var row = table.insertRow(<%$field%>_rownum++); + + var label_cell = document.createElement('TD'); + + label_cell.id = '<% $field %>_label' + <%$field%>_fieldnum; + + label_cell.style.textAlign = "right"; + label_cell.style.verticalAlign = "top"; + label_cell.style.borderTop = "1px solid black"; + label_cell.style.paddingTop = "5px"; + + label_cell.innerHTML = '<% $label %>'; + + row.appendChild(label_cell); + + var widget_cell = document.createElement('TD'); + + widget_cell.style.borderTop = "1px solid black"; + widget_cell.style.paddingTop = "3px"; + widget_cell.colSpan = "<% $f->{'colspan'} || 1 %>" + + widget_cell.innerHTML = newrow; + + row.appendChild(widget_cell); + +% if ( $f->{'m2_fields'} ) { +% foreach my $c ( @{ $f->{'m2_fields'} } ) { +% my $column = $c->{field}; +% my @column = &{ $column_sub }(%$c, 'fieldnum' => 'MAGIC_NUMBER'); + + var column = <% include(@column, html_only=>1) |js_string %>; + column = column.replace( magic_regex, <%$field%>_fieldnum ); + + var column_label = document.createElement('TD'); + column_label.id = + '<% $field %>__<% $column %>_label' + <%$field%>_fieldnum; + + column_label.style.textAlign = "right"; + column_label.style.verticalAlign = "top"; + column_label.style.borderTop = "1px solid black"; + column_label.style.paddingTop = "5px"; + + column_label.innerHTML = '<% $c->{'label'} || '' %>'; + + row.appendChild(column_label); + + var column_widget = document.createElement('TD'); + + column_widget.style.borderTop = "1px solid black"; + column_widget.style.paddingTop = "3px"; + + column_widget.innerHTML = column; + + row.appendChild(column_widget); + +% } +% } + +% if ( $f->{'m2_new_js'} ) { + // take out items selected in previous dropdowns + var new_element = document.getElementById("<%$field%>" + <%$field%>_fieldnum ); + <% $f->{'m2_new_js'} %>(new_element); + + if ( new_element.length < 2 ) { + //just the ** Select new **, so don't display the row + row.style.display = 'none'; + } +% } + + } + + function remove_<%$field%>(remove_fieldnum) { + //alert("remove <%$field%> " + remove_fieldnum); + var select = document.getElementById('<%$field%>' + remove_fieldnum); + + if ( ! select ) { + alert("can't find element <%$field%>" + remove_fieldnum); + return; + } + +% my $warnings = $f->{'m2_remove_warnings'}; +% if ( $warnings ) { + var sel_value = select.options[select.selectedIndex].value; +% foreach my $value ( keys %$warnings ) { + if ( sel_value == '<% $value %>' ) { + if ( ! confirm( <% $warnings->{$value} |js_string %> ) ) { + return; + } + } +% } +% } + + select.disabled = 'disabled'; // this seems to prevent it from being submitted on tested browsers so far (IE, moz, konq at least) + var label_td = document.getElementById('<%$field%>_label' + remove_fieldnum ); + label_td.parentNode.style.display = 'none'; + +% if ( $f->{m2_remove_js} ) { + var opt = select.options[select.selectedIndex]; + <% $f->{m2_remove_js} %>( opt.value, opt.text, 'no_match'); +% } + + } + + </SCRIPT> + +% } + +% } + +<% ref( $opt{'html_table_bottom'} ) + ? &{ $opt{'html_table_bottom'} }( $object ) + : $opt{'html_table_bottom'} +%> + +</TABLE> + +<% ref( $opt{'html_bottom'} ) + ? &{ $opt{'html_bottom'} }( $object ) + : $opt{'html_bottom'} +%> + +<BR> + +<INPUT TYPE = "submit" + ID = "submit" + VALUE = "<% ( !$clone && $object->$pkey() ) + ? "Apply changes" + : "Add ". ( $opt{'name'} || $opt{'name_singular'} ) + %>" +> + +</FORM> + +<% ref( $opt{'html_foot'} ) + ? &{ $opt{'html_foot'} }( $object ) + : $opt{'html_foot'} +%> + +<% include("/elements/footer.html") %> +<%init> + +my(%opt) = @_; + +my $curuser = $FS::CurrentUser::CurrentUser; + +#false laziness w/process.html +my $table = $opt{'table'}; +my $class = "FS::$table"; +my $pkey = dbdef->table($table)->primary_key; #? $opt{'primary_key'} || +my $fields = $opt{'fields'} + #|| [ grep { $_ ne $pkey } dbdef->table($table)->columns ]; + || [ grep { $_ ne $pkey } fields($table) ]; +#my @actualfields = map { ref($_) ? $_->{'field'} : $_ } @$fields; + +if ( $cgi->param('redirect') ) { + my $session = $cgi->param('redirect'); + my $pref = $curuser->option("redirect$session"); + die "unknown redirect session $session\n" unless length($pref); + $cgi = new CGI($pref); +} + +&{$opt{'begin_callback'}}( $cgi, $fields, \%opt ) + if $opt{'begin_callback'}; + +my %qsearch = ( + 'table' => $table, + 'extra_sql' => ( $opt{'agent_virt'} + ? ' AND '. $curuser->agentnums_sql( + 'null_right' => $opt{'agent_null_right'} + ) + : '' + ), +); + +my $mode; +my $object; +my $clone = ''; +if ( $cgi->param('error') ) { + + $mode = 'error'; + + $object = $class->new( { + map { $_ => scalar($cgi->param($_)) } fields($table) + }); + + &{$opt{'error_callback'}}( $cgi, $object, $fields, \%opt ) + if $opt{'error_callback'}; + +} elsif ( $cgi->param('clone') =~ /^(\d+)$/ ) { + + $mode = 'clone'; + + $clone = $1; + + $qsearch{'extra_sql'} = ' AND '. $opt{'agent_clone_extra_sql'} + if $opt{'agent_clone_extra_sql'}; + + $object = qsearchs({ %qsearch, 'hashref' => { $pkey => $clone } }) + or die "$pkey $clone not found in $table"; + + &{$opt{'clone_callback'}}( $cgi, $object, $fields, \%opt ) + if $opt{'clone_callback'}; + + #$object->$pkey(''); + + $opt{action} ||= 'Add'; + +} elsif ( $cgi->keywords || $cgi->param($pkey) ) { #editing + + $mode = 'edit'; + + my $value; + if ( $cgi->param($pkey) ) { + $value = $cgi->param($pkey) + } else { + my( $query ) = $cgi->keywords; + $value = $query; + } + $value =~ /^(\d+)$/ or die "unparsable $pkey"; + $object = qsearchs({ %qsearch, 'hashref' => { $pkey => $1 } }) + or die "$pkey $1 not found in $table"; + + warn "$table $pkey => $1" + if $opt{'debug'}; + + &{$opt{'edit_callback'}}( $cgi, $object, $fields, \%opt ) + if $opt{'edit_callback'}; + +} else { #adding + + $mode = 'new'; + + my $hashref = $opt{'new_hashref_callback'} + ? &{$opt{'new_hashref_callback'}} + : {}; + + $object = $opt{'new_object_callback'} + ? &{$opt{'new_object_callback'}}( $cgi, $hashref, $fields, \%opt ) + : $class->new( $hashref ); + + &{$opt{'new_callback'}}( $cgi, $object, $fields, \%opt ) + if $opt{'new_callback'}; + +} + +&{$opt{'end_callback'}}( $cgi, $object, $fields, \%opt ) + if $opt{'end_callback'}; + +$opt{action} ||= $object->$pkey() ? 'Edit' : 'Add'; + +my $title = $opt{action}. ' '. ( $opt{name} || $opt{'name_singular'} ); + +my $viewall_url = $p . ( $opt{'viewall_dir'} || 'search' ) . "/$table.html"; +$viewall_url = $opt{'viewall_url'} if $opt{'viewall_url'}; + +my @menubar = (); +if ( $opt{'menubar'} ) { + @menubar = @{ $opt{'menubar'} }; +} else { + my $items = $opt{'name'} ? $opt{'name'}.'s' : PL($opt{'name_singular'}); + @menubar = ( + "View all $items" => $viewall_url, + ); +} + +</%init> diff --git a/httemplate/edit/elements/rate_detail.html b/httemplate/edit/elements/rate_detail.html new file mode 100644 index 000000000..faf11f844 --- /dev/null +++ b/httemplate/edit/elements/rate_detail.html @@ -0,0 +1,239 @@ +<%doc> +<% include('/edit/elements/rate_detail.html', + # required + 'ratenum' => '1', + + # optional + 'regionnum' => '25', + # or + 'countrycode' => '237', +) %> + +If regionnum is specified, this produces column headers plus +one row of rate details for that region (in all time periods). +Otherwise, there's one row for each region in the specified +countrycode (or each region anywhere, if there is no countrycode), +with row headers showing the region name and prefixes. + +</%doc> +<% include('/elements/table-grid.html') %> +<TR> +% my $col = 0; +% foreach (@header) { +% my $hlink = $hlinks[$col]; + <TH CLASS = "grid", + BGCOLOR = "#cccccc"> + <% $hlink ? qq!<A HREF="$hlink">$_</A>! : $_ %> + </TH> +% $col++; +% } #foreach @header +</TR><TR> +% my $row = 0; +% foreach my $r (@rows) { +% $col = 0; +% if ( !$opt{'regionnum'} ) { +% $region = $r; +% foreach ($r->regionname, $r->prefixes_short) { + <TD> + <A HREF="<% $p.'edit/rate_region.cgi?'.$r->regionnum %>"><% $_ %></A> + </TD> +% } +% } +% elsif ( !$opt{'ratenum'} ) { +% $rate = $r; + <TD> + <A HREF="<% $p.'edit/rate.cgi?'.$r->ratenum %>"><% $r->ratename %></A> + </TD> +% } +% foreach my $rate_time (@rate_time, '') { + <TD> +% my $detail = $details[$row][$col]; +% if($detail) { + <TABLE CLASS="inv" STYLE="border:none"> + <TR><TD><% edit_link($detail) %><% $money_char.$detail->min_charge %> + <% $detail->sec_granularity ? ' / minute':' / call' %> + <% $edit_hint %></A> + </TD></TR> + <% granularity_detail($detail) %> + <% min_included_detail($detail) %> + <% conn_charge_detail($detail) %> + <TR><TD><% $rate_time ? delete_link($detail) : '' %></TD></TR> + </TABLE> +% } +% else { #!$detail + <% add_link($rate, $region, $rate_time) %> +% } +% $col++; + </TD> +% } # foreach @rate_time +</TR> +% $row++; +% }# foreach @rate_region +</TABLE> + +<%once> + +tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); +tie my %conn_secs, 'Tie::IxHash', FS::rate_detail::conn_secs(); + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; + +sub small { + '<FONT SIZE="-1">'.shift.'</FONT>' +} +my $edit_hint = small('(edit)'); + +sub edit_link { + my $rate_detail = shift; + my $ratedetailnum = $rate_detail->ratedetailnum; + '<A HREF="javascript:void(0);" onclick="'. + include( '/elements/popup_link_onclick.html', + 'action' => "${p}edit/rate_detail.html?$ratedetailnum", + 'actionlabel' => 'Edit rate', + 'height' => 420, + #default# 'width' => 540, + #default# 'color' => '#333399', + ) . '">' +} + +sub add_link { + my ($rate, $region, $rate_time) = @_; + '<A HREF="javascript:void(0);" onclick="'. + include( '/elements/popup_link_onclick.html', + 'action' => "${p}edit/rate_detail.html?ratenum=". + $rate->ratenum. + ';dest_regionnum='. + $region->regionnum. + ($rate_time ? + ';ratetimenum='.$rate_time->ratetimenum : + ''), + 'actionlabel' => 'Add rate', + 'height' => 420, + ).'">'.small('(add)').'</A>' +} + +sub delete_link { + my $rate_detail = shift; + my $ratedetailnum = $rate_detail->ratedetailnum; + my $onclick = include( '/elements/popup_link_onclick.html', + 'action' => "${p}misc/delete-rate_detail.html?$ratedetailnum", + 'actionlabel' => 'Delete rate', + 'width' => 510, + 'height' => 315, + 'frame' => 'top', + ); + $onclick = "if(confirm('Delete this rate?')) { $onclick }"; + qq!<A HREF="javascript:void(0);" onclick="$onclick">!.small('(delete)').'</A>' +} + +sub granularity_detail { + my $rate_detail = shift; + if($rate_detail->sec_granularity != 60 && $rate_detail->sec_granularity > 0) { + '<TR><TD>'. + small('in '.$granularity{$rate_detail->sec_granularity}.' increments'). + '</TD></TR>'; + } + else { '' } +} + +sub min_included_detail { + my $rate_detail = shift; + if($rate_detail->min_included) { + '<TR><TD>'. + small( $rate_detail->min_included . + ($rate_detail->sec_granularity ? + ' minutes included' : + ' calls included') ). + '</TD></TR>' + } + else { '' } +} + +sub conn_charge_detail { + my $rate_detail = shift; + if($rate_detail->conn_charge > 0) { + #return '' unless $rate_detail->conn_charge > 0 || $rate_detail->conn_sec; + '<TR><TD>'. + small( $money_char. $rate_detail->conn_charge. + ' for '.$conn_secs{$rate_detail->conn_sec} + ). + '</TD></TR>' + } + else { '' } +} + +</%once> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %opt = @_; +my $ratenum = $opt{'ratenum'} || ''; +my $regionnum = $opt{'regionnum'} || ''; + +# either of these, if the $opt isn't passed, will be set to the +# correct object when generating each row. +my $rate = qsearchs('rate', { 'ratenum' => $ratenum } ) if $ratenum; +my $region = qsearchs('rate_region', { 'regionnum' => $regionnum }) if $regionnum; + +my @rate_time = qsearch('rate_time', {}); +my @header = ( + map( { $_->ratetimename } @rate_time ), + 'Default rate'); +my @hlinks = map( {$p.'edit/rate_time.cgi?'.$_->ratetimenum} @rate_time ), ''; +my @rtns = ( map( { $_->ratetimenum } @rate_time ), '' ); + +my @details; +my @rows; +if ( $ratenum ) { + if ( $regionnum ) { + @rows = qsearch('rate_region', + { ratenum => $ratenum, regionnum => $regionnum }); + } + else { + my $where = ''; + if ( $opt{'countrycode'} ) { + $where = "WHERE 0 < ( + SELECT COUNT(*) FROM rate_prefix + WHERE rate_prefix.regionnum = rate_region.regionnum + AND countrycode = '$opt{countrycode}' + )"; + } + @rows = qsearch({ table => 'rate_region', + hashref => { }, + extra_sql => $where, + }); + die "no region found" if !@rows; + + unshift @header, 'Region', 'Prefix(es)'; + unshift @hlinks, '', ''; + } + foreach my $region (@rows) { + push @details, [ map { qsearchs('rate_detail', + { 'ratenum' => $ratenum, + 'dest_regionnum' => $region->regionnum, + 'ratetimenum' => $_ } ) or '' + } @rtns + ]; + } +} +elsif ( $regionnum ) { + @rows = qsearch('rate', {}) or die "no rate plans found"; + unshift @header, 'Rate plan'; + unshift @hlinks, ''; + foreach my $rate (@rows) { + push @details, [ map { qsearchs('rate_detail', + { 'ratenum' => $rate->ratenum, + 'dest_regionnum' => $regionnum, + 'ratetimenum' => $_ } ) or '' + } @rtns + ]; + } +} +else { + die "no ratenum or regionnum specified"; +} + +</%init> diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html new file mode 100644 index 000000000..e74f44276 --- /dev/null +++ b/httemplate/edit/elements/svc_Common.html @@ -0,0 +1,211 @@ +<% include( 'edit.html', + + 'menubar' => [], + + 'error_callback' => sub { + my( $cgi, $svc_x, $fields, $opt ) = @_; + #$svcnum = $svc_x->svcnum; + $pkgnum = $cgi->param('pkgnum'); + $svcpart = $cgi->param('svcpart'); + + $part_svc = qsearchs( 'part_svc', { svcpart=>$svcpart }); + die "No part_svc entry!" unless $part_svc; + + label_fixup($part_svc, $opt); + + $svc_x->setfield('svcpart', $svcpart); + }, + + 'edit_callback' => sub { + my( $cgi, $svc_x, $fields, $opt ) = @_; + #$svcnum = $svc_x->svcnum; + my $cust_svc = $svc_x->cust_svc + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum = $cust_svc->pkgnum; + $svcpart = $cust_svc->svcpart; + + $part_svc = qsearchs ('part_svc', { svcpart=>$svcpart }); + die "No part_svc entry!" unless $part_svc; + + label_fixup($part_svc, $opt); + }, + + 'new_hashref_callback' => sub { + #my( $cgi, $svc_x ) = @_; + + { pkgnum => $pkgnum, + svcpart => $svcpart, + }; + + }, + + 'new_callback' => sub { + my( $cgi, $svc_x, $fields, $opt ) = @_; + + $part_svc = qsearchs( 'part_svc', { svcpart=>$svcpart }); + die "No part_svc entry!" unless $part_svc; + + label_fixup($part_svc, $opt); + + #$svcnum=''; + + if ( my $cb = $opt{'svc_new_callback'} ) { + my $cust_pkg = $pkgnum + ? qsearchs('cust_pkg', {pkgnum=>$pkgnum}) + : ''; #? + &{ $cb }( $cgi,$svc_x, $part_svc,$cust_pkg, $fields,$opt); + } + + $svc_x->set_default_and_fixed; + + }, + + 'field_callback' => sub { + my ($cgi, $object, $f) = @_; + + my $columndef = $part_svc->part_svc_column($f->{'field'}); + my $flag = $columndef->columnflag; + if ( $flag eq 'F' ) { + $f->{'type'} = length($columndef->columnvalue) + ? 'fixed' + : 'hidden'; + $f->{'value'} = $columndef->columnvalue; + } elsif ( $flag eq 'A' ) { + $f->{'type'} = 'hidden'; + } elsif ( $flag eq 'M' ) { + $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->{'disable_empty'} = $object->svcnum ? 1 : 0, + } + + if ( $f->{'type'} eq 'select-svc_pbx' + || $f->{'type'} eq 'select-svc-domain' + ) + { + $f->{'include_opt_callback'} = + sub { ( 'pkgnum' => $pkgnum, + 'svcpart' => $svcpart, + ); + }; + } + + if ( $f->{'field'} eq 'custnum' && $pkgnum ) { + my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum}); + $object->set('custnum', $cust_pkg->custnum); + } + + }, + + 'html_init' => sub { + my $cust_main; + if ( $pkgnum ) { + my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum}); + $cust_main = $cust_pkg->cust_main if $cust_pkg; + } + $cust_main + ? include( '/elements/small_custview.html', + $cust_main, + '', + 1, + popurl(2). "view/cust_main.cgi" + ). '<BR>' + : ''; + + }, + + 'html_table_bottom' => sub { + my $svc_x = shift; + my $html = ''; + foreach my $field ($svc_x->virtual_fields) { + if ($part_svc->part_svc_column($field)->columnflag ne 'F'){ + # If the flag is X, it won't even show up + # in $svc_acct->virtual_fields. + $html .= + $svc_x->pvf($field)->widget( 'HTML', + 'edit', + $svc_x->getfield($field) + ); + } + } + $html; + }, + + 'html_bottom' => sub { + qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="$pkgnum">!. + qq!<INPUT TYPE="hidden" NAME="svcpart" VALUE="$svcpart">!; + }, + + %opt #pass through/override params + ) +%> +<%once> + +sub label_fixup { + my( $part_svc, $opt ) = @_; + + $opt->{'name'} ||= $part_svc->svc; + + my $svcdb = $part_svc->svcdb; + require "FS/$svcdb.pm"; + + if ( UNIVERSAL::can("FS::$svcdb", 'table_info') ) { + #$opt->{'name'} ||= "FS::$svcdb"->table_info->{'name'}; + + my $fields = "FS::$svcdb"->table_info->{'fields'}; + $opt->{'fields'} ||= [ grep { $_ ne 'svcnum' } keys %$fields ]; + + $opt->{labels} ||= { + map { $_ => ( ref($fields->{$_}) + ? $fields->{$_}{'label'} + : $fields->{$_} + ); + } + keys %$fields + }; + } + + #false laziness w/view/svc_Common.html + #override default labels with service-definition labels if applicable + my $labels = $opt->{labels}; # with -> here + foreach my $field ( keys %{ $opt->{labels} } ) { + my $col = $part_svc->part_svc_column($field); + $labels->{$field} = $col->columnlabel if $col->columnlabel !~ /^\s*$/; + } + +} + +</%once> +<%init> + +my %opt = @_; + +#my( $svcnum, $pkgnum, $svcpart, $part_svc ); +my( $pkgnum, $svcpart, $part_svc ); + +#get & untaint pkgnum & svcpart +if ( ! $cgi->param('error') + && $cgi->param('pkgnum') && $cgi->param('svcpart') + ) +{ + $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; + $pkgnum = $1; + $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; + $svcpart = $1; + #$cgi->delete_all(); #so edit.html treats this correctly as new?? +} + +</%init> diff --git a/httemplate/edit/inventory_class.html b/httemplate/edit/inventory_class.html new file mode 100644 index 000000000..3ab47fe28 --- /dev/null +++ b/httemplate/edit/inventory_class.html @@ -0,0 +1,16 @@ +<% include( 'elements/edit.html', + 'name' => 'Inventory Class', + 'table' => 'inventory_class', + 'labels' => { + 'classnum' => 'Class number', + 'classname' => 'Class name', + }, + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/invoice_logo.html b/httemplate/edit/invoice_logo.html new file mode 100644 index 000000000..e1c61496f --- /dev/null +++ b/httemplate/edit/invoice_logo.html @@ -0,0 +1,136 @@ +<% include("/elements/header.html", "Edit $type2desc{$type} invoice logo", + menubar( + 'View all invoice templates' => $p.'browse/invoice_template.html' + ) + ) +%> + +% if ( $error ) { + <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error %></FONT> + <BR><BR> +% } + +% if ( $cgi->param('msg') ) { + <FONT SIZE="+1"><B><% $cgi->param('msg') |h %></B></FONT> + <BR><BR> +% } + +% if ( $mode eq 'upload' ) { + <FORM ACTION="invoice_logo.html" METHOD="POST" ENCTYPE="multipart/form-data"> + <INPUT TYPE="hidden" NAME="mode" VALUE="preview"> +% } elsif ( $mode eq 'preview' ) { + <FORM ACTION="process/invoice_logo.html" METHOD="POST"> + <INPUT TYPE="hidden" NAME="preview_session" VALUE="<% $session %>"> +% } + +<INPUT TYPE="hidden" NAME="type" VALUE="<% $type %>"> +<INPUT TYPE="hidden" NAME="name" VALUE="<% $name %>"> + +<% include('/elements/table-grid.html') %> + +<TR> + <TH CLASS="grid" BGCOLOR="#cccccc">Current logo</TH> + <TH CLASS="grid" BGCOLOR="#cccccc">New logo preview</TH> +</TR> + +<TR> + + <TD CLASS="grid" BGCOLOR="#ffffff"> + +% if ( $type eq 'png' ) { + + <IMG SRC="<% $p %>view/logo.cgi?type=png;name=<% $name %>"> + +% } elsif ( $type eq 'eps' ) { + + <i>EPS preview not yet supported</i> + +% } + + </TD> + + <TD CLASS="grid" BGCOLOR="#ffffff"> + +% if ( $mode eq 'upload' ) { + + Upload new logo (.<%uc($type)%> format): <INPUT TYPE="file" NAME="new_logo"> + <BR><INPUT TYPE="submit" NAME="submit" VALUE="Upload"> + +% } elsif ( $mode eq 'preview' ) { + + <IMG SRC="<% $p %>view/logo.cgi?type=png;preview_session=<% $session %>"> + +% } + + </TD> + + +</TR> + +</TABLE> + +% if ( $mode eq 'preview' ) { + <BR> + <INPUT TYPE="submit" NAME="submit" VALUE="Change logo"> +% } + +</FORM> + +<% include("/elements/footer.html") %> + +<%once> + +my %type2desc = ( + 'png' => 'online', + 'eps' => 'Print/PDF (typeset)', +); + +</%once> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; + +my $type = $cgi->param('type'); + +$cgi->param('name') =~ /^([^\.\/]*)$/ or die "illegal name"; +my $name = $1; + +$cgi->param('mode') =~ /^(\w*)$/ or die "illegal mode"; +my $mode = $1 || 'upload'; + +my $error = ''; +my $session = ''; +if ( $mode eq 'preview' ) { + + my $fh = $cgi->upload('new_logo'); + + if ( defined $fh ) { + + local $/; + my $logo_data = <$fh>; + + $session = int(rand(4294967296)); #XXX + my $pref = new FS::access_user_pref({ + 'usernum' => $FS::CurrentUser::CurrentUser->usernum, + 'prefname' => "logo_preview$session", + 'prefvalue' => encode_base64($logo_data), + 'expiration' => time + 3600, #1h? 1m? + }); + my $pref_error = $pref->insert; + if ( $pref_error ) { + die "FATAL: couldn't set preview cookie: $pref_error\n"; + } + + } else { + + $mode = 'upload'; + $error = 'No file uploaded'; + + } + +} + +</%init> diff --git a/httemplate/edit/invoice_template.html b/httemplate/edit/invoice_template.html new file mode 100644 index 000000000..9cec62c86 --- /dev/null +++ b/httemplate/edit/invoice_template.html @@ -0,0 +1,69 @@ +<% include("/elements/header.html", "Edit $type2desc{$type} invoice template", + menubar( + 'View all invoice templates' => $p.'browse/invoice_template.html' + ) + ) +%> + +<FORM ACTION="process/invoice_template.html" METHOD="POST"> +<INPUT TYPE="hidden" NAME="confname" VALUE="<% $confname %>"> + +% if ( $type eq 'html' ) { + + <% include('/elements/htmlarea.html', + 'field' => 'value', + 'curr_value' => $value, + 'height' => 800, + ) + %> + +% } else { + + <TEXTAREA NAME="value" ROWS=30 COLS=80 WRAP="off"><%$value |h %></TEXTAREA> + +% } + +<BR><BR> +<INPUT TYPE="submit" VALUE="Change template"> + +</FORM> + +<% include("/elements/footer.html") %> + +<%once> + +my %type2desc = ( + 'html' => 'HTML', + 'latex' => 'Print/PDF (typeset)', + 'text' => 'Plaintext', +); + +my %type2base = ( + 'html' => 'invoice_html', + 'latex' => 'invoice_latex', + 'text' => 'invoice_template', +); + +</%once> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $type = $cgi->param('type'); +my $name = $cgi->param('name'); +my $suffix = $cgi->param('suffix'); + +#XXX type handling, just testing this out for now + +my $conf = new FS::Conf; + +my $value = length($name) + ? join("\n", $conf->config_orbase($type2base{$type}.$suffix, $name) ) + : join("\n", $conf->config($type2base{$type}.$suffix) ); + +my $confname = length($name) + ? $type2base{$type}.$suffix. '_'. $name + : $type2base{$type}.$suffix; + +</%init> diff --git a/httemplate/edit/mailinglistmember.html b/httemplate/edit/mailinglistmember.html new file mode 100644 index 000000000..2391cb697 --- /dev/null +++ b/httemplate/edit/mailinglistmember.html @@ -0,0 +1,25 @@ +<% include( 'elements/edit.html', + 'name_singular' => 'member', + 'table' => 'mailinglistmember', + 'popup' => 1, + 'fields' => [ + { field=>'listnum', type=>'hidden', }, + { field=>'svcnum', type=>'hidden', }, #not yet + { field=>'contactemailnum', type=>'hidden', }, #not yet + { field=>'email', type=>'text', }, + ], + 'labels' => { 'membernum' => 'Member', + 'email' => 'Email address', + }, + 'new_callback' => $new_callback, + ) +%> +<%init> + +my $new_callback = sub { + #my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; + my( $cgi, $object ) = @_; + $object->listnum( $cgi->param('listnum') ); +}; + +</%init> diff --git a/httemplate/edit/msg_template.html b/httemplate/edit/msg_template.html new file mode 100644 index 000000000..67eae185d --- /dev/null +++ b/httemplate/edit/msg_template.html @@ -0,0 +1,158 @@ +<% include( 'elements/edit.html', + 'html_init' => '<TABLE id="outerTable"><TR><TD>', + 'name_singular' => 'template', + 'table' => 'msg_template', + 'viewall_dir' => 'browse', + 'agent_virt' => 1, + 'agent_null' => 1, + 'agent_null_right' => 'Edit global templates', + + 'fields' => [ 'msgname', + 'subject', + 'from_addr', + { field=>'body', type=>'htmlarea', width=>763 }, + ], + 'labels' => { 'msgnum' => 'Template', + 'msgname' => 'Template name', + 'from_addr' => 'Return address', + 'subject' => 'Message subject', + 'body' => 'Message template', + }, + 'html_foot' => "</TD>$sidebar</TR></TABLE>", + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit templates') + || $FS::CurrentUser::CurrentUser->access_right('Edit global templates') + || $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +# Create hints pane + +my %substitutions = ( + 'cust_main' => [ + '$display_custnum'=> 'Customer#', + '$agentnum' => 'Agent#', + '$agent_name' => 'Agent name', + '$payby' => 'Payment method', + '$paymask' => 'Card/account# (masked)', + '$payname' => 'Name on card/bank name', + '$paytype' => 'Account type', + '$payip' => 'IP address used to submit payment info', + '$num_ncancelled_pkgs' => '# of active packages', + '$num_cancelled_pkgs' => '# of cancelled packages', + '$num_pkgs' => '# of packages', + '$classname' => 'Customer class', + '$categoryname' => 'Customer category', + '$balance' => 'Current balance', + '$invoicing_list_emailonly' => 'Billing email address', + '$cust_status' => 'Status', + '$ucfirst_cust_status' => 'Status, capitalized', + '$cust_statuscolor' => 'Status color code', + '$company_name' => 'Our company name', + ], + 'contact' => [ # duplicate this for shipping + '$name' => 'Company and contact name', + '$name_short' => 'Company or contact name', + '$company' => 'Company name', + '$contact' => 'Contact name (last, first)', + '$contact_firstlast'=> 'Contact name (first last)', + '$first' => 'First name', + '$last' => 'Last name', + '$address1' => 'Address line 1', + '$address2' => 'Address line 2', + '$city' => 'City', + '$county' => 'County', + '$state' => 'State', + '$zip' => 'Zip', + '$country' => 'Country', + '$daytime' => 'Day phone', + '$night' => 'Night phone', + '$fax' => 'Fax', + ], + 'cust_bill' => [ + '$invnum' => 'Invoice#', + ], + 'cust_pkg' => [ + '$pkgnum' => 'Package#', + '$pkg_label' => 'Package label (short)', + '$pkg_label_long' => 'Package label (long)', + '$status' => 'Status', + '$statuscolor' => 'Status color code', + '$start_ymd' => 'Start date', + '$setup_ymd' => 'Setup date', + '$last_bill_ymd' => 'Last bill date', + '$next_bill_ymd' => 'Next bill date', + '$susp_ymd' => 'Suspended on date', + '$cancel_ymd' => 'Canceled on date', + '$adjourn_ymd' => 'Adjournment date', + '$expire_ymd' => 'Expiration date', + '$labels_short' => 'Service labels', + '$location_label' => 'Service location', + ], + 'svc_acct' => [ + '$username' => 'Login name', + '$password' => 'Password', + ], + 'cust_pay' => [ + '$paynum' => 'Payment#', + '$paid' => 'Amount', + '$payby' => 'Payment method', + '$date' => 'Payment date', + '$payinfo' => 'Card/account# (masked)', + ], +); +my @c = @{ $substitutions{'contact'} }; +for (my $i=0; $i<scalar(@c); $i += 2) { + $c[$i] =~ s/\$(.*)/\$ship_$1/; +} +$substitutions{'shipping'} = \@c; + +tie my %sections, 'Tie::IxHash', ( +'contact' => 'Name and contact info (billing)', +'shipping' => 'Name and contact info (shipping)', +'cust_main' => 'Customer status and payment info', +'cust_pkg' => 'Package fields', +'cust_bill' => 'Invoice fields', +'cust_pay' => 'Payment fields', +'svc_acct' => 'Login service fields', +); + +my $widget = new HTML::Widgets::SelectLayers( + 'options' => \%sections, + 'form_name' => 'dummy', + 'html_between'=>'</FORM><FONT SIZE=-1>', + 'selected_layer'=>(keys(%sections))[0], + 'layer_callback' => sub { + my $section = shift; + my $html = include('/elements/table-grid.html'); + my @hints = @{ $substitutions{$section} }; + while(@hints) { + my $key = shift @hints; + $html .= qq!\n<TR><TD><A href="javascript:insertHtml('{$key}')">$key</A></TD>!; + $html .= "\n<TD>".shift(@hints).'</TD></TR>'; + } + $html .= "\n</TABLE>"; + return $html; + }, +); + +my $sidebar = ' +<SCRIPT TYPE="text/javascript"> +function insertHtml(what) { + var oEditor = FCKeditorAPI.GetInstance("body"); + oEditor.InsertHtml(what); +}; +</SCRIPT> +<TD valign="top"><FORM name="dummy"> +Substitutions: ' +. $widget->html . +'<BR>Click links to insert. +<BR>Enclose substitutions and other Perl expressions in braces: +<BR>{ $name } = ExampleCo (Smith, John) +<BR>{ time2str("%D", time) } = '.time2str("%D", time).' +</FONT></TD> +'; + +</%init> diff --git a/httemplate/edit/msgcat.cgi b/httemplate/edit/msgcat.cgi new file mode 100755 index 000000000..85b300876 --- /dev/null +++ b/httemplate/edit/msgcat.cgi @@ -0,0 +1,54 @@ +<% header("Edit Message catalog" ) %> +<BR> + +<% include('/elements/error.html') %> + +<% $widget->html %> + + </TABLE> + </BODY> +</HTML> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $widget = new HTML::Widgets::SelectLayers( + 'selected_layer' => 'en_US', + 'options' => { 'en_US'=>'en_US' }, + 'form_action' => 'process/msgcat.cgi', + 'layer_callback' => sub { + my $layer = shift; + my $html = qq!<INPUT TYPE="hidden" NAME="locale" VALUE="$layer">!. + "<BR>Messages for locale $layer<BR>". table(). + "<TR><TH COLSPAN=2>Code</TH>". + "<TH>Message</TH>"; + $html .= "<TH>en_US Message</TH>" unless $layer eq 'en_US'; + $html .= '</TR>'; + + #foreach my $msgcat ( sort { $a->msgcode cmp $b->msgcode } + # qsearch('msgcat', { 'locale' => $layer } ) ) { + foreach my $msgcat ( qsearch('msgcat', { 'locale' => $layer } ) ) { + $html .= + '<TR><TD>'. $msgcat->msgnum. '</TD><TD>'. $msgcat->msgcode. '</TD>'. + '<TD><INPUT TYPE="text" SIZE=32 '. + qq! NAME="!. $msgcat->msgnum. '" '. + qq!VALUE="!. ($cgi->param($msgcat->msgnum)||$msgcat->msg). qq!"></TD>!; + unless ( $layer eq 'en_US' ) { + my $en_msgcat = qsearchs('msgcat', { + 'locale' => 'en_US', + 'msgcode' => $msgcat->msgcode, + } ); + $html .= '<TD>'. $en_msgcat->msg. '</TD>'; + } + $html .= '</TR>'; + } + + $html .= '</TABLE><BR><INPUT TYPE="submit" VALUE="Apply changes">'; + + $html; + }, + +); + +</%init> diff --git a/httemplate/edit/part_bill_event.cgi b/httemplate/edit/part_bill_event.cgi new file mode 100755 index 000000000..c0ff38689 --- /dev/null +++ b/httemplate/edit/part_bill_event.cgi @@ -0,0 +1,570 @@ +<% include('/elements/header.html', + "$action Invoice Event Definition", + menubar( + 'View all invoice events' => popurl(2). 'browse/part_bill_event.cgi', + ) + ) +%> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% popurl(1) %>process/part_bill_event.cgi" NAME="editEvent" METHOD=POST> +<INPUT TYPE="hidden" NAME="eventpart" VALUE="<% $part_bill_event->eventpart %>"> +Invoice Event #<% $hashref->{eventpart} ? $hashref->{eventpart} : "(NEW)" %> + +<% ntable("#cccccc",2) %> + + <TR> + <TD ALIGN="right">Event name </TD> + <TD><INPUT TYPE="text" NAME="event" VALUE="<% $hashref->{event} %>"></TD> + </TR> + + <TR> + <TD ALIGN="right">For </TD> + <TD> + <SELECT NAME="payby" <% $hashref->{eventpart} ? '' : 'MULTIPLE SIZE=7'%>> +% tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2longname; +% foreach my $payby ( keys %payby ) { + <OPTION VALUE="<% $payby %>"<% ($part_bill_event->payby eq $payby) ? ' SELECTED' : '' %>><% $payby{$payby} %></OPTION> +% } + </SELECT> customers + </TD> + </TR> +% my $days = $hashref->{seconds}/86400; + + + <TR> + <TD ALIGN="right">After</TD> + <TD><INPUT TYPE="text" NAME="days" VALUE="<% $days %>"> days</TD> + </TR> + + <TR> + <TD ALIGN="right">Test event</TD> + <TD> + <SELECT NAME="freq"> +% tie my %freq, 'Tie::IxHash', '1d' => 'daily', '1m' => 'monthly'; +% foreach my $freq ( keys %freq ) { +% + + + <OPTION VALUE="<% $freq %>"<% ($part_bill_event->freq eq $freq) ? ' SELECTED' : '' %>><% $freq{$freq} %></OPTION> +% } + + + </SELECT> + </TD> + </TR> + + + <TR> + <TD ALIGN="right">Disabled</TD> + <TD> + <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>> + </TD> + </TR> + + <TR> + <TD VALIGN="top" ALIGN="right">Action</TD> + <TD> +% +% +%#print ntable(); +% +%sub select_pkgpart { +% my $label = shift; +% my $plandata = shift; +% my %selected = map { $_=>1 } split(/,\s*/, $plandata->{$label}); +% qq(<SELECT NAME="$label" MULTIPLE>). +% join("\n", map { +% '<OPTION VALUE="'. $_->pkgpart. '"'. +% ( $selected{$_->pkgpart} ? ' SELECTED' : '' ). +% '>'. $_->pkg_comment +% } qsearch('part_pkg', { 'disabled' => '' } ) ). +% '</SELECT>'; +%} +% +%sub select_agentnum { +% my $plandata = shift; +% #my $agentnum = $plandata->{'agentnum'}; +% my %agentnums = map { $_=>1 } split(/,\s*/, $plandata->{'agentnum'}); +% '<SELECT NAME="agentnum" MULTIPLE>'. +% join("\n", map { +% '<OPTION VALUE="'. $_->agentnum. '"'. +% ( $agentnums{$_->agentnum} ? ' SELECTED' : '' ). +% '>'. $_->agent +% } qsearch('agent', { 'disabled' => '' } ) ). +% '</SELECT>'; +%} +% +%sub honor_dundate { +% my $label = shift; +% my $plandata = shift; +% '<TABLE>'. +% '<TR><TD ALIGN="right">Allow delay until dun date? </TD>'. +% qq(<TD><INPUT TYPE="checkbox" NAME="$label" VALUE="$label => 1," ). +% ( $plandata->{$label} eq "$label => 1," ? 'CHECKED' : '' ). +% '>'. +% '</TD></TR>'. +% '</TABLE>' +%} +% +%my $conf = new FS::Conf; +%my $money_char = $conf->config('money_char') || '$'; +% +%my $late_taxclass = ''; +%my $late_percent_taxclass = ''; +%if ( $conf->exists('enable_taxclasses') ) { +% $late_taxclass = +% '<BR>Taxclass '. +% include('/elements/select-taxclass.html', +% 'curr_value' => '%%%late_taxclass%%%', +% 'name' => 'late_taxclass' ); +% $late_percent_taxclass = +% '<BR>Taxclass '. +% include('/elements/select-taxclass.html', +% 'curr_value' => '%%%late_percent_taxclass%%%', +% 'name' => 'late_percent_taxclass' ); +%} +% +%#this is pretty kludgy right here. +%tie my %events, 'Tie::IxHash', +% +% 'fee' => { +% 'name' => 'Late fee (flat)', +% 'code' => '$cust_main->charge( %%%charge%%%, \'%%%reason%%%\', \'$%%%charge%%%\', \'%%%late_taxclass%%%\' );', +% 'html' => +% 'Amount <INPUT TYPE="text" SIZE="7" NAME="charge" VALUE="%%%charge%%%">'. +% '<BR>Reason <INPUT TYPE="text" NAME="reason" VALUE="%%%reason%%%">'. +% $late_taxclass, +% 'weight' => 10, +% }, +% 'fee_percent' => { +% 'name' => 'Late fee (percentage)', +% 'code' => '$cust_main->charge( sprintf(\'%.2f\', $cust_bill->owed * %%%percent%%% / 100 ), \'%%%percent_reason%%%\', \'%%%percent%%% percent\', \'%%%late_percent_taxclass%%%\' );', +% 'html' => +% 'Percent <INPUT TYPE="text" SIZE="2" NAME="percent" VALUE="%%%percent%%%">%'. +% '<BR>Reason <INPUT TYPE="text" NAME="percent_reason" VALUE="%%%percent_reason%%%">'. +% $late_percent_taxclass, +% 'weight' => 10, +% }, +% 'suspend' => { +% 'name' => 'Suspend', +% 'code' => '$cust_main->suspend(reason => %%%sreason%%%, %%%honor_dundate%%% );', +% 'html' => sub { &honor_dundate('honor_dundate', @_) }, +% 'weight' => 10, +% 'reason' => 'S', +% }, +% 'suspend-if-balance' => { +% 'name' => 'Suspend if balance (this invoice and previous) over', +% 'code' => '$cust_bill->cust_suspend_if_balance_over( %%%balanceover%%%, reason => %%%sreason%%%, %%%balance_honor_dundate%%% );', +% 'html' => sub { " $money_char ". '<INPUT TYPE="text" SIZE="7" NAME="balanceover" VALUE="%%%balanceover%%%"> '. &honor_dundate('balance_honor_dundate', @_) }, +% 'weight' => 10, +% 'reason' => 'S', +% }, +% 'suspend-if-pkgpart' => { +% 'name' => 'Suspend packages', +% 'code' => '$cust_main->suspend_if_pkgpart({pkgparts => [%%%if_pkgpart%%%,], reason => %%%sreason%%%, %%%if_pkgpart_honor_dundate%%% });', +% 'html' => sub { &select_pkgpart('if_pkgpart', @_). &honor_dundate('if_pkgpart_honor_dundate', @_) }, +% 'weight' => 10, +% 'reason' => 'S', +% }, +% 'suspend-unless-pkgpart' => { +% 'name' => 'Suspend packages except', +% 'code' => '$cust_main->suspend_unless_pkgpart({unless_pkgpart => [%%%unless_pkgpart%%%], reason => %%%sreason%%%, %%%unless_pkgpart_honor_dundate%%% });', +% 'html' => sub { &select_pkgpart('unless_pkgpart', @_). &honor_dundate('unless_pkgpart_honor_dundate' => @_) }, +% 'weight' => 10, +% 'reason' => 'S', +% }, +% 'cancel' => { +% 'name' => 'Cancel', +% 'code' => '$cust_main->cancel(reason => %%%creason%%%);', +% 'weight' => 80, #10, +% 'reason' => 'C', +% }, +% +% 'addpost' => { +% 'name' => 'Add postal invoicing', +% 'code' => '$cust_main->invoicing_list_addpost(); "";', +% 'weight' => 20, +% }, +% +% 'comp' => { +% 'name' => 'Pay invoice with a complimentary "payment"', +% 'code' => '$cust_bill->comp();', +% 'weight' => 90, #30, +% }, +% +% 'credit' => { +% 'name' => "Create and apply a credit for the customer's balance (i.e. write off as bad debt)", +% 'code' => '$cust_main->credit( $cust_main->balance, \'%%%credit_reason%%%\' );', +% 'html' => '<INPUT TYPE="text" NAME="credit_reason" VALUE="%%%credit_reason%%%">', +% 'weight' => 30, +% }, +% +% 'realtime-card' => { +% 'name' => 'Run card with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway', +% 'code' => '$cust_bill->realtime_card();', +% 'weight' => 30, +% }, +% +% 'realtime-check' => { +% 'name' => 'Run check with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway', +% 'code' => '$cust_bill->realtime_ach();', +% 'weight' => 30, +% }, +% +% 'realtime-lec' => { +% 'name' => 'Run phone bill ("LEC") billing with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway', +% 'code' => '$cust_bill->realtime_lec();', +% 'weight' => 30, +% }, +% +% 'batch-card' => { +% 'name' => 'Add card or check to a pending batch', +% 'code' => '$cust_bill->batch_card(%options);', +% 'weight' => 40, +% }, +% +% +% #'retriable' => { +% # 'name' => 'Mark batched card event as retriable', +% # 'code' => '$cust_pay_batch->retriable();', +% # 'weight' => 60, +% #}, +% +% 'send' => { +% 'name' => 'Send invoice (email/print/fax)', +% 'code' => '$cust_bill->send();', +% 'weight' => 50, +% }, +% +% 'send_email' => { +% 'name' => 'Send invoice (email only)', +% 'code' => '$cust_bill->email();', +% 'weight' => 50, +% }, +% +% 'send_alternate' => { +% 'name' => 'Send invoice (email/print/fax) with alternate template', +% 'code' => '$cust_bill->send(\'%%%templatename%%%\');', +% 'html' => +% '<INPUT TYPE="text" NAME="templatename" VALUE="%%%templatename%%%">', +% 'weight' => 50, +% }, +% +% 'send_if_newest' => { +% 'name' => 'Send invoice (email/print/fax) with alternate template, if it is still the newest invoice (useful for late notices - set to 31 days or later)', +% 'code' => '$cust_bill->send_if_newest(\'%%%if_newest_templatename%%%\');', +% 'html' => +% '<INPUT TYPE="text" NAME="if_newest_templatename" VALUE="%%%if_newest_templatename%%%">', +% 'weight' => 50, +% }, +% +% 'send_agent' => { +% 'name' => 'Send invoice (email/print/fax) ', +% 'code' => '$cust_bill->send( \'%%%agent_templatename%%%\', +% [ %%%agentnum%%% ], +% \'%%%agent_invoice_from%%%\', +% %%%agent_balanceover%%% +% );', +% 'html' => sub { +% '<TABLE BORDER=0> +% <TR> +% <TD ALIGN="right">only for agent(s) </TD> +% <TD>'. &select_agentnum(@_). '</TD> +% </TR> +% <TR> +% <TD ALIGN="right">with template </TD> +% <TD> +% <INPUT TYPE="text" NAME="agent_templatename" VALUE="%%%agent_templatename%%%"> +% </TD> +% </TR> +% <TR> +% <TD ALIGN="right">email From: </TD> +% <TD> +% <INPUT TYPE="text" NAME="agent_invoice_from" VALUE="%%%agent_invoice_from%%%"> +% </TD> +% </TR> +% <TR> +% <TD ALIGN="right">if balance (this invoice and previous) over +% </TD> +% <TD> +% '. $money_char. '<INPUT TYPE="text" SIZE="7" NAME="agent_balanceover" VALUE="%%%agent_balanceover%%%"> +% </TD> +% </TR> +% </TABLE>'; +% }, +% 'weight' => 50, +% }, +% +% 'send_csv_ftp' => { +% 'name' => 'Upload CSV invoice data to an FTP server', +% 'code' => '$cust_bill->send_csv( protocol => \'ftp\', +% server => \'%%%ftpserver%%%\', +% username => \'%%%ftpusername%%%\', +% password => \'%%%ftppassword%%%\', +% dir => \'%%%ftpdir%%%\', +% \'format\' => \'%%%ftpformat%%%\', +% );', +% 'html' => +% '<TABLE BORDER=0>'. +% '<TR><TD ALIGN="right">Format ("default" or "billco"): </TD>'. +% '<TD>'. +% '<!--'. +% '<SELECT NAME="ftpformat">'. +% '<OPTION VALUE="default">Default'. +% '<OPTION VALUE="billco">Billco'. +% '</SELECT>'. +% '-->'. +% '<INPUT TYPE="text" NAME="ftpformat" VALUE="%%%ftpformat%%%">'. +% '</TD></TR>'. +% '<TR><TD ALIGN="right">FTP server: </TD>'. +% '<TD><INPUT TYPE="text" NAME="ftpserver" VALUE="%%%ftpserver%%%">'. +% '</TD></TR>'. +% '<TR><TD ALIGN="right">FTP username: </TD><TD>'. +% '<INPUT TYPE="text" NAME="ftpusername" VALUE="%%%ftpusername%%%">'. +% '</TD></TR>'. +% '<TR><TD ALIGN="right">FTP password: </TD><TD>'. +% '<INPUT TYPE="text" NAME="ftppassword" VALUE="%%%ftppassword%%%">'. +% '</TD></TR>'. +% '<TR><TD ALIGN="right">FTP directory: </TD>'. +% '<TD><INPUT TYPE="text" NAME="ftpdir" VALUE="%%%ftpdir%%%">'. +% '</TD></TR>'. +% '</TABLE>', +% 'weight' => 50, +% }, +% +% 'spool_csv' => { +% 'name' => 'Spool CSV invoice data', +% 'code' => '$cust_bill->spool_csv( +% \'format\' => \'%%%spoolformat%%%\', +% \'dest\' => \'%%%spooldest%%%\', +% \'balanceover\' => \'%%%spoolbalanceover%%%\', +% \'agent_spools\' => \'%%%spoolagent_spools%%%\', +% );', +% 'html' => sub { +% my $plandata = shift; +% +% my $html = +% '<TABLE BORDER=0>'. +% '<TR><TD ALIGN="right">Format: </TD>'. +% '<TD>'. +% '<SELECT NAME="spoolformat">'; +% +% foreach my $option (qw( default billco )) { +% $html .= qq(<OPTION VALUE="$option"); +% $html .= ' SELECTED' if $option eq $plandata->{'spoolformat'}; +% $html .= ">\u$option"; +% } +% +% $html .= +% '</SELECT>'. +% '</TD></TR>'. +% '<TR><TD ALIGN="right">For destination: </TD>'. +% '<TD>'. +% '<SELECT NAME="spooldest">'; +% +% tie my %dest, 'Tie::IxHash', +% '' => '(all)', +% 'POST' => 'Postal Mail', +% 'EMAIL' => 'Email', +% 'FAX' => 'Fax', +% ; +% +% foreach my $dest (keys %dest) { +% $html .= qq(<OPTION VALUE="$dest"); +% $html .= ' SELECTED' if $dest eq $plandata->{'spooldest'}; +% $html .= '>'. $dest{$dest}; +% } +% +% $html .= +% '</SELECT>'. +% '</TD></TR>'. +% +% '<TR>'. +% '<TD ALIGN="right">if balance (this invoice and previous) over </TD>'. +% '<TD>'. +% "$money_char ". +% '<INPUT TYPE="text" SIZE="7" NAME="spoolbalanceover" VALUE="%%%spoolbalanceover%%%">'. +% '</TD>'. +% '<TR><TD ALIGN="right">Individual per-agent spools? </TD>'. +% '<TD><INPUT TYPE="checkbox" NAME="spoolagent_spools" VALUE="1" '. +% ( $plandata->{'spoolagent_spools'} ? 'CHECKED' : '' ). +% '>'. +% '</TD></TR>'. +% '</TABLE>'; +% +% $html; +% }, +% 'weight' => 50, +% }, +% +% 'bill' => { +% 'name' => 'Generate invoices (normally only used with a <i>Late Fee</i> event)', +% 'code' => '$cust_main->bill();', +% 'weight' => 60, +% }, +% +% 'apply' => { +% 'name' => 'Apply unapplied payments and credits', +% 'code' => '$cust_main->apply_payments_and_credits; "";', +% 'weight' => 70, +% }, +% +%; +% +<SCRIPT TYPE="text/javascript">var myreasons = new Array();</SCRIPT> +%foreach my $event ( keys %events ) { +% my %plandata = map { /^(\w+) (.*)$/; ($1, $2); } +% split(/\n/, $part_bill_event->plandata); +% my $html = $events{$event}{html}; +% if ( ref($html) eq 'CODE' ) { +% $html = &{$html}(\%plandata); +% } +% while ( $html =~ /%%%(\w+)%%%/ ) { +% my $field = $1; +% $html =~ s/%%%$field%%%/$plandata{$field}/; +% } +% +<SCRIPT TYPE="text/javascript">myreasons.push('<% $events{$event}{reason} %>'); +</SCRIPT> +% if ($event eq $part_bill_event->plan){ +% $currentreasonclass=$events{$event}{reason}; +% } +% print ntable( "#cccccc", 2). +% qq!<TR><TD><INPUT TYPE="radio" NAME="plan_weight_eventcode" !; +% print "CHECKED " if $event eq $part_bill_event->plan; +% print qq!onClick="showhide_table()" !; +% print qq!VALUE="!. $event. ":". $events{$event}{weight}. ":". +% encode_entities($events{$event}{code}). +% qq!">$events{$event}{name}</TD>!; +% print '<TD>'. $html. '</TD>' if $html; +% print qq!</TR>!; +% print '</TABLE>'; +% print qq!<HR WIDTH="90%">!; +%} +% +% if ($currentreasonclass eq 'C'){ +% if ($cgi->param('creason') =~ /^(-?\d+)$/){ +% $creason = $1; +% }else{ +% $creason = $part_bill_event->reason; +% } +% if ($cgi->param('newcreasonT') =~ /^(\d+)$/){ +% $newcreasonT = $1; +% } +% if ($cgi->param('newcreason') =~ /^([\w\s]+)$/){ +% $newcreason = $1; +% } +% }elsif ($currentreasonclass eq 'S'){ +% if ($cgi->param('sreason') =~ /^(-?\d+)$/){ +% $sreason = $1; +% }else{ +% $sreason = $part_bill_event->reason; +% } +% if ($cgi->param('newsreasonT') =~ /^(\d+)$/){ +% $newsreasonT = $1; +% } +% if ($cgi->param('newsreason') =~ /^([\w\s]+)$/){ +% $newsreason = $1; +% } +% } +% + +</TD></TR> +</TABLE> + +<SCRIPT TYPE="text/javascript"> + function showhide_table() + { + for(i=0;i<document.editEvent.plan_weight_eventcode.length;i++){ + if (document.editEvent.plan_weight_eventcode[i].checked == true){ + currentevent=i; + } + } + if(myreasons[currentevent] == 'C'){ + document.getElementById('Ctable').style.display = 'inline'; + document.getElementById('Stable').style.display = 'none'; + }else if(myreasons[currentevent] == 'S'){ + document.getElementById('Ctable').style.display = 'none'; + document.getElementById('Stable').style.display = 'inline'; + }else{ + document.getElementById('Ctable').style.display = 'none'; + document.getElementById('Stable').style.display = 'none'; + } + } +</SCRIPT> + +<TABLE BGCOLOR="#cccccc" BORDER=0 WIDTH="100%"> +<TR><TD> +<TABLE BORDER=0 id="Ctable" style="display:<% $currentreasonclass eq 'C' ? 'inline' : 'none' %>"> +<% include('/elements/tr-select-reason.html', + 'field' => 'creason', + 'reason_class' => 'C', + 'curr_value' => $creason, + 'init_type' => $newcreasonT, + 'init_newreason' => $newcreason + ) +%> +</TABLE> +</TR></TD> +</TABLE> + +<TABLE BGCOLOR="#cccccc" BORDER=0 WIDTH="100%"> +<TR><TD> +<TABLE BORDER=0 id="Stable" style="display:<% $currentreasonclass eq 'S' ? 'inline' : 'none' %>"> +<% include('/elements/tr-select-reason.html', + 'field' => 'sreason', + 'reason_class' => 'S', + 'curr_value' => $sreason, + 'init_type' => $newsreasonT, + 'init_newreason' => $newsreason + ) +%> +</TABLE> +</TR></TD> +</TABLE> + +% +%print qq!<INPUT TYPE="submit" VALUE="!, +% $hashref->{eventpart} ? "Apply changes" : "Add invoice event", +% qq!">!; +% + + + </FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +if ( $cgi->param('eventpart') && $cgi->param('eventpart') =~ /^(\d+)$/ ) { + $cgi->param('eventpart', $1); +} else { + $cgi->param('eventpart', ''); +} + +my ($creason, $newcreasonT, $newcreason); +my ($sreason, $newsreasonT, $newsreason); + +my ($query) = $cgi->keywords; +my $action = ''; +my $part_bill_event = ''; +my $currentreasonclass = ''; +if ( $cgi->param('error') ) { + $part_bill_event = new FS::part_bill_event ( { + map { $_, scalar($cgi->param($_)) } fields('part_bill_event') + } ); +} +if ( $query && $query =~ /^(\d+)$/ ) { + $part_bill_event ||= qsearchs('part_bill_event',{'eventpart'=>$1}); +} else { + $part_bill_event ||= new FS::part_bill_event {}; +} +$action ||= $part_bill_event->eventpart ? 'Edit' : 'Add'; +my $hashref = $part_bill_event->hashref; + +</%init> diff --git a/httemplate/edit/part_device.html b/httemplate/edit/part_device.html new file mode 100644 index 000000000..146070fd9 --- /dev/null +++ b/httemplate/edit/part_device.html @@ -0,0 +1,45 @@ +<% include( 'elements/edit.html', + 'name' => 'Phone device type', + 'table' => 'part_device', + 'labels' => { + 'devicepart' => 'Part number', + 'devicename' => 'Device name', + }, + 'viewall_dir' => 'browse', + 'html_bottom' => $html_bottom_sub, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $extra_sql = + join( ' OR ', map { "exporttype = '$_'" } + keys %{FS::part_export::export_info('part_device')} + ); +$extra_sql = $extra_sql ? " WHERE ( $extra_sql ) " : " WHERE 0 = 1 "; + +my $html_bottom_sub = sub { + my $part_device = shift; + + '<BR>'. + '<FONT SIZE="+1">Exports</FONT><BR>'. + + '<TABLE BGCOLOR="#cccccc" WIDTH=100%>'. + '<TR><TD>'. + include( '/elements/checkboxes-table.html', + 'source_obj' => $part_device, + 'link_table' => 'export_device', + 'target_table' => 'part_export', + 'extra_sql' => $extra_sql, + 'name_callback' => sub { my $o = shift; + $o->exporttype. ' to '. $o->machine; + }, + ). + '</TD></TR></TABLE>'; + +}; + +</%init> + diff --git a/httemplate/edit/part_event.html b/httemplate/edit/part_event.html new file mode 100644 index 000000000..6a532223e --- /dev/null +++ b/httemplate/edit/part_event.html @@ -0,0 +1,679 @@ +<% include( 'elements/edit.html', + 'name' => 'Billing event definition', + 'table' => 'part_event', + 'fields' => [ + 'event', + { field => 'eventtable', + type => 'select', + options => [ FS::part_event->eventtables ], + labels => $eventtable_labels, + onchange => 'eventtable_changed', + }, + { field => 'agentnum', + type => 'select-agent', + disable_empty => $disable_empty_agent, + }, + { field => 'check_freq', + type => 'select', + options => [ '1d', '1m' ], + labels => $check_freq_labels, + }, + { field => 'disabled', + type => 'checkbox', + value => 'Y', + }, + { type => 'title', + value => 'Event Conditions', + }, + { field => 'conditionname', + type => 'selectlayers', + options => [ keys %all_conditions ], + labels => \%condition_labels, + onchange => 'condition_changed(what);', + layer_fields => \%condition_fields, + layer_values_callback => $condition_layer_values, + html_between => n_a('action'), + m2name_table => 'part_event_condition', + m2name_namecol => 'conditionname', + m2_label => 'Condition', + m2_new_default => \@implicit_condition_objs, + m2_error_callback => $condition_error_callback, + m2_remove_warnings => \%condition_remove_warnings, + m2_new_js => 'condition_repop', + m2_remove_js => 'condition_add', + }, + { type => 'title', + value => 'Event Action', + }, + { field => 'action', + type => 'selectlayers', + options => [ keys %all_actions ], + labels => \%action_labels, + onchange => 'action_changed(what);', + layer_fields => \%action_fields, + layer_values_callback => $action_layer_values, + html_between => n_a('action'), + }, + + ], + 'labels' => { + 'eventpart' => 'Event', + 'event' => 'Event name', + 'eventtable' => 'Type', + 'agentnum' => 'Agent', + 'check_freq' => 'Check frequency', + 'disabled' => 'Disable event', + + 'conditionname' => 'Add new condition', + #'weight', + 'action' => 'Action', + }, + 'viewall_dir' => 'browse', + 'new_callback' => sub { #start empty for new events only + my( $cgi, $object, $fields_listref ) = @_; + unshift @{ $fields_listref->[1]{'options'} }, ''; + }, + 'error_callback' => $error_callback, + + 'agent_virt' => 1, + 'agent_null_right' => 'Edit global billing events', + ) +%> +<SCRIPT TYPE="text/javascript"> + + window.onload = function () { eventtable_changed(document.getElementById('eventtable')) }; + var notonload = 0; + + function eventtable_changed(what) { + +% if ( $JS_DEBUG ) { + alert('eventtable_changed called on ' + what ); +% } + + var eventtable = what.options[what.selectedIndex].value; +% if ( $JS_DEBUG ) { + alert ("eventtable: " + eventtable); +% } + var eventdesc = what.options[what.selectedIndex].text; + + //remove the ** Select type ** + if ( what.options[0].value == '' && notonload++ > 0 ) { + what.options[0] = null; + } + + //// + // XXX gray out conditions that can't apply (in addition to the warning)? + //// + + //// + // update condition selects + //// + + for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { + var cond_id = 'conditionname' + cnum; + var cond_select = document.getElementById(cond_id); + +% if ( $JS_DEBUG ) { + alert('updating ' + cond_id); +% } + + // save off the current value + var conditionname = cond_select.options[cond_select.selectedIndex].value; + var cond_desc = cond_select.options[cond_select.selectedIndex].text; + + var seen_condition = condition_repop(cond_select); + + var warning = document.getElementById(cond_id + '_warning'); +% if ( $JS_DEBUG ) { + alert('turning off warning; setting style.display of '+ cond_id + + '_warning (' + warning + ') to none'); +% } + warning.style.display = 'none'; + + if ( ! seen_condition && conditionname != '' ) { + // add the current (not valid) condition back + opt(cond_select, conditionname, cond_desc, true ); + if ( ! condition_is_implicit(conditionname) ) { + cond_select.parentNode.parentNode.style.display = ''; + cond_select.disabled = ''; + // turn on a warning and gray out the condition row +% if ( $JS_DEBUG ) { + alert('turning on warning; setting style.display of '+ cond_id + + '_warning (' + warning + ') to ""'); +% } + warning.innerHTML = 'Not applicable to ' + eventdesc + ' events'; + warning.style.display = ''; + } else { + if ( ! condition_in_eventtable(conditionname) ) { +% if ( $JS_DEBUG ) { + alert(conditionname + " not in " + eventtable + "; disabling"); +% } + cond_select.parentNode.parentNode.style.display = 'none'; + cond_select.disabled = 'disabled'; + } else { +% if ( $JS_DEBUG ) { + alert(conditionname + " implicit for " + eventtable + "; enabling"); +% } + cond_select.parentNode.parentNode.style.display = ''; + cond_select.disabled = ''; + } + } + } + + } + + + //// + // update action select + //// + + // save off the current value first!! + var action = what.form.action.options[what.form.action.selectedIndex].value; + var a_desc = what.form.action.options[what.form.action.selectedIndex].text; + var seen_action = false; + + // blank the current action select + for ( var i = what.form.action.length; i >= 0; i-- ) + what.form.action.options[i] = null; + + if ( action == '' ) { + opt(what.form.action, action, a_desc, true ); + } + + // repopulate it +% foreach my $eventtable ( FS::part_event->eventtables ) { +% tie my %actions, 'Tie::IxHash', FS::part_event->actions($eventtable); +% #use Data::Dumper; warn Dumper(%actions); + + if ( eventtable == '<% $eventtable %>' ) { + +% foreach my $action ( keys %actions ) { +% ( my $description = $actions{$action}->{'description'} ) =~ s/'/\\'/g; + + var sel = false; + if ( action == '<% $action %>' ) { + seen_action = true; + sel = true; + } + opt( what.form.action, '<% $action %>', '<% $description %>', sel ); +% } + + } + +% } + + // by default, turn off warnings and enable the submit button + var warning = document.getElementById('action_warning'); + warning.style.display = 'none'; + var submit_button = document.getElementById('submit'); + submit_button.disabled = ''; + + if ( ! seen_action && action != '' ) { + // add the current (not valid) action back + opt( what.form.action, action, a_desc, true ); + // turn on a warning and disable the submit button + //warning.innerHTML = a_desc + ' event not available as a ' + + warning.innerHTML = 'Not available as a ' + eventdesc + ' action'; + warning.style.display = ''; + submit_button.disabled = 'disabled'; + } + + } + + function opt(what,value,text,selected) { + var optionName = new Option(text, value, false, selected); + var length = what.length; + what.options[length] = optionName; + } + + function action_changed(what) { + // remove '** Select new **' + if ( what.options[0].value == '' ) { + what.options[0] = null; + } + // remove the warning, remove the invalid action, enable the submit button + var warning = document.getElementById('action_warning'); + if ( warning.style.display == '' ) { + warning.style.display = 'none'; + what.options[what.length-1] = null; + document.getElementById('submit').disabled = ''; + } + } + + function condition_changed(what) { + // remove '** Select new **' + if ( what.options[0].value == '' ) { + what.options[0] = null; + } + + var previousValue = what.getAttribute('previousValue'); + var previousText = what.getAttribute('previousText'); + var value = what.options[what.selectedIndex].value; + var text = what.options[what.selectedIndex].text; + +% foreach my $value ( keys %condition_remove_warnings ) { + if ( previousValue == '<% $value %>' ) { + if ( !confirm( <% $condition_remove_warnings{$value} |js_string %> ) ) { + for ( var i=0; i < what.length; i++ ) { + if ( what.options[i].value == previousValue ) { + what.selectedIndex = i; + } + } + return false; + } + } +% } + + //alert(previous + ' changed to ' + value); + + var field_regex = /(\d+)$/; + var match = field_regex.exec(what.name); + if ( !match ) { + alert(what.name + " didn't match?!"); + return; + } + + //add the previous condition *back* to all the other selects... + condition_add(previousValue, previousText, match[1]); + + what.setAttribute('previousValue', value); + what.setAttribute('previousText', text); + + // remove the new condition from all other selects + condition_remove(value, match[1]); + + } + + function condition_avail(check_cond, curnum) { + for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { + if ( cnum == curnum ) continue; + + var cond_id = 'conditionname' + cnum; + var cond_select = document.getElementById(cond_id); + + //alert("checking " + cond_id + " (" + cond_select.disabled + ")"); + + if ( cond_select.disabled ) continue; + + // the current value + var conditionname = cond_select.options[cond_select.selectedIndex].value; + + if ( check_cond == conditionname ) return false; + + } + + return true; + + } + + function condition_remove(remove_cond, curnum) { + + if ( remove_cond.length == 0 ) return; + + for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { + if ( cnum == curnum ) continue; + + var cond_id = 'conditionname' + cnum; + var cond_select = document.getElementById(cond_id); + + //for ( var i = cond_select.length; i >= 0; i-- ) { + for ( var i=0; i < cond_select.length; i++ ) { + if ( cond_select.options[i].value == remove_cond ) { + cond_select.options[i] = null; + } + } + + } + + } + + function condition_add(add_condname, add_conddesc, curnum) { + + if ( add_condname.length == 0 ) return; + + var in_eventtable = condition_in_eventtable(add_condname); + + if ( ! in_eventtable ) return; + + for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { + if ( cnum == curnum ) continue; + + var cond_id = 'conditionname' + cnum; + var cond_select = document.getElementById(cond_id); + + if ( cond_select.disabled ) continue; + + //alert("adding " + add_condname + " to " + cond_id); + + opt(cond_select, add_condname, add_conddesc, false ); + + cond_select.parentNode.parentNode.style.display = ''; + + } + + } + + function condition_in_eventtable(condname) { + + var eventtable_el = document.getElementById('eventtable'); + var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value; + + var in_eventtable = false; + +% foreach my $eventtable ( FS::part_event->eventtables ) { +% tie my %conditions, 'Tie::IxHash', +% FS::part_event_condition->conditions($eventtable); + + if ( eventtable == '<% $eventtable %>' ) { + +% foreach my $conditionname ( keys %conditions ) { + + if ( condname == '<% $conditionname %>' ) { + in_eventtable = true; + } + +% } + + } + +% } + + return in_eventtable; + + } + + function condition_is_implicit(condname) { + + if ( true <% @implicit_conditions + ? ( ' && '. join(' && ', map { "condname != '$_'" } + @implicit_conditions + ) + ) + : '' + %> ) { + return false; + } else { + return true; + } + } + + function condition_repop(cond_select) { + + var eventtable_el = document.getElementById('eventtable'); + var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value; + + // save off the current value + var conditionname = cond_select.options[cond_select.selectedIndex].value; + var cond_desc = cond_select.options[cond_select.selectedIndex].text; + var seen_condition = false; + + //skip deleted conditions + if ( cond_select.disabled && conditionname != '' && ! condition_is_implicit(conditionname) ) { + return false; + } + + var field_regex = /(\d+)$/; + var match = field_regex.exec(cond_select.name); + if ( !match ) { + alert(what.name + " didn't match?!"); + return; + } + var cnum = match[1]; + + // blank the current condition select + for ( var i = cond_select.length; i >= 0; i-- ) + cond_select.options[i] = null; + + if ( conditionname == '' ) { + opt(cond_select, conditionname, cond_desc, true ); + } + + // repopulate it +% foreach my $eventtable ( FS::part_event->eventtables ) { +% tie my %conditions, 'Tie::IxHash', +% FS::part_event_condition->conditions($eventtable); + + if ( eventtable == '<% $eventtable %>' ) { + +% foreach my $conditionname ( keys %conditions ) { +% my $description = $conditions{$conditionname}->{'description'}; +% $description =~ s/'/\\'/g; + + var sel = false; + if ( conditionname == '<% $conditionname %>' ) { + seen_condition = true; + sel = true; + } + + if ( condition_avail("<% $conditionname %>", cnum) ) { + opt(cond_select, '<% $conditionname %>', '<% $description %>', sel); + } + +% } + + } + +% } + + if ( cond_select.length > 1 || cond_select.length == 1 && cond_select.options[0].value.length > 0 ) { + + cond_select.parentNode.parentNode.style.display = ''; + cond_select.disabled = ''; + + } else { + cond_select.parentNode.parentNode.style.display = 'none'; + cond_select.disabled = 'disabled'; + } + + return seen_condition; + + } + +</SCRIPT> +<%once> + +#misc (eventtable, check_freq) + +my $eventtable_labels = FS::part_event->eventtable_labels; +$eventtable_labels->{''} = '** Select type **'; + +my $check_freq_labels = FS::part_event->check_freq_labels; + +#conditions + +tie my %all_conditions, 'Tie::IxHash', + '' => { 'description' => '*** Select new condition ***', }, + FS::part_event_condition->conditions(); + +my %condition_labels = map { $_ => $all_conditions{$_}->{'description'} } + keys %all_conditions; + +#my %condition_fields = map { $_ => $all_conditions{$_}->{option_fields} } +# keys %all_conditions; +my %condition_fields = map { my $c = $_; + tie my %opts, 'Tie::IxHash', + @{ $all_conditions{$c}->{'option_fields'} || []}; + %opts = ( map { ( "$c.$_" => $opts{$_} ); } + keys %opts + ); + ( $c => [ %opts ] ); + } + keys %all_conditions; + +my @implicit_conditions = sort { $all_conditions{$a}->{'implicit_flag'} <=> + $all_conditions{$b}->{'implicit_flag'} + } + grep { $all_conditions{$_}->{'implicit_flag'} } + keys %all_conditions; + +my @implicit_condition_objs = map { + new FS::part_event_condition { + 'conditionname' => $_, + }; + } + @implicit_conditions; + +my %condition_remove_warnings = + map { ( $_ => $all_conditions{$_}->{'remove_warning'} ); } + grep { $all_conditions{$_}->{'remove_warning'} } + keys %all_conditions; + +#actions + +tie my %all_actions, 'Tie::IxHash', + '' => { 'description' => '*** Select event action ***', }, + FS::part_event->actions(); + +my %action_labels = map { $_ => $all_actions{$_}->{'description'} } + keys %all_actions; + +#my %action_fields = map { $_ => $all_actions{$_}->{option_fields} } +# keys %all_actions; +my %action_fields = map { my $action = $_; + tie my %opts, 'Tie::IxHash', + @{ $all_actions{$action}->{option_fields} || [] }; + %opts = ( map { ( "$action.$_" => $opts{$_} ); } + keys %opts + ); + ( $action => [ %opts ] ); + } + keys %all_actions; + +#subs + +sub n_a { + my $t = shift; + + return sub { + my $field = shift; + qq( <FONT ID="${field}_warning" STYLE="display:none" COLOR="#FF0000">). + "Party Party Join us Join us". + '</FONT>'; + }; +} + +my $action_layer_values = sub { + my( $cgi, $part_event ) = @_; + my $action = $cgi->param('action') || $part_event->action; + return {} unless $action; + scalar( #force hashref + { + #map { $_ => { $part_event->options } } + # keys %action_fields + map { my $action = $_; + my %fields = @{ $action_fields{$action} }; + my %obj_opts = $part_event->options; + %obj_opts = map { ( "$action.$_" => $obj_opts{$_} ); } + keys %obj_opts; + my %opts = + map { #false laziness w/process/part_event.html + my $option = $_; + my $value = scalar($cgi->param($_)) || $obj_opts{$_}; + + if ( $option =~ /^(.*)\.reasonnum$/ && $value == -1 ) { + $value = { + 'typenum' => scalar( $cgi->param( "new${option}T" ) ), + 'reason' => scalar( $cgi->param( "new${option}" ) ), + }; + } + + ( $option => $value ); + + } + keys %fields; + ( $action => \%opts ); + } + keys %action_fields + } + ); +}; + +tie my %cgi_conditions, 'Tie::IxHash'; + +my $error_callback = sub { + my( $cgi, $object, $fields_listref ) = @_; + + my @cond_params = grep /^conditionname\d+$/, $cgi->param; + + %cgi_conditions = map { + my $param = $_; + my $conditionname = $cgi->param($param); + $conditionname => { + map { + + my $cgi_key = $_; + $cgi_key =~ /^$param\.$conditionname\.(.*)$/ or die 'wtf!'; + my $key = $1; + #my $value = $cgi->param($_); + + #my $info = $all_conditions->{$conditionname} + my %cond_opts = + @{ $all_conditions{$conditionname}->{'option_fields'} || []}; + my $info = $cond_opts{$key}; + + my $value; + #false laziness w/process/part_event.html + if ( $info->{'type'} =~ /^(select|checkbox)-?multiple$/ + or $info->{'type'} =~ /^select/ && $info->{'multiple'} ) { + $value = { map { $_ => 1 } $cgi->param($cgi_key) }; + } elsif ( $info->{'type'} eq 'freq' ) { + $value = $cgi->param($cgi_key). $cgi->param($cgi_key.'_units'); + } else { + $value = $cgi->param($cgi_key); + } + + $key => $value; + + } grep /^$param\.$conditionname\./, $cgi->param + }; + } grep $cgi->param($_), grep /^conditionname\d+$/, $cgi->param; + +}; + +my $condition_error_callback = sub { + map { + new FS::part_event_condition { 'conditionname' => $_, }; + } keys %cgi_conditions; +}; + +my $condition_layer_values = sub { + #m2_table option causes this to be + # part_event_condition instead of part_event + my ( $cgi, $part_event_condition, $switches ) = @_; + scalar( #force hashref + { + #map { $_ => { $part_event_condition->options } } + # keys %condition_fields + map { my $conditionname = $_; + my %opts = $switches->{'mode'} eq 'error' + ? %{ $cgi_conditions{$conditionname} || {} } + : $part_event_condition->options; + %opts = ( + map { ( "$conditionname.$_" => $opts{$_} ); } + keys %opts + ); + ( $conditionname => \%opts ); + } + keys %condition_fields + } + ); +}; + + +</%once> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Edit billing events') + || $curuser->access_right('Edit global billing events'); + +my $disable_empty_agent= ! $curuser->access_right('Edit global billing events'); + +%cgi_conditions = (); +my $use_cgi_conditions = 0; + +my $JS_DEBUG = 0; + +</%init> diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi new file mode 100644 index 000000000..8b697e142 --- /dev/null +++ b/httemplate/edit/part_export.cgi @@ -0,0 +1,138 @@ +<% include('/elements/header.html', "$action Export", '', ' onLoad="visualize()"') %> + +<% include('/elements/error.html') %> + +<FORM NAME="dummy"> +<INPUT TYPE="hidden" NAME="exportnum" VALUE="<% $part_export->exportnum %>"> + +<% ntable("#cccccc",2) %> +<TR> + <TD ALIGN="right">Export host</TD> + <TD> + <INPUT TYPE="text" NAME="machine" VALUE="<% $part_export->machine %>"> + </TD> +</TR> +<TR> + <TD ALIGN="right">Export</TD> + <TD><% $widget->html %> + +<% include('/elements/footer.html') %> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +#if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) { +# $cgi->param('clone', $1); +#} else { +# $cgi->param('clone', ''); +#} + +my($query) = $cgi->keywords; +my $action = ''; +my $part_export = ''; +if ( $cgi->param('error') ) { + $part_export = new FS::part_export ( { + map { $_, scalar($cgi->param($_)) } fields('part_export') + } ); +} elsif ( $query =~ /^(\d+)$/ ) { + $part_export = qsearchs('part_export', { 'exportnum' => $1 } ); +} else { + $part_export = new FS::part_export; +} +$action ||= $part_export->exportnum ? 'Edit' : 'Add'; + +#my $exports = FS::part_export::export_info($svcdb); +my $exports = FS::part_export::export_info(); + +my %layers = map { $_ => "$_ - ". $exports->{$_}{desc} } keys %$exports; +$layers{''}=''; + +my $widget = new HTML::Widgets::SelectLayers( + 'selected_layer' => $part_export->exporttype, + 'options' => \%layers, + 'form_name' => 'dummy', + 'form_action' => 'process/part_export.cgi', + 'form_text' => [qw( exportnum machine )], +# 'form_checkbox' => [qw()], + 'html_between' => "</TD></TR></TABLE>\n", + 'layer_callback' => sub { + my $layer = shift; + my $html = qq!<INPUT TYPE="hidden" NAME="exporttype" VALUE="$layer">!. + ntable("#cccccc",2); + + $html .= '<TR><TD ALIGN="right">Description</TD><TD BGCOLOR=#ffffff>'. + $exports->{$layer}{notes}. '</TD></TR>' + if $layer; + + foreach my $option ( keys %{$exports->{$layer}{options}} ) { + my $optinfo = $exports->{$layer}{options}{$option}; + die "Retreived non-ref export info option from $layer export: $optinfo" + unless ref($optinfo); + my $label = $optinfo->{label}; + my $type = defined($optinfo->{type}) ? $optinfo->{type} : 'text'; + my $value = $cgi->param($option) + || ( $part_export->exportnum && $part_export->option($option) ) + || ( (exists $optinfo->{default} && !$part_export->exportnum) + ? $optinfo->{default} + : '' + ); + $html .= qq!<TR><TD ALIGN="right">$label</TD><TD>!; + if ( $type eq 'select' ) { + my $size = defined($optinfo->{size}) ? " SIZE=" . $optinfo->{size} : ''; + my $multi = defined($optinfo->{multi}) ? ' MULTIPLE' : ''; + $html .= qq!<SELECT NAME="$option"$multi$size>!; + my @values = split '\s+', $value if $multi; + my @options; + if (defined($optinfo->{option_values})) { + my $valsub = $optinfo->{option_values}; + @options = &$valsub(); + } elsif (defined($optinfo->{options})) { + @options = @{$optinfo->{options}}; + } + foreach my $select_option ( @options ) { + #if ( ref($select_option) ) { + #} else { + my $selected = ($multi ? grep {$_ eq $select_option} @values : $select_option eq $value ) ? ' SELECTED' : ''; + my $label = $select_option; + if (defined($optinfo->{option_label})) { + my $labelsub = $optinfo->{option_label}; + $label = &$labelsub($select_option); + } + $html .= qq!<OPTION VALUE="$select_option"$selected>!. + qq!$label</OPTION>!; + #} + } + $html .= '</SELECT>'; + } elsif ( $type eq 'textarea' ) { + $html .= qq!<TEXTAREA NAME="$option" COLS=80 ROWS=8 WRAP="virtual">!. + encode_entities($value). '</TEXTAREA>'; + } elsif ( $type eq 'text' ) { + $html .= qq!<INPUT TYPE="text" NAME="$option" VALUE="!. + encode_entities($value). '" SIZE=64>'; + } elsif ( $type eq 'checkbox' ) { + $html .= qq!<INPUT TYPE="checkbox" NAME="$option" VALUE="1"!; + $html .= ' CHECKED' if $value; + $html .= '>'; + } else { + $html .= "unknown type $type"; + } + $html .= '</TD></TR>'; + } + $html .= '</TABLE>'; + + $html .= '<INPUT TYPE="hidden" NAME="options" VALUE="'. + join(',', keys %{$exports->{$layer}{options}} ). '">'; + + $html .= '<INPUT TYPE="hidden" NAME="nodomain" VALUE="'. + $exports->{$layer}{nodomain}. '">'; + + $html .= '<INPUT TYPE="submit" VALUE="'. + ( $part_export->exportnum ? "Apply changes" : "Add export" ). + '">'; + + $html; + }, +); + +</%init> diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi new file mode 100755 index 000000000..deefa9cc1 --- /dev/null +++ b/httemplate/edit/part_pkg.cgi @@ -0,0 +1,739 @@ +<% 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, + + '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', + 'setup_cost' => 'Setup cost', + 'recur_cost' => 'Recur cost', + 'pay_weight' => 'Payment weight', + 'credit_weight' => 'Credit weight', + 'agentnum' => 'Agent', + 'setup_fee' => 'Setup fee', + 'recur_fee' => 'Recurring fee', + 'bill_dst_pkgpart' => 'Include line item(s) from package', + 'svc_dst_pkgpart' => 'Include services of package', + 'report_option' => 'Report classes', + 'fcc_ds0s' => 'Voice-grade eqivalents', + }, + + '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' }, + + { 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'}, + + { type => 'tablebreak-tr-title', + value => 'Pricing', #better name? + }, + { field => 'plan', + type => 'selectlayers-select', + options => [ keys %plan_labels ], + labels => \%plan_labels, + }, + { field => 'setup_fee', + type => 'money', + }, + { field => 'freq', + type => 'part_pkg_freq', + onchange => 'freq_changed', + }, + { field => 'recur_fee', + type => 'money', + disabled => sub { $recur_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_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' => 'Pricing add-ons', + 'colspan' => 4, + }, + { 'field' => 'bill_dst_pkgpart', + 'type' => 'select-part_pkg', + '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', + '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; + +my $edit_global = 'Edit global package definitions'; +my $acl_edit = $curuser->access_right('Edit package definitions'); +my $acl_edit_global = $curuser->access_right($edit_global); + +my $acl_edit_either = $acl_edit || $acl_edit_global; + +my $begin_callback = sub { + my( $cgi, $fields, $opt ) = @_; + die "access denied" + unless $acl_edit_either + || ( $cgi->param('pkgnum') + && $curuser->access_right('Customize customer package') + ); +}; + +my $disabled_type = $acl_edit_either ? 'checkbox' : 'hidden'; + +#arg. access rights for cloning are Hard. +# on the one hand we don't really want cloning (customizing a package) to fail +# for want of finding the source package in normal usage +# on the other hand, we don't want people using the clone link to be able to +# see +my $agent_clone_extra_sql = + ' ( '. FS::part_pkg->curuser_pkgs_sql. + " OR ( part_pkg.custom = 'Y' ) ". + ' ) '; + +my $conf = new FS::Conf; +my $taxproducts = $conf->exists('enable_taxproducts'); + +my $sth = dbh->prepare("SELECT COUNT(*) FROM part_pkg_report_option". + " WHERE disabled IS NULL OR disabled = '' ") + or die dbh->errstr; +$sth->execute or die $sth->errstr; +my $report_option = $sth->fetchrow_arrayref->[0]; + +#XXX +# - tr-part_pkg_freq: month_increments_only (from price plans) +# - test cloning +# - test errors cloning +# - test custom pricing +# - move the selectlayer divs away from lame layer_callback + +#my ($query) = $cgi->keywords; +# +#my $part_pkg = ''; + +my @agent_type = (); +my %tax_override = (); + +my %taxproductnums = map { ($_->classnum => 1) } + qsearch('usage_class', { 'disabled' => '' }); +my @taxproductnums = ( qw( setup recur ), sort (keys %taxproductnums) ); + +my %options = (); +my $recur_disabled = 1; + +my $error_callback = sub { + my($cgi, $object, $fields, $opt ) = @_; + + (@agent_type) = $cgi->param('agent_type'); + + $opt->{action} = 'Custom' if $cgi->param('pkgnum'); + + $recur_disabled = $cgi->param('freq') ? 0 : 1; + + foreach ($cgi->param) { + /^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1); + } + $tax_override{''} = $cgi->param('tax_override'); + $tax_override{$_} = $cgi->param('tax_override_$_') + foreach(grep { /^tax_override_(\w+)$/ } $cgi->param); + + #some false laziness w/process + $cgi->param('plan') =~ /^(\w+)$/ or die 'unparsable plan'; + my $plan = $1; + my $options = $cgi->param($plan."__OPTIONS"); + my @options = split(',', $options); + %options = + map { my $optionname = $_; + my $param = $plan."__$optionname"; + my $value = join(', ', $cgi->param($param)); + ( $optionname => $value ); + } + @options; + + #$cgi->param($_, $options{$_}) foreach (qw( setup_fee recur_fee )); + $object->set($_ => scalar($cgi->param($_)) ) + foreach (qw( setup_fee recur_fee )); + +}; + +my $new_hashref_callback = sub { { 'plan' => 'flat' }; }; + +my $new_object_callback = sub { + my( $cgi, $hashref, $fields, $opt ) = @_; + + my $part_pkg = FS::part_pkg->new( $hashref ); + $part_pkg->set($_ => '0') + foreach (qw( setup_fee recur_fee )); + + $part_pkg; + +}; + +my $edit_callback = sub { + my( $cgi, $object, $fields, $opt ) = @_; + + $recur_disabled = $object->freq ? 0 : 1; + + (@agent_type) = + map {$_->typenum} qsearch('type_pkgs', { 'pkgpart' => $object->pkgpart } ); + + my @report_option = (); + foreach ($object->options) { + /^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1); + /^report_option_(\d+)$/ && (push @report_option, $1); + } + foreach ($object->part_pkg_taxoverride) { + $taxproductnums{$_->usage_class} = 1 + if $_->usage_class; + } + + $cgi->param('report_option', join(',', @report_option)); + foreach my $field ( @$fields ) { + next unless ( + ref($field) eq 'HASH' && + $field->{field} && + $field->{field} eq 'report_option' + ); + #$field->{curr_value} = join(',', @report_option); + $field->{value} = join(',', @report_option); + } + + %options = $object->options; + + $object->set($_ => $object->option($_)) + foreach (qw( setup_fee recur_fee )); + +}; + +my $new_callback = sub { + my( $cgi, $object, $fields ) = @_; + + my $conf = new FS::Conf; + if ( $conf->exists('agent_defaultpkg') ) { + #my @all_agent_types = map {$_->typenum} qsearch('agent_type',{}); + @agent_type = map {$_->typenum} qsearch('agent_type',{}); + } + +}; + +my $clone_callback = sub { + my( $cgi, $object, $fields, $opt ) = @_; + + if ( $cgi->param('pkgnum') ) { + + my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cgi->param('pkgnum') } ); + $object->agentnum( $cust_pkg->cust_main->agentnum ); + + $opt->{action} = 'Custom'; + + #my $part_pkg = $clone_part_pkg->clone; + #this is all clone does anyway + $object->custom('Y'); + + $object->disabled('Y'); + + } else { #not when cloning... + + (@agent_type) = + map {$_->typenum} qsearch('type_pkgs',{ 'pkgpart' => $object->pkgpart } ); + + } + + %options = $object->options; + + $object->set($_ => $options{$_}) + foreach (qw( setup_fee recur_fee )); + + $recur_disabled = $object->freq ? 0 : 1; +}; + +my $m2_error_callback_maker = sub { + my $link_type = shift; #yay closures + return sub { + my( $cgi, $object ) = @_; + map { + + if ( /^${link_type}_dst_pkgpart(\d+)$/ && + ( my $dst = $cgi->param("${link_type}_dst_pkgpart$1") ) ) + { + + my $hidden = $cgi->param("${link_type}_dst_pkgpart__hidden$1") + || ''; + new FS::part_pkg_link { + 'link_type' => $link_type, + 'src_pkgpart' => $object->pkgpart, + 'dst_pkgpart' => $dst, + 'hidden' => $hidden, + }; + } else { + (); + } + } + $cgi->param; + }; +}; + +my $javascript = <<'END'; + <SCRIPT TYPE="text/javascript"> + + function freq_changed(what) { + var freq = what.options[what.selectedIndex].value; + + if ( freq == '0' ) { + what.form.recur_fee.disabled = true; + what.form.recur_fee.style.backgroundColor = '#dddddd'; + } else { + what.form.recur_fee.disabled = false; + what.form.recur_fee.style.backgroundColor = '#ffffff'; + } + + } + + function agent_changed(what) { + + var agentnum = what.options[what.selectedIndex].value; + + if ( agentnum == 0 ) { + what.form.agent_type.disabled = false; + //what.form.agent_type.style.backgroundColor = '#ffffff'; + what.form.agent_type.style.visibility = ''; + } else { + what.form.agent_type.disabled = true; + //what.form.agent_type.style.backgroundColor = '#dddddd'; + what.form.agent_type.style.visibility = 'hidden'; + } + + } + + </SCRIPT> +END + +tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() }; + +tie my %plan_labels, 'Tie::IxHash', + map { $_ => ( $plans{$_}->{'shortname'} || $plans{$_}->{'name'} ) } + keys %plans; + +my $html_bottom = sub { + my( $object ) = @_; + + #warn join("\n", map { "$_: $options{$_}" } keys %options ). "\n"; + + my $layer_callback = sub { + + my $layer = shift; + my $html = ntable("#cccccc",2); + + #$html .= ' + # <TR> + # <TD ALIGN="right">Recurring fee frequency </TD> + # <TD><SELECT NAME="freq"> + #'; + # + #my @freq = keys %freq; + #@freq = grep { /^\d+$/ } @freq + #XXX this bit# # if exists($plans{$layer}->{'freq'}) && $plans{$layer}->{'freq'} eq 'm'; + #foreach my $freq ( @freq ) { + # $html .= qq(<OPTION VALUE="$freq"); + # $html .= ' SELECTED' if $freq eq $part_pkg->freq; + # $html .= ">$freq{$freq}"; + #} + + #$html .= '</SELECT></TD></TR>'; + + my $href = $plans{$layer}->{'fields'}; + my @fields = exists($plans{$layer}->{'fieldorder'}) + ? @{$plans{$layer}->{'fieldorder'}} + : keys %{ $href }; + + foreach my $field ( grep $_ !~ /^(setup|recur)_fee$/, @fields ) { + + if(!exists($href->{$field})) { + # shouldn't happen + warn "nonexistent part_pkg option: '$field'\n"; + next; + } + + $html .= '<TR><TD ALIGN="right">'. $href->{$field}{'name'}. '</TD><TD>'; + + my $format = sub { shift }; + $format = $href->{$field}{'format'} if exists($href->{$field}{'format'}); + + #XXX these should use elements/ fields... (or this whole thing should + #just use layer_fields instead of layer_callback) + + if ( ! exists($href->{$field}{'type'}) ) { + + $html .= qq!<INPUT TYPE="text" NAME="${layer}__$field" VALUE="!. + ( exists($options{$field}) + ? &$format($options{$field}) + : $href->{$field}{'default'} ). + qq!">!; + + } elsif ( $href->{$field}{'type'} eq 'checkbox' ) { + + $html .= qq!<INPUT TYPE="checkbox" NAME="${layer}__$field" VALUE=1 !. + ( exists($options{$field}) && $options{$field} + ? ' CHECKED' + : '' + ). '>'; + + } elsif ( $href->{$field}{'type'} =~ /^select/ ) { + + $html .= '<SELECT'; + $html .= ' MULTIPLE' + if $href->{$field}{'type'} eq 'select_multiple'; + $html .= qq! NAME="${layer}__$field">!; + + $html .= '<OPTION VALUE="">'. $href->{$field}{'empty_label'} + if exists($href->{$field}{'disable_empty'}) + && ! $href->{$field}{'disable_empty'}; + + if ( $href->{$field}{'select_table'} ) { + foreach my $record ( + qsearch( $href->{$field}{'select_table'}, + $href->{$field}{'select_hash'} ) + ) { + my $value = $record->getfield($href->{$field}{'select_key'}); + $html .= qq!<OPTION VALUE="$value"!. + ( $options{$field} =~ /(^|, *)$value *(,|$)/ #? + ? ' SELECTED' + : '' + ). + '>'. $record->getfield($href->{$field}{'select_label'}); + } + } elsif ( $href->{$field}{'select_options'} ) { + foreach my $key ( keys %{ $href->{$field}{'select_options'} } ) { + my $label = $href->{$field}{'select_options'}{$key}; + $html .= qq!<OPTION VALUE="$key"!. + ( $options{$field} =~ /(^|, *)$key *(,|$)/ #? + ? ' SELECTED' + : '' + ). + '>'. $label; + } + + } else { + $html .= '<font color="#ff0000">warning: '. + "don't know how to retreive options for $field select field". + '</font>'; + } + $html .= '</SELECT>'; + + } elsif ( $href->{$field}{'type'} eq 'radio' ) { + + my $radio = + qq!<INPUT TYPE="radio" NAME="${layer}__$field"!; + + foreach my $key ( keys %{ $href->{$field}{'options'} } ) { + my $label = $href->{$field}{'options'}{$key}; + $html .= qq!$radio VALUE="$key"!. + ( $options{$field} =~ /(^|, *)$key *(,|$)/ #? + ? ' CHECKED' + : '' + ). + "> $label<BR>"; + } + + } + + $html .= '</TD></TR>'; + } + $html .= '</TABLE>'; + + $html .= qq(<INPUT TYPE="hidden" NAME="${layer}__OPTIONS" VALUE="). + join(',', keys %{ $href } ). '">'; + + $html; + + }; + + my %selectlayers = ( + field => 'plan', + options => [ keys %plan_labels ], + labels => \%plan_labels, + curr_value => $object->plan, + layer_callback => $layer_callback, + ); + + my $return = + include('/elements/selectlayers.html', %selectlayers, 'layers_only'=>1 ). + '<SCRIPT TYPE="text/javascript">'. + include('/elements/selectlayers.html', %selectlayers, 'js_only'=>1 ); + + $return .= + "taxproduct_selectchanged(document.getElementById('taxproduct_select'));\n" + if $taxproducts; + + $return .= '</SCRIPT>'; + + $return; + +}; + +my %usage_class = map { ($_->classnum => $_->classname) } + qsearch('usage_class', {}); +$usage_class{setup} = 'Setup'; +$usage_class{recur} = 'Recurring'; + +my %taxproduct_fields = (); +my $end_callback = sub { + my( $cgi, $object, $fields, $opt ) = @_; + + @taxproductnums = ( qw( setup recur ), sort (keys %taxproductnums) ); + + if ( $object->pkgpart ) { + foreach my $usage_class ( '', @taxproductnums ) { + $tax_override{$usage_class} = + join (",", map $_->taxclassnum, + qsearch( 'part_pkg_taxoverride', { + 'pkgpart' => $object->pkgpart, + 'usage_class' => $usage_class, + }) + ); + } + } + + %taxproduct_fields = + map { $_ => [ "taxproductnum_$_", + { type => 'select-taxproduct', + #label => "$usage_class{$_} tax product", + }, + "tax_override_$_", + { type => 'select-taxoverride' } + ] + } + @taxproductnums; + + $taxproduct_fields{'(default)'} = + [ 'taxproductnum', { type => 'select-taxproduct', + #label => 'Default tax product', + }, + 'tax_override', { type => 'select-taxoverride' }, + ]; +}; + +my $taxproduct_values = sub { + my ($cgi, $object, $flags) = @_; + my $routine = + sub { my $layer = shift; + my @fields = @{$taxproduct_fields{$layer}}; + my @values = (); + while( @fields ) { + my $field = shift @fields; + shift @fields; + $field =~ /^taxproductnum_\w+$/ && + push @values, ( $field => $options{"usage_$field"} ); + $field =~ /^tax_override_(\w+)$/ && + push @values, ( $field => $tax_override{$1} ); + $field =~ /^taxproductnum$/ && + push @values, ( $field => $object->taxproductnum ); + $field =~ /^tax_override$/ && + push @values, ( $field => $tax_override{''} ); + } + { (@values) }; + }; + + my @result = + map { ( $_ => { &{$routine}($_) } ) } ( '(default)', @taxproductnums ); + return({ @result }); + +}; + +my $field_callback = sub { + my ($cgi, $object, $fieldref) = @_; + + my $field = $fieldref->{field}; + if ($field eq 'taxproductnums') { + $fieldref->{value} = join(',', @taxproductnums); + } elsif ($field eq 'taxproduct_select') { + $fieldref->{options} = [ '(default)', @taxproductnums ]; + $fieldref->{labels} = { ( '(default)' => '(default)' ), + map {( $_ => ($usage_class{$_} || $_) )} + @taxproductnums + }; + $fieldref->{layer_fields} = \%taxproduct_fields; + $fieldref->{layer_values_callback} = $taxproduct_values; + } +}; + +</%init> diff --git a/httemplate/edit/part_pkg_report_option.html b/httemplate/edit/part_pkg_report_option.html new file mode 100644 index 000000000..a6f8e57b7 --- /dev/null +++ b/httemplate/edit/part_pkg_report_option.html @@ -0,0 +1,23 @@ +<% include( 'elements/edit.html', + 'name' => 'Package optional report class', + 'table' => 'part_pkg_report_option', + 'fields' => [ + 'name', + { field=>'num', type=>'hidden' }, + { field=>'disabled', type=>'checkbox', value=>'Y', }, + ], + 'labels' => { + 'num' => 'Class number', + 'name' => 'Class name', + 'disabled' => 'Disable class', + }, + 'viewall_dir' => 'browse', + ) + +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/part_pkg_taxclass.html b/httemplate/edit/part_pkg_taxclass.html new file mode 100644 index 000000000..ad030449f --- /dev/null +++ b/httemplate/edit/part_pkg_taxclass.html @@ -0,0 +1,23 @@ +<% include('elements/edit.html', + 'name_singular' => 'tax class', + 'table' => 'part_pkg_taxclass', + 'labels' => { + 'taxclassnum' => 'Tax class', + 'taxclass' => 'Tax class', + 'disabled' => 'Disabled', + }, + 'fields' => [ 'taxclass', + { 'field' => 'disabled', + 'type' => 'checkbox', + 'value' => 'Y', + }, + ], + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/part_pkg_taxoverride.html b/httemplate/edit/part_pkg_taxoverride.html new file mode 100644 index 000000000..61dfa2ac5 --- /dev/null +++ b/httemplate/edit/part_pkg_taxoverride.html @@ -0,0 +1,132 @@ +<% include('/elements/header-popup.html', 'Override taxes', '', 'onload="resizeFrames()"') %> + +<TABLE WIDTH="100%" HEIGHT="100%"> + <TR><TD> + <iframe name="selected" src="<% $p %>browse/tax_class.html?_type=select;magic=select;maxrecords=15;offset=<% $selected_offset %>;selected=<% $selected %>;" width="100%" frameborder="0" border="0" id="selectorSelected" scrolling="no"> +</iframe> + <BR> + </TD></TR> + + <TR><TD> +<FORM="dummy"> + <CENTER> + <INPUT type="submit" value="Finish" onclick="s=fetchSelected(); s.shift(); parent.document.getElementById('<% $element_name || "tax_override" %>').value=s.toString(); parent.<% $onclick %>();"> + <INPUT type="reset" value="Cancel" onclick="parent.<% $onclick %>();"> + </CENTER> +</FORM> + </TD></TR> + + <TR><TD> + <iframe name="unselected" src="<% $p %>browse/tax_class.html?_type=select;magic=omit;maxrecords=15;offset=<% $unselected_offset %>;omit=<% $selected %>;" width="100%" frameborder="0" border="0" id="selectorUnselected" scrolling="no"> +</iframe> + <BR> + </TD></TR> + +</TABLE> +<SCRIPT> + + function resizeFrames() { + //frames['selected'].style.height = + // frames['selected'].contentWindow.document.body.scrollHeight + "px"; + //frames['unselected'].style.height = + // frames['unselected'].contentWindow.document.body.scrollHeight + "px"; + var f = document.getElementById('selectorSelected'); + f.style.height = f.contentWindow.document.body.scrollHeight + "px"; + var f = document.getElementById('selectorUnselected'); + f.style.height = f.contentWindow.document.body.scrollHeight + "px"; + } + + function fetchOffset(search) { + var value = 0; + if (search.length > 1) { + var params = search.split(';'); + for (i=0; i<params.length; i++) { + if (params[i].substr(0,7) == 'offset=') { + value = params[i].substr(7); + } + } + } + return value; + } + + function fetchOffsetStrings() { + return 'selected_offset=' + + fetchOffset(frames['selected'].location.search) + ';' + + 'unselected_offset=' + + fetchOffset(frames['unselected'].location.search) + ';'; + } + + function fetchSelected() { + var i; + var selected = new Array; + var replace = '?'; + if (window.location.search.length > 1) { + var search = window.location.search.substr(1).split(';'); + for (i=0; i<search.length; i++) { + if (search[i].substr(0,9) == 'selected=') { + selected = search[i].substr(9).split(',') + }else if (search[i].substr(0,16) == 'selected_offset=') { + }else if (search[i].substr(0,18) == 'unselected_offset=') { + }else if (search[i].length) { + replace += search[i] + ';'; + } + } + } + selected.unshift(replace); + return selected; + } + function doUnselect(classnum) { + var selected = fetchSelected(); + var search = selected.shift(); + //alert("discovered: "+selected.toString()); + var i=-1, j=-1, k=selected.length; + while(++j < k) { + if (!(selected[j]==classnum)) { + selected[++i]=selected[j]; + } + } + selected.length = ++i; + //alert("finished: "+selected.toString()); + + search += "selected=" + selected.toString() + ';'; + window.location.search = search + fetchOffsetStrings(); + } + function doSelect(classnum) { + var selected = fetchSelected(); + var search = selected.shift(); + selected.push(classnum); + search += "selected=" + selected.toString() + ';'; + window.location.search = search + fetchOffsetStrings(); + } +</SCRIPT> + +<% include('/elements/footer.html') %> +<%once> + +my $conf = new FS::Conf; + +</%once> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + + +my $selected_offset = $1 + if $cgi->param('selected_offset') =~/^(\d+)$/; + +my $unselected_offset = $1 + if $cgi->param('unselected_offset') =~/^(\d+)$/; + +my $selected = $1 + if $cgi->param('selected') =~/^([,\d]+)$/; + +my $element_name = $1 + if $cgi->param('element_name') =~/^(\w+)$/; + +my $onclick = $1 + if $cgi->param('onclick') =~/^(\w+)$/; + +$onclick = 'cClick' unless $onclick; + +</%init> diff --git a/httemplate/edit/part_referral.html b/httemplate/edit/part_referral.html new file mode 100755 index 000000000..daf8773f0 --- /dev/null +++ b/httemplate/edit/part_referral.html @@ -0,0 +1,19 @@ +<% include( 'elements/edit.html', + 'name' => 'Advertising source', + 'table' => 'part_referral', + 'fields' => [ 'referral', + { field=>'agentnum', type=>'select-agent', }, + ], + 'labels' => { 'referral' => 'Advertising source', + 'agentnum' => 'Agent', + }, + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit advertising sources') + || $FS::CurrentUser::CurrentUser->access_right('Edit global advertising sources'); + +</%init> diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi new file mode 100755 index 000000000..6fe015ab5 --- /dev/null +++ b/httemplate/edit/part_svc.cgi @@ -0,0 +1,418 @@ +<% include('/elements/header.html', "$action Service Definition", + menubar('View all service definitions' => "${p}browse/part_svc.cgi"), + #" onLoad=\"visualize()\"" + ) +%> + +<FORM NAME="dummy"> + + Service Part #<% $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %> +<BR><BR> +Service <INPUT TYPE="text" NAME="svc" VALUE="<% $hashref->{svc} %>"><BR> +Disable new orders <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>><BR> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $hashref->{svcpart} %>"> +<BR> +Service definitions are the templates for items you offer to your customers. +<UL><LI>svc_acct - Accounts - anything with a username (Mailboxes, PPP accounts, shell accounts, RADIUS entries for broadband, etc.) + <LI>svc_domain - Domains + <LI>svc_forward - Mail forwarding + <LI>svc_mailinglist - Mailing list + <LI>svc_www - Virtual domain website + <LI>svc_broadband - Broadband/High-speed Internet service (always-on) + <LI>svc_phone - Customer phone numbers + <LI>svc_pbx - Customer PBXs + <LI>svc_external - Externally-tracked service +<!-- <LI>svc_charge - One-time charges (Partially unimplemented) + <LI>svc_wo - Work orders (Partially unimplemented) +--> +</UL> +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. +<BR><BR> + +% #YUCK. false laziness w/part_svc.pm. go away virtual fields, please +% my %vfields; +% foreach my $svcdb ( FS::part_svc->svc_tables() ) { +% eval "use FS::$svcdb;"; +% my $self = "FS::$svcdb"->new; +% $vfields{$svcdb} = {}; +% foreach my $field ($self->virtual_fields) { # svc_Common::virtual_fields with a null svcpart returns all of them +% my $pvf = $self->pvf($field); +% $vfields{$svcdb}->{$field} = $pvf; +% #warn "\$vfields{$svcdb}->{$field} = $pvf"; +% } #next $field +% } #next $svcdb +% +% #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, +% }, +% 'X' => { 'desc' => 'Excluded', +% 'condition' => +% sub { ! $vfields{$_[1]}->{$_[2]} }, +% +% }, +% ; +% +% my @dbs = $hashref->{svcdb} +% ? ( $hashref->{svcdb} ) +% : FS::part_svc->svc_tables(); +% +% 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_text' => [ qw( svc svcpart ) ], +% 'form_checkbox' => [ 'disabled' ], +% 'layer_callback' => sub { +% my $layer = shift; +% +% my $html = qq!<INPUT TYPE="hidden" NAME="svcdb" VALUE="$layer">!; +% +% my $columns = 3; +% my $count = 0; +% my @part_export = +% map { qsearch( 'part_export', {exporttype => $_ } ) } +% keys %{FS::part_export::export_info($layer)}; +% $html .= '<BR><BR>'. table(). +% "<TR><TH COLSPAN=$columns>Exports</TH></TR><TR>"; +% foreach my $part_export ( @part_export ) { +% $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->exportnum. ': '. $part_export->exporttype. +% ' to '. $part_export->machine. '</TD>'; +% $count++; +% $html .= '</TR><TR>' unless $count % $columns; +% } +% $html .= '</TR></TABLE><BR><BR>'; +% +% $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 = defined( dbdef->table($layer) ) +% ? grep { +% $_ ne 'svcnum' && +% ( !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'; #kludge +% $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)$/; +% +% #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" ) { //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"'; +% +% if ( !$def->{type} || $def->{type} eq 'text' ) { +% +% my $nodisplay = ' STYLE="display:none"'; +% 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", +% '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 +% } else { # select_list +% foreach my $item ( @{$def->{select_list}} ) { +% $html .= qq!<OPTION VALUE="$item"!. +% (grep(/^$item$/, split(',',$value)) ? ' SELECTED>' : '>' ). +% $item. '</OPTION>'; +% } #next $item +% } #endif +% $html .= '</SELECT>'; +% +% } elsif ( $def->{type} eq 'textarea' ) { +% +% $html .= +% qq!<TEXTAREA NAME="${layer}__${field}">!. encode_entities($value). +% '</TEXTAREA>'; +% +% } elsif ( $def->{type} eq 'select-svc_pbx.html' ) { +% +% $html .= include('/elements/select-svc_pbx.html', +% 'curr_value' => $value, +% 'element_name' => "${layer}__${field}", +% 'element_etc' => $disabled, +% 'multiple' => ($flag eq 'S'), +% ); +% +% } elsif ( $def->{type} eq 'radius_usergroup_selector' ) { +% +% #XXX disable the RADIUS usergroup selector? ugh it sure does need +% #an overhaul, people have dum group problems because of it +% +% $html .= FS::svc_acct::radius_usergroup_selector( +% [ split(',', $value) ], "${layer}__${field}" ); +% +% } 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 '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 disabled 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; +% +% }, +% ); +% +% + +Table <% $widget->html %> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $part_svc; +my $clone = ''; +if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone + #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query"; + $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } ) + or die "unknown svcpart: $1"; + $clone = $part_svc->svcpart; + $part_svc->svcpart(''); +} elsif ( $cgi->keywords ) { #edit + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die "malformed query: $query"; + $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } ) + or die "unknown svcpart: $1"; +} else { #adding + $part_svc = new FS::part_svc {}; +} + +my $action = $part_svc->svcpart ? 'Edit' : 'Add'; +my $hashref = $part_svc->hashref; +# my $p_svcdb = $part_svc->svcdb || 'svc_acct'; + + + +</%init> + + + diff --git a/httemplate/edit/part_tag.html b/httemplate/edit/part_tag.html new file mode 100644 index 000000000..87c77fa98 --- /dev/null +++ b/httemplate/edit/part_tag.html @@ -0,0 +1,29 @@ +<% include( 'elements/edit.html', + 'table' => 'part_tag', + 'name_singular' => 'tag', + 'fields' => [ + { field=>'tagname', type=>'text', size=>10 }, + { field=>'disabled', type=>'checkbox', value=>'Y' }, + { field=>'tagdesc', type=>'text', size=>60 }, + $tagcolor, + ], + 'labels' => { 'tagnum' => 'Tag #', + 'tagname' => 'Tag', + 'tagdesc' => 'Message', + 'tagcolor' => 'Highlight Color', + 'disabled' => 'Disabled', + }, + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $tagcolor = { + field => 'tagcolor', + type => 'pickcolor', +}; + +</%init> diff --git a/httemplate/edit/part_virtual_field.cgi b/httemplate/edit/part_virtual_field.cgi new file mode 100644 index 000000000..04ba9b0c0 --- /dev/null +++ b/httemplate/edit/part_virtual_field.cgi @@ -0,0 +1,104 @@ +<% include('/elements/header.html', "$action Virtual Field Definition") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/generic.cgi" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="table" VALUE="part_virtual_field"> +<INPUT TYPE="hidden" NAME="redirect_ok" + VALUE="<%popurl(2)%>browse/part_virtual_field.cgi"> +<INPUT TYPE="hidden" NAME="vfieldpart" VALUE="<% + $vfieldpart%>"> +Field #<B><%$vfieldpart or "(NEW)"%></B><BR><BR> + +<%ntable("#cccccc",2)%> + <TR> + <TD ALIGN="right">Name</TD> + <TD><INPUT TYPE="text" NAME="name" MAXLENGTH=32 VALUE="<% + $part_virtual_field->name%>"></TD> + </TR> + <TR> + <TD ALIGN="right">Table</TD> + <TD> +% if ($action eq 'Add') { + + <SELECT SIZE=1 NAME="dbtable"> +% +% my $dbdef = dbdef; # ick +% #foreach my $dbtable (sort { $a cmp $b } $dbdef->tables) { +% foreach my $dbtable (qw( svc_broadband router )) { +% if ($dbtable !~ /^h_/ +% and $dbdef->table($dbtable)->primary_key) { + + <OPTION VALUE="<%$dbtable%>"><%$dbtable%></OPTION> +% +% } +% } +% +</SELECT> +% +% } else { # Edit +% +<%$part_virtual_field->dbtable%> + <INPUT TYPE="hidden" NAME="dbtable" VALUE="<%$part_virtual_field->dbtable%>"> +% } + + </TD> + <TR> + <TD ALIGN="right">Label</TD> + <TD><INPUT TYPE="text" NAME="label" MAXLENGTH="80" VALUE="<% + $part_virtual_field->label%>"></TD> + </TR> + <TR> + <TD ALIGN="right">Length</TD> + <TD><INPUT TYPE="text" NAME="length" MAXLENGTH=4 VALUE="<% + $part_virtual_field->length%>"></TD> + </TR> + <TR> + <TD ALIGN="right">Check</TD> + <TD><TEXTAREA COLS="20" ROWS="4" NAME="check_block"><% + $part_virtual_field->check_block%></TEXTAREA></TD> + </TR> + <TR> + <TD ALIGN="right">List source</TD> + <TD><TEXTAREA COLS="20" ROWS="4" NAME="list_source"><% + $part_virtual_field->list_source%></TEXTAREA></TD> + </TR> +</TABLE><BR><INPUT TYPE="submit" VALUE="Submit"> + +</FORM> + +<BR> +<FONT SIZE=-2>If you don't understand what <I>check_block</I> and +<I>list_source</I> mean, <B>LEAVE THEM BLANK</B>. We mean it.</FONT> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my ($vfieldpart, $part_virtual_field); + +if ( $cgi->param('error') ) { + $part_virtual_field = new FS::part_virtual_field ( { + map { $_, scalar($cgi->param($_)) } fields('part_virtual_field')}); + $vfieldpart = $part_virtual_field->vfieldpart; +} else { + my($query) = $cgi->keywords; + if ( $query =~ /^(\d+)$/ ) { #editing + $vfieldpart=$1; + $part_virtual_field=qsearchs('part_virtual_field', + {'vfieldpart' => $vfieldpart}) + or die "Unknown vfieldpart!"; + + } else { #adding + $part_virtual_field = new FS::part_virtual_field({}); + } +} +my $action = $part_virtual_field->vfieldpart ? 'Edit' : 'Add'; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html new file mode 100644 index 000000000..a9909c365 --- /dev/null +++ b/httemplate/edit/payment_gateway.html @@ -0,0 +1,139 @@ +<% include( 'elements/edit.html', + 'table' => 'payment_gateway', + 'name_singular' => 'Payment gateway', + 'viewall_dir' => 'browse', + 'fields' => $fields, + 'field_callback' => $field_callback, + 'labels' => { + 'gatewaynum' => 'Gateway #', + 'gateway_module' => 'Gateway', + 'gateway_username' => 'Username', + 'gateway_password' => 'Password', + 'gateway_action' => 'Action', + 'gateway_options' => 'Options: (Name/Value pairs, one element per line)', + 'gateway_callback_url' => 'Callback URL', + }, + ) +%> + + +<SCRIPT TYPE="text/javascript"> + var gatewayNamespace = new Array; + +% foreach my $module ( sort { lc($a) cmp lc ($b) } keys %modules ) { + gatewayNamespace.push('<% $modules{$module} %>') +% } + + // document.getElementById('gateway_namespace').value = gatewayNamespace[0]; + function setNamespace(what) { + document.getElementById('gateway_namespace').value = + gatewayNamespace[what.selectedIndex]; + } + +</SCRIPT> + +<%init> + +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', + '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', +); + +my @actions = ( + 'Normal Authorization', + 'Authorization Only', + 'Authorization Only, Post Authorization', + ); + +my $fields = [ + { + field => 'gateway_namespace', + type => 'hidden', + curr_value_callback => sub { my($cgi, $object, $fref) = @_; + $modules{$object->gateway_module} + || 'Business::OnlinePayment' + }, + }, + { + field => 'gateway_module', + type => 'select', + options => [ sort { lc($a) cmp lc ($b) } keys %modules ], + onchange => 'setNamespace', + }, + 'gateway_username', + 'gateway_password', + { + field => 'gateway_action', + type => 'select', + options => \@actions, + }, + 'gateway_callback_url', + { + field => 'gateway_options', + type => 'textarea', + curr_value_callback => sub { my($cgi, $object, $fref) = @_; + join("\r", $object->options ); + }, + }, + ]; + +my $field_callback = sub { + my ($cgi, $object, $field_hashref ) = @_; + if ($object->gatewaynum) { + if ( $field_hashref->{field} eq 'gateway_module' ) { + $field_hashref->{type} = 'fixed'; + } + } +}; + +</%init> diff --git a/httemplate/edit/phone_device.html b/httemplate/edit/phone_device.html new file mode 100644 index 000000000..a1aa16620 --- /dev/null +++ b/httemplate/edit/phone_device.html @@ -0,0 +1,37 @@ +<% include( 'elements/edit.html', + 'name' => 'Phone device', + 'table' => 'phone_device', + 'labels' => { + 'devicenum' => 'Device', + 'devicepart' => 'Device type', + 'mac_addr' => 'MAC address', + }, + 'fields' => [ { 'field' => 'devicepart', + 'type' => 'select-table', + 'table' => 'part_device', + 'name_col' => 'devicename', + 'empty_label' =>'Select device type', + #'hashref' =>{ disabled => '' }, + }, + 'mac_addr', + { 'field' => 'svcnum', + 'type' => 'hidden', + }, + ], + 'menubar' => [], #disable viewall + #'viewall_dir' => 'browse', + 'new_callback' => sub { + my( $cgi, $object ) = @_; + $object->svcnum( $cgi->param('svcnum') ); + }, + ) +%> +<%init> + +# :/ 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/pkg_category.html b/httemplate/edit/pkg_category.html new file mode 100644 index 000000000..3be74c7f5 --- /dev/null +++ b/httemplate/edit/pkg_category.html @@ -0,0 +1,28 @@ +<% include( 'elements/edit.html', + 'name_singular' => 'Package Category', + 'table' => 'pkg_category', + 'fields' => [ + 'categoryname', + 'weight', + { field=>'condense', type=>'checkbox', value=>'Y', }, + { field=>'disabled', type=>'checkbox', value=>'Y', }, + ], + 'labels' => { + 'categorynum' => 'Category number', + 'categoryname' => 'Category name', + 'weight' => 'Weight', + 'condense' => 'Collapse identical items to one', + 'disabled' => 'Disable category', + }, + 'viewall_dir' => 'browse', + %opt, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %opt = @_; + +</%init> diff --git a/httemplate/edit/pkg_class.html b/httemplate/edit/pkg_class.html new file mode 100644 index 000000000..4f7a729bd --- /dev/null +++ b/httemplate/edit/pkg_class.html @@ -0,0 +1,5 @@ +<% include( 'elements/class_Common.html', + 'name' => 'Package Class', + 'table' => 'pkg_class', + ) +%> diff --git a/httemplate/edit/prepay_credit.cgi b/httemplate/edit/prepay_credit.cgi new file mode 100644 index 000000000..f7a1b0801 --- /dev/null +++ b/httemplate/edit/prepay_credit.cgi @@ -0,0 +1,113 @@ +<% include("/elements/header.html",'Generate prepaid cards'. ($agent ? ' for '. $agent->agent : '') ) %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%popurl(1)%>process/prepay_credit.cgi" METHOD="POST" NAME="OneTrueForm" onSubmit="document.OneTrueForm.submit.disabled=true"> + +Generate +<INPUT TYPE="text" NAME="num" VALUE="<% $cgi->param('num') || '(quantity)' |h %>" SIZE=10 MAXLENGTH=10 onFocus="if ( this.value == '(quantity)' ) { this.value = ''; }"> + +prepaid cards of + +<INPUT TYPE="text" NAME="length" SIZE=3 MAXLENGTH=2 VALUE=8> +<SELECT NAME="type"> +% foreach (qw(alpha alphanumeric numeric)) { + <OPTION<% $cgi->param('type') eq $_ ? ' SELECTED' : '' %>><% $_ %> +% } +</SELECT> + +characters each + +<BR>for <SELECT NAME="agentnum"><OPTION>(any agent) +% foreach my $opt_agent ( qsearch('agent', { 'disabled' => '' } ) ) { + + <OPTION VALUE="<% $opt_agent->agentnum %>"<% $opt_agent->agentnum == $agentnum ? ' SELECTED' : '' %>><% $opt_agent->agent %> +% } + +</SELECT> + +<TABLE> +<TR><TD>Value: +$<INPUT TYPE="text" NAME="amount" SIZE=8 MAXLENGTH=7 VALUE="<% $cgi->param('amount') |h %>"> +</TD> +<TD>and/or +<INPUT TYPE="text" NAME="seconds" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('seconds') |h %>"> +<SELECT NAME="multiplier"> +% foreach my $multiplier ( keys %multiplier ) { + + <OPTION VALUE="<% $multiplier %>"<% $cgi->param('multiplier') eq $multiplier ? ' SELECTED' : '' %>><% $multiplier{$multiplier} %> +% } + +</SELECT> +</TD></TR> +<TR><TD></TD> +<TD>and/or +<INPUT TYPE="text" NAME="upbytes" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('upbytes') |h %>"> +<SELECT NAME="upmultiplier"> +% foreach my $multiplier ( keys %bytemultiplier ) { + + <OPTION VALUE="<% $multiplier %>"<% $cgi->param('upmultiplier') eq $multiplier ? ' SELECTED' : '' %>><% $bytemultiplier{$multiplier} %> +% } + +</SELECT> upload +</TD></TR> +<TR><TD></TD> +<TD>and/or +<INPUT TYPE="text" NAME="downbytes" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('downbytes') |h %>"> +<SELECT NAME="downmultiplier"> +% foreach my $multiplier ( keys %bytemultiplier ) { + + <OPTION VALUE="<% $multiplier %>"<% $cgi->param('downmultiplier') eq $multiplier ? ' SELECTED' : '' %>><% $bytemultiplier{$multiplier} %> +% } + +</SELECT> download +</TD></TR> +<TR><TD></TD> +<TD>and/or +<INPUT TYPE="text" NAME="totalbytes" SIZE=6 MAXLENGTH=5 VALUE="<% $cgi->param('totalbytes') |h %>"> +<SELECT NAME="totalmultiplier"> +% foreach my $multiplier ( keys %bytemultiplier ) { + + <OPTION VALUE="<% $multiplier %>"<% $cgi->param('totalmultiplier') eq $multiplier ? ' SELECTED' : '' %>><% $bytemultiplier{$multiplier} %> +% } + +</SELECT> total transfer +</TD></TR> +</TABLE> +<BR><BR> +<INPUT TYPE="submit" NAME="submit" VALUE="Generate" onSubmit="this.disabled = true"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $agent = ''; +my $agentnum = ''; +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { + $agent = qsearchs('agent', { 'agentnum' => $agentnum=$1 } ); +} + +tie my %multiplier, 'Tie::IxHash', + 1 => 'seconds', + 60 => 'minutes', + 3600 => 'hours', +; + +tie my %bytemultiplier, 'Tie::IxHash', + 1 => 'bytes', + 1024 => 'Kbytes', + 1048576 => 'Mbytes', + 1073741824 => 'Gbytes', +; + +$cgi->param('multiplier', '60') unless $cgi->param('multiplier'); +$cgi->param('upmultiplier', '1048576') unless $cgi->param('upmultiplier'); +$cgi->param('downmultiplier', '1048576') unless $cgi->param('downmultiplier'); +$cgi->param('totalmultiplier','1048576') unless $cgi->param('totalmultiplier'); + +</%init> diff --git a/httemplate/edit/process/REAL_cust_pkg.cgi b/httemplate/edit/process/REAL_cust_pkg.cgi new file mode 100755 index 000000000..570f0e031 --- /dev/null +++ b/httemplate/edit/process/REAL_cust_pkg.cgi @@ -0,0 +1,49 @@ +%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 adjourn expire ); + +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, '_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_group.html b/httemplate/edit/process/access_group.html new file mode 100644 index 000000000..2d125c387 --- /dev/null +++ b/httemplate/edit/process/access_group.html @@ -0,0 +1,27 @@ +<% include( 'elements/process.html', + 'table' => 'access_group', + 'viewall_dir' => 'browse', + 'process_m2m' => { 'link_table' => 'access_groupagent', + 'target_table' => 'agent', + }, + 'process_m2name' => { + 'link_table' => 'access_right', + 'link_static' => { 'righttype' => 'FS::access_group', }, + 'num_col' => 'rightobjnum', + 'name_col' => 'rightname', + 'names_list' => [ FS::AccessRight->rights() ], + 'param_style' => 'link_table.value checkboxes', + }, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +if ( FS::Conf->new->exists('disable_acl_changes') ) { + errorpage('ACL changes disabled in public demo.'); + die "shouldn't be reached"; +} + +</%init> diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html new file mode 100644 index 000000000..e6258a9b1 --- /dev/null +++ b/httemplate/edit/process/access_user.html @@ -0,0 +1,26 @@ +% if ( $cgi->param('_password') ne $cgi->param('_password2') ) { +% $cgi->param('error', "The passwords do not match"); +% 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' ], + 'clear_on_error' => [ '_password', '_password2' ], + 'process_m2m' => { 'link_table' => 'access_usergroup', + 'target_table' => 'access_group', + }, + ) +%> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +if ( FS::Conf->new->exists('disable_acl_changes') ) { + errorpage('ACL changes disabled in public demo.'); + die "shouldn't be reached"; +} + +</%init> diff --git a/httemplate/edit/process/addr_block/add.cgi b/httemplate/edit/process/addr_block/add.cgi new file mode 100755 index 000000000..39d6348ce --- /dev/null +++ b/httemplate/edit/process/addr_block/add.cgi @@ -0,0 +1,20 @@ +<% include( '../elements/process.html', + 'table' => 'addr_block', + 'redirect' => popurl(4). 'browse/addr_block.cgi?dummy=', + 'error_redirect' => popurl(4). 'browse/addr_block.cgi?', + 'agent_virt' => 1, + 'agent_null_right' => 'Broadband global configuration', + + ) +%> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +$cgi->param('routernum', 0) # in FS::addr_block::check instead? + unless $cgi->param('routernum'); + +</%init> diff --git a/httemplate/edit/process/addr_block/allocate.cgi b/httemplate/edit/process/addr_block/allocate.cgi new file mode 100755 index 000000000..40d04b369 --- /dev/null +++ b/httemplate/edit/process/addr_block/allocate.cgi @@ -0,0 +1,16 @@ +<% include( '../elements/process.html', + 'table' => 'addr_block', + 'copy_on_empty' => [ fields 'addr_block' ], + 'error_redirect' => popurl(3). 'allocate.html?', + 'popup_reload' => 'Block allocated', + ) +%> +<%init> + +my $conf = new FS::Conf; +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +</%init> diff --git a/httemplate/edit/process/addr_block/deallocate.cgi b/httemplate/edit/process/addr_block/deallocate.cgi new file mode 100755 index 000000000..128824ec7 --- /dev/null +++ b/httemplate/edit/process/addr_block/deallocate.cgi @@ -0,0 +1,20 @@ +<% include( '../elements/process.html', + 'table' => 'addr_block', + 'copy_on_empty' => [ grep { $_ ne 'routernum' } + fields 'addr_block' ], + 'redirect' => popurl(4). 'browse/addr_block.cgi?', + 'error_redirect' => popurl(4). 'browse/addr_block.cgi?', + 'agent_virt' => 1, + 'agent_null_right' => 'Broadband global configuration', + ) +%> +<%init> + +my $conf = new FS::Conf; +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +$cgi->param('routernum', 0); # just to be explicit about what we are doing +</%init> diff --git a/httemplate/edit/process/addr_block/manual_flag.cgi b/httemplate/edit/process/addr_block/manual_flag.cgi new file mode 100755 index 000000000..dc0cbbbf5 --- /dev/null +++ b/httemplate/edit/process/addr_block/manual_flag.cgi @@ -0,0 +1,30 @@ +<% $cgi->redirect(popurl(4). "browse/addr_block.cgi?". $cgi->query_string ) %> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +my $error = ''; +$cgi->param('blocknum') =~ /^(\d+)$/ or die "invalid blocknum"; +my $blocknum = $1; + +my $addr_block = qsearchs({ 'table' => 'addr_block', + 'hashref' => { blocknum => $blocknum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql( + 'null_right' => 'Broadband global configuration' + ), + }) + or $error = "Unknown blocknum: $blocknum"; + +$addr_block->manual_flag($cgi->param('manual_flag')) + unless $error; + +$error ||= $addr_block->replace; + +$cgi->param('error', $error) + if $error; + +</%init> diff --git a/httemplate/edit/process/addr_block/split.cgi b/httemplate/edit/process/addr_block/split.cgi new file mode 100755 index 000000000..045fd30de --- /dev/null +++ b/httemplate/edit/process/addr_block/split.cgi @@ -0,0 +1,27 @@ +<% $cgi->redirect(popurl(4). "browse/addr_block.cgi?". $cgi->query_string ) %> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +my $error = ''; +$cgi->param('blocknum') =~ /^(\d+)$/ or die "invalid blocknum"; +my $blocknum = $1; + +my $addr_block = qsearchs({ 'table' => 'addr_block', + 'hashref' => { blocknum => $blocknum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql( + 'null_right' => 'Broadband global configuration' + ), + }) + or $error = "Unknown blocknum: $blocknum"; + +$error ||= $addr_block->split_block; + +$cgi->param('error', $error) + if $error; + +</%init> diff --git a/httemplate/edit/process/agent.cgi b/httemplate/edit/process/agent.cgi new file mode 100755 index 000000000..e776d281c --- /dev/null +++ b/httemplate/edit/process/agent.cgi @@ -0,0 +1,21 @@ +<% include( 'elements/process.html', + 'table' => 'agent', + 'viewall_dir' => 'browse', + 'viewall_ext' => 'cgi', + 'process_m2m' => { 'link_table' => 'access_groupagent', + 'target_table' => 'access_group', + }, + 'edit_ext' => 'cgi', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +if ( FS::Conf->new->exists('disable_acl_changes') ) { + errorpage('ACL changes disabled in public demo.'); + die "shouldn't be reached"; +} + +</%init> diff --git a/httemplate/edit/process/agent_payment_gateway.html b/httemplate/edit/process/agent_payment_gateway.html new file mode 100644 index 000000000..5b5fd948a --- /dev/null +++ b/httemplate/edit/process/agent_payment_gateway.html @@ -0,0 +1,29 @@ +<% $cgi->redirect(popurl(3). "browse/agent.cgi") %> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +$cgi->param('agentnum') =~ /(\d+)$/ or die "illegal agentnum"; +my $agent = qsearchs('agent', { 'agentnum' => $1 } ); +die "agentnum $1 not found" unless $agent; + +#my $old + +my @new = map { + my $cardtype = $_; + new FS::agent_payment_gateway { + ( map { $_ => scalar($cgi->param($_)) } + fields('agent_payment_gateway') + ), + 'cardtype' => $cardtype, + }; + } + $cgi->param('cardtype'); + +foreach my $new (@new) { + my $error = $new->insert; + die $error if $error; +} + +</%init> diff --git a/httemplate/edit/process/agent_type.cgi b/httemplate/edit/process/agent_type.cgi new file mode 100755 index 000000000..ad5963b31 --- /dev/null +++ b/httemplate/edit/process/agent_type.cgi @@ -0,0 +1,35 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "agent_type.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/agent_type.cgi") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $typenum = $cgi->param('typenum'); +my $old = qsearchs('agent_type',{'typenum'=>$typenum}) if $typenum; + +my $new = new FS::agent_type ( { + map { + $_, scalar($cgi->param($_)); + } fields('agent_type') +} ); + +my $error; +if ( $typenum ) { + $error = $new->replace($old); +} else { + $error = $new->insert; + $typenum = $new->getfield('typenum'); +} + + $error ||= $new->process_m2m( + 'link_table' => 'type_pkgs', + 'target_table' => 'part_pkg', + 'params' => scalar($cgi->Vars) + ); + +</%init> diff --git a/httemplate/edit/process/bulk-cust_main_county.html b/httemplate/edit/process/bulk-cust_main_county.html new file mode 100644 index 000000000..af9e49500 --- /dev/null +++ b/httemplate/edit/process/bulk-cust_main_county.html @@ -0,0 +1,66 @@ +% if ( $error ) { #better to redirect back to +%# <% $cgi->redirect("$url?". $cgi->query_string ) %> + <% include('/elements/header-popup.html', "Error ${action}ing taxes" ) %> + + <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT> + <BR><BR> + + </BODY> + </HTML> + +% } else { + <% include('/elements/header-popup.html', "Taxes ${action}ed") %> + + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + + </BODY> + </HTML> +% } +<%init> + +$cgi->param('taxnum') =~ /^([\d,]+)$/ + or die 'Guru Meditation #69'; #??? should have been passed in +my @taxnum = split(',', $1); + +$cgi->param('action') =~ /^(add|edit)$/ or die "unknown action"; +my $action = $1; + +my $error = ''; +foreach my $taxnum ( @taxnum ) { + + my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } ) + or die "unknown taxnum: $taxnum"; + + if ( $action eq 'edit' || $cust_main_county->tax == 0 ) { #let's replace + + foreach (qw( taxname tax exempt_amount setuptax recurtax )) { + $cust_main_county->set( $_ => scalar($cgi->param($_)) ) + } + + $error = $cust_main_county->replace and last; + + } else { #let's insert a new record + + my $new = + new FS::cust_main_county { + ( map { $_ => scalar($cgi->param($_)) } + qw( taxname tax exempt_amount setuptax recurtax ) + ), + ( map { $_ => $cust_main_county->get($_) } + qw( country state county taxclass ) + ) + }; + + $error = $new->insert and last; + + } + +} + +if ( $error ) { + $cgi->param('error', $error); +} + +</%init> diff --git a/httemplate/edit/process/bulk-cust_svc.cgi b/httemplate/edit/process/bulk-cust_svc.cgi new file mode 100644 index 000000000..313b061ff --- /dev/null +++ b/httemplate/edit/process/bulk-cust_svc.cgi @@ -0,0 +1,9 @@ +<% $server->process %> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $server = new FS::UI::Web::JSRPC 'FS::part_svc::process_bulk_cust_svc', $cgi; + +</%init> diff --git a/httemplate/edit/process/cgp_rule.html b/httemplate/edit/process/cgp_rule.html new file mode 100644 index 000000000..5326587cb --- /dev/null +++ b/httemplate/edit/process/cgp_rule.html @@ -0,0 +1,30 @@ +<% include( 'elements/process.html', + 'table' => 'cgp_rule', + 'redirect' => $redirect, + 'process_o2m' => [ + { + 'table' => 'cgp_rule_condition', + 'fields' => [qw( conditionname op params )], + }, + { + 'table' => 'cgp_rule_action', + 'fields' => [qw( action params )], + }, + ], + 'noerror_callback' => sub { + my( $cgi, $object ) = @_; + my $error = $object->svc_export; + #shit, not a good place for error handling :/ + die $error if $error; + }, + ) +%> +<%init> + +my $redirect = sub { + my($cgi, $new) = @_; + my $svcnum = $new->svcnum; + popurl(3)."browse/cgp_rule.html?svcnum=$svcnum;rulenum="; +}; + +</%init> diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html new file mode 100644 index 000000000..7356e6122 --- /dev/null +++ b/httemplate/edit/process/change-cust_pkg.html @@ -0,0 +1,46 @@ +% if ($error) { +% $cgi->param('error', $error); +% $cgi->redirect(popurl(3). 'misc/change_pkg.cgi?'. $cgi->query_string ); +% } else { + + <% header("Package 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({ + #'select' => 'cust_pkg.*', + '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 %change = map { $_ => scalar($cgi->param($_)) } + qw( locationnum pkgpart ); + +if ( $cgi->param('locationnum') == -1 ) { + my $cust_location = new FS::cust_location { + 'custnum' => $cust_pkg->custnum, + map { $_ => scalar($cgi->param($_)) } + qw( address1 address2 city county state zip country ) + }; + $change{'cust_location'} = $cust_location; +} + +my $pkg_or_error = $cust_pkg->change( \%change ); + +my $error = ref($pkg_or_error) ? '' : $pkg_or_error; + +</%init> diff --git a/httemplate/edit/process/cust_bill_pay.cgi b/httemplate/edit/process/cust_bill_pay.cgi new file mode 100755 index 000000000..2845d3233 --- /dev/null +++ b/httemplate/edit/process/cust_bill_pay.cgi @@ -0,0 +1,13 @@ +<% include('elements/ApplicationCommon.html', + 'error_redirect' => 'cust_bill_pay.cgi', + 'src_table' => 'cust_pay', + 'src_thing' => 'payment', + 'link_table' => 'cust_bill_pay', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/process/cust_category.html b/httemplate/edit/process/cust_category.html new file mode 100644 index 000000000..c3a880944 --- /dev/null +++ b/httemplate/edit/process/cust_category.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'cust_category', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/cust_class.html b/httemplate/edit/process/cust_class.html new file mode 100644 index 000000000..3f63ea4b3 --- /dev/null +++ b/httemplate/edit/process/cust_class.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'cust_class', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/cust_credit.cgi b/httemplate/edit/process/cust_credit.cgi new file mode 100755 index 000000000..8715ad61e --- /dev/null +++ b/httemplate/edit/process/cust_credit.cgi @@ -0,0 +1,63 @@ +%if ( $error ) { +% $cgi->param('reasonnum', $reasonnum); +% $cgi->param('error', $error); +% $dbh->rollback if $oldAutoCommit; +% +<% $cgi->redirect(popurl(2). "cust_credit.cgi?". $cgi->query_string ) %> +% +%} else { +% +% if ( $cgi->param('apply') eq 'yes' ) { +% my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum }) +% or die "unknown custnum $custnum"; +% $cust_main->apply_credits; +% } +% #print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum"); +% +% $dbh->commit or die $dbh->errstr if $oldAutoCommit; +% +<% header('Credit sucessful') %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + + </BODY></HTML> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Post credit'); + +$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!"; +my $custnum = $1; + +$cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum"; +my $reasonnum = $1; + +my $oldAutoCommit = $FS::UID::AutoCommit; +local $FS::UID::AutoCommit = 0; +my $dbh = dbh; + +my $error = ''; +if ($reasonnum == -1) { + + $error = 'Enter a new reason (or select an existing one)' + unless $cgi->param('newreasonnum') !~ /^\s*$/; + my $reason = new FS::reason({ 'reason_type' => $cgi->param('newreasonnumT'), + 'reason' => $cgi->param('newreasonnum'), + }); + $error ||= $reason->insert; + $cgi->param('reasonnum', $reason->reasonnum) + unless $error; +} + +unless ($error) { + my $new = new FS::cust_credit ( { + map { + $_, scalar($cgi->param($_)); + } fields('cust_credit') + } ); + $error = $new->insert; +} + +</%init> diff --git a/httemplate/edit/process/cust_credit_bill.cgi b/httemplate/edit/process/cust_credit_bill.cgi new file mode 100755 index 000000000..d3847dc40 --- /dev/null +++ b/httemplate/edit/process/cust_credit_bill.cgi @@ -0,0 +1,19 @@ +<% include('elements/ApplicationCommon.html', + 'error_redirect' => 'cust_credit_bill.cgi', + 'src_table' => 'cust_credit', + 'src_thing' => 'credit', + 'link_table' => 'cust_credit_bill', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply credit'); + +if ( $cgi->param('src_amount') ) { + die "access denied" + unless ( $FS::CurrentUser::CurrentUser->access_right('Post credit') && + $FS::CurrentUser::CurrentUser->access_right('Delete credit') ); +} + +</%init> diff --git a/httemplate/edit/process/cust_credit_refund.cgi b/httemplate/edit/process/cust_credit_refund.cgi new file mode 100755 index 000000000..88420f8ab --- /dev/null +++ b/httemplate/edit/process/cust_credit_refund.cgi @@ -0,0 +1,13 @@ +<% include('elements/ApplicationCommon.html', + 'error_redirect' => 'cust_credit_refund.cgi', + 'src_table' => 'cust_credit', + 'src_thing' => 'credit', + 'link_table' => 'cust_credit_refund', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply credit'); + +</%init> diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi new file mode 100755 index 000000000..3158d7bbf --- /dev/null +++ b/httemplate/edit/process/cust_main.cgi @@ -0,0 +1,262 @@ +% if ( $error ) { +% $cgi->param('error', $error); +% +<% $cgi->redirect(popurl(2). "cust_main.cgi?". $cgi->query_string ) %> +% +% } else { +% +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?". $new->custnum) %> +% +% } +<%once> + +my $me = '[edit/process/cust_main.cgi]'; +my $DEBUG = 0; + +</%once> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit customer'); + +my $conf = new FS::Conf; + +my $error = ''; + +#unmunge stuff + +$cgi->param('tax','') unless defined $cgi->param('tax'); + +$cgi->param('refnum', (split(/:/, ($cgi->param('refnum'))[0] ))[0] ); + +my $payby = $cgi->param('payby'); + +my %noauto = ( + 'CARD' => 'DCRD', + 'CHEK' => 'DCHK', +); +$payby = $noauto{$payby} + if ! $cgi->param('payauto') && exists $noauto{$payby}; + +$cgi->param('payby', $payby); + +if ( $payby ) { + if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) { + $cgi->param('payinfo', + $cgi->param('payinfo1'). '@'. $cgi->param('payinfo2') ); + } + $cgi->param('paydate', + $cgi->param( 'exp_month' ). '-'. $cgi->param( 'exp_year' ) ); +} + +my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') ); +push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST'); +push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX'); +$cgi->param('invoicing_list', join(',', @invoicing_list) ); + + +#create new record object + +my $new = new FS::cust_main ( { + map { + $_, scalar($cgi->param($_)) +# } qw(custnum agentnum last first ss company address1 address2 city county +# state zip daytime night fax payby payinfo paydate payname tax +# otaker refnum) + } fields('cust_main') +} ); + +if ( defined($cgi->param('same')) && $cgi->param('same') eq "Y" ) { + $new->setfield("ship_$_", '') foreach qw( + last first company address1 address2 city county state zip + country daytime night fax + ); +} + +$new->tagnum( [ $cgi->param('tagnum') ] ); + +my %usedatetime = ( 'birthdate' => 1 ); + +foreach my $dfield (qw( birthdate signupdate )) { + + if ( $cgi->param($dfield) && $cgi->param($dfield) =~ /^([ 0-9\-\/]{0,10})$/) { + + my $value = $1; + my $parsed = ''; + + if ( exists $usedatetime{$dfield} && $usedatetime{$dfield} ) { + + my $format = $conf->config('date_format') || "%m/%d/%Y"; + my $parser = DateTime::Format::Strptime->new( pattern => $format, + time_zone => 'floating', + ); + my $dt = $parser->parse_datetime($value); + if ( $dt ) { + $parsed = $dt->epoch; + } else { + # $error ||= $cgi->param('birthdate') . " is an invalid birthdate:" . $parser->errmsg; + $error ||= "Invalid $dfield: $value"; + } + + } else { + + $parsed = parse_datetime($value) + or $error ||= "Invalid $dfield: $value"; + + } + + $new->setfield( $dfield, $parsed ); + $cgi->param( $dfield, $parsed ); + + } + +} + +$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; + +#perhaps this stuff should go to cust_main.pm +if ( $new->custnum eq '' ) { + + my $cust_pkg = ''; + my $svc; + + if ( $cgi->param('pkgpart_svcpart') ) { + + my $x = $cgi->param('pkgpart_svcpart'); + $x =~ /^(\d+)_(\d+)$/ or die "illegal pkgpart_svcpart $x\n"; + my($pkgpart, $svcpart) = ($1, $2); + my $part_pkg = qsearchs('part_pkg', { 'pkgpart' => $pkgpart } ); + #false laziness: copied from FS::cust_pkg::order (which should become a + #FS::cust_main method) + my(%part_pkg); + # generate %part_pkg + # $part_pkg{$pkgpart} is true iff $custnum may purchase $pkgpart + my $agent = qsearchs('agent',{'agentnum'=> $new->agentnum }); + + if ( $agent ) { + # $pkgpart_href->{PKGPART} is true iff $custnum may purchase $pkgpart + my $pkgpart_href = $agent->pkgpart_hashref + if $agent; + #eslaf + + # this should wind up in FS::cust_pkg! + $error ||= "Agent ". $new->agentnum. " (type ". $agent->typenum. + ") can't purchase pkgpart ". $pkgpart + #unless $part_pkg{ $pkgpart }; + unless $pkgpart_href->{ $pkgpart } + || $agent->agentnum == $part_pkg->agentnum; + } else { + $error = 'Select agent'; + } + + $cust_pkg = new FS::cust_pkg ( { + #later 'custnum' => $custnum, + 'pkgpart' => $pkgpart, + } ); + #$error ||= $cust_pkg->check; + + #$cust_svc = new FS::cust_svc ( { 'svcpart' => $svcpart } ); + + #$error ||= $cust_svc->check; + + my $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } ); + my $svcdb = $part_svc->svcdb; + + if ( $svcdb eq 'svc_acct' ) { + + my %svc_acct = ( + 'svcpart' => $svcpart, + 'username' => scalar($cgi->param('username')), + '_password' => scalar($cgi->param('_password')), + 'popnum' => scalar($cgi->param('popnum')), + ); + $svc_acct{'domsvc'} = $cgi->param('domsvc') + if $cgi->param('domsvc'); + + $svc = new FS::svc_acct \%svc_acct; + + #and just in case you were silly + $svc->svcpart($svcpart); + $svc->username($cgi->param('username')); + $svc->_password($cgi->param('_password')); + $svc->popnum($cgi->param('popnum')); + + } elsif ( $svcdb eq 'svc_phone' ) { + + my %svc_phone = ( + 'svcpart' => $svcpart, + map { $_ => scalar($cgi->param($_)) } + qw( countrycode phonenum sip_password pin phone_name ) + ); + + $svc = new FS::svc_phone \%svc_phone; + + } else { + die "$svcdb not handled on new customer yet"; + } + + #$error ||= $svc_acct->check; + + } + + use Tie::RefHash; + tie my %hash, 'Tie::RefHash'; + %hash = ( $cust_pkg => [ $svc ] ) if $cust_pkg; + $error ||= $new->insert( \%hash, \@invoicing_list, + 'tax_exemption' => \@tax_exempt, + ); + + my $conf = new FS::Conf; + if ( $conf->exists('backend-realtime') && ! $error ) { + + my $berror = $new->bill + || $new->apply_payments_and_credits + || $new->collect( 'realtime' => 1 ); + warn "Warning, error billing during backend-realtime: $berror" if $berror; + + } + +} else { #create old record object + + my $old = qsearchs( 'cust_main', { 'custnum' => $new->custnum } ); + $error ||= "Old record not found!" unless $old; + if ( length($old->paycvv) && $new->paycvv =~ /^\s*\*+\s*$/ ) { + $new->paycvv($old->paycvv); + } + if ($new->ss =~ /xx/) { + $new->ss($old->ss); + } + if ($new->stateid =~ /^xxx/) { + $new->stateid($old->stateid); + } + if ($new->payby =~ /^(CARD|DCRD)$/ && $new->payinfo =~ /xx/) { + $new->payinfo($old->payinfo); + } elsif ($new->payby =~ /^(CHEK|DCHK)$/ && $new->payinfo =~ /xx/) { + #fix for #3085 "edit of customer's routing code only surprisingly causes + #nothing to happen... + # this probably won't do the right thing when we don't have the + # public key (can't actually get the real $old->payinfo) + my($new_account, $new_aba) = split('@', $new->payinfo); + my($old_account, $old_aba) = split('@', $old->payinfo); + $new_account = $old_account if $new_account =~ /xx/; + $new_aba = $old_aba if $new_aba =~ /xx/; + $new->payinfo($new_account.'@'.$new_aba); + } + + warn "$me calling $new -> replace( $old, \ @invoicing_list )" if $DEBUG; + local($FS::cust_main::DEBUG) = $DEBUG if $DEBUG; + local($FS::Record::DEBUG) = $DEBUG if $DEBUG; + + $error ||= $new->replace( $old, \@invoicing_list, + 'tax_exemption' => \@tax_exempt, + ); + + warn "$me returned from replace" if $DEBUG; + +} + +</%init> diff --git a/httemplate/edit/process/cust_main_attach.cgi b/httemplate/edit/process/cust_main_attach.cgi new file mode 100644 index 000000000..291135718 --- /dev/null +++ b/httemplate/edit/process/cust_main_attach.cgi @@ -0,0 +1,99 @@ +%if ($error) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). 'cust_main_attach.cgi?'. $cgi->query_string ) %> +%} else { +% my $act = 'added'; +% $act = 'updated' if ($attachnum); +% $act = 'purged' if($attachnum and $purge); +% $act = 'undeleted' if($attachnum and $undelete); +% $act = 'deleted' if($attachnum and $delete); +<% header('Attachment ' . $act ) %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +my $error; +$cgi->param('custnum') =~ /^(\d+)$/ + or die "Illegal custnum: ". $cgi->param('custnum'); +my $custnum = $1; + +$cgi->param('attachnum') =~ /^(\d*)$/ + or die "Illegal attachnum: ". $cgi->param('attachnum'); +my $attachnum = $1; + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $delete = $cgi->param('delete'); +my $undelete = $cgi->param('undelete'); +my $purge = $cgi->param('purge'); + +my $new = new FS::cust_attachment ( { + attachnum => $attachnum, + custnum => $custnum, + _date => time, + usernum => $curuser->usernum, + disabled => '', +}); +my $old; + +if($attachnum) { + $old = qsearchs('cust_attachment', { attachnum => $attachnum }); + if(!$old) { + $error = "Attachnum '$attachnum' not found"; + } + elsif($purge) { # do nothing + } + else { + map { $new->$_($old->$_) } + ('_date', 'otaker', 'body', 'disabled'); + $new->filename($cgi->param('filename') || $old->filename); + $new->mime_type($cgi->param('mime_type') || $old->mime_type); + $new->title($cgi->param('title')); + if($delete and not $old->disabled) { + $new->disabled(time); + } + if($undelete and $old->disabled) { + $new->disabled(''); + } + } +} +else { # This is a new attachment, so require a file. + + my $filename = $cgi->param('file'); + if($filename) { + $new->filename($filename); + $new->mime_type($cgi->uploadInfo($filename)->{'Content-Type'}); + $new->title($cgi->param('title')); + + local $/; + my $fh = $cgi->upload('file'); + $new->body(<$fh>); + } + else { + $error = 'No file uploaded'; + } +} +my $action = 'Add'; +$action = 'Edit' if $attachnum; +$action = 'Delete' if $attachnum and $delete; +$action = 'Undelete' if $attachnum and $undelete; +$action = 'Purge' if $attachnum and $purge; + +$error = 'access denied' unless $curuser->access_right($action . ' attachment'); + +if(!$error) { + if($old and $old->disabled and $purge) { + $error = $old->delete; + } + elsif($old) { + $error = $new->replace($old); + } + else { + $error = $new->insert; + } +} + +</%init> diff --git a/httemplate/edit/process/cust_main_county-collapse.cgi b/httemplate/edit/process/cust_main_county-collapse.cgi new file mode 100755 index 000000000..9608fc919 --- /dev/null +++ b/httemplate/edit/process/cust_main_county-collapse.cgi @@ -0,0 +1,46 @@ +<% $cgi->redirect(popurl(3). "browse/cust_main_county.cgi") %> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my($query) = $cgi->keywords; +$query =~ /^(\d+)$/ or die "Illegal taxnum!"; +my $taxnum = $1; +my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } ) + or die "Unknown taxnum $taxnum"; + +#really should do this in a .pm & start transaction + +my %search = ( + 'country' => $cust_main_county->country, + 'state' => $cust_main_county->state, + ); + +$search{'county'} = $cust_main_county->county + if $cust_main_county->city; + +foreach my $delete ( qsearch('cust_main_county', \%search) ) { +# unless ( qsearch('cust_main',{ +# 'state' => $cust_main_county->getfield('state'), +# 'county' => $cust_main_county->getfield('county'), +# 'country' => $cust_main_county->getfield('country'), +# } ) ) { + my $error = $delete->delete; + die $error if $error; +# } else { + #should really fix the $cust_main record +# } + +} + +$cust_main_county->taxnum(''); +if ( $cust_main_county->city ) { + $cust_main_county->city(''); +} else { + $cust_main_county->county(''); +} +my $error = $cust_main_county->insert; +die $error if $error; + +</%init> diff --git a/httemplate/edit/process/cust_main_county-expand.cgi b/httemplate/edit/process/cust_main_county-expand.cgi new file mode 100755 index 000000000..9984b08fa --- /dev/null +++ b/httemplate/edit/process/cust_main_county-expand.cgi @@ -0,0 +1,83 @@ +<% include('/elements/header-popup.html', 'Addition successful' ) %> + +<SCRIPT TYPE="text/javascript"> + window.top.location.reload(); +</SCRIPT> + +</BODY> +</HTML> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +$cgi->param('taxnum') =~ /^(\d+)$/ or die "Illegal taxnum!"; +my $taxnum = $1; +my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum}) + or die ("Unknown taxnum!"); + +my @expansion; +if ( $cgi->param('taxclass') ) { + my $sth = dbh->prepare('SELECT taxclass FROM part_pkg_taxclass') + or die dbh->errstr; + $sth->execute or die $sth->errstr; + @expansion = map $_->[0], @{$sth->fetchall_arrayref}; + die "no taxclasses - add one first" unless @expansion;#XXX better err handling +} else { + @expansion = split /[\n\r]{1,2}/, $cgi->param('expansion'); + + #warn scalar(@expansion); + #warn "$_: $expansion[$_]\n" foreach (0..$#expansion); + + @expansion=map { + unless ( /^\s*([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]+)\s*$/ ) { + $cgi->param('error', "Illegal item in expansion: $_"); + print $cgi->redirect(popurl(2). "cust_main_county-expand.cgi?". $cgi->query_string ); + myexit(); + } + $1; + } @expansion; + +} + +foreach ( @expansion) { + my(%hash)=$cust_main_county->hash; + my($new)=new FS::cust_main_county \%hash; + $new->setfield('taxnum',''); + if ( $cgi->param('taxclass') ) { + $new->setfield('taxclass', $_); + } elsif ( ! $cust_main_county->state ) { + $new->setfield('state',$_); + } elsif ( ! $cust_main_county->county ) { + $new->setfield('county',$_); + } else { + #uppercase cities in the US to try and agree with USPS validation + $new->setfield('city', $new->country eq 'US' ? uc($_) : $_ ); + } + my $error = $new->insert; + die $error if $error; +} + +unless ( qsearch( 'cust_main', { + 'city' => $cust_main_county->city, + 'county' => $cust_main_county->county, + 'state' => $cust_main_county->state, + 'country' => $cust_main_county->country, + } ) + || ! @expansion +) { + my $error = $cust_main_county->delete; + die $error if $error; +} + +if ( $cgi->param('taxclass') ) { + print $cgi->redirect(popurl(3). "browse/cust_main_county.cgi?". + 'city='. uri_escape($cust_main_county->city ).';'. + 'county='. uri_escape($cust_main_county->county ).';'. + 'state='. uri_escape($cust_main_county->state ).';'. + 'country='. uri_escape($cust_main_county->country) + ); + myexit; +} + +</%init> diff --git a/httemplate/edit/process/cust_main_county.html b/httemplate/edit/process/cust_main_county.html new file mode 100644 index 000000000..cb56166c8 --- /dev/null +++ b/httemplate/edit/process/cust_main_county.html @@ -0,0 +1,13 @@ +<% include( 'elements/process.html', + 'table' => 'cust_main_county', + 'popup_reload' => 'Tax changed', #a popup "parent reload" for now + #someday change the individual element and go away instead + ) +%> +<%init> + +my $conf = new FS::Conf; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/cust_main_note.cgi b/httemplate/edit/process/cust_main_note.cgi new file mode 100755 index 000000000..f904c5968 --- /dev/null +++ b/httemplate/edit/process/cust_main_note.cgi @@ -0,0 +1,55 @@ +%if ($error) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). 'cust_main_note.cgi?'. $cgi->query_string ) %> +%} else { +<% header('Note ' . ($notenum ? 'updated' : 'added') ) %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +$cgi->param('custnum') =~ /^(\d+)$/ + or die "Illegal custnum: ". $cgi->param('custnum'); +my $custnum = $1; + +$cgi->param('notenum') =~ /^(\d*)$/ + or die "Illegal notenum: ". $cgi->param('notenum'); +my $notenum = $1; + +my $comment = $cgi->param('comment_html') || + join("<br />\n", + split "(?:\r|\n)+", $cgi->param('comment_plain') + ); + +my $new = new FS::cust_main_note ( { + notenum => $notenum, + custnum => $custnum, + _date => time, + usernum => $FS::CurrentUser::CurrentUser->usernum, + comments => $comment, +} ); + +my $error; +if ($notenum) { + + die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit customer note'); + + my $old = qsearchs('cust_main_note', { 'notenum' => $notenum }); + $error = "No such note: $notenum" unless $old; + unless ($error) { + map { $new->$_($old->$_) } ('_date', 'otaker'); + $error = $new->replace($old); + } + +} else { + + die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Add customer note'); + + $error = $new->insert; +} + +</%init> diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi new file mode 100755 index 000000000..df506c677 --- /dev/null +++ b/httemplate/edit/process/cust_pay.cgi @@ -0,0 +1,57 @@ +%if ($error) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). 'cust_pay.cgi?'. $cgi->query_string ) %> +%} elsif ( $field eq 'invnum' ) { +<% $cgi->redirect(popurl(3). "view/cust_bill.cgi?$linknum") %> +%} elsif ( $field eq 'custnum' ) { +% if ( $cgi->param('apply') eq 'yes' ) { +% my $cust_main = qsearchs('cust_main', { 'custnum' => $linknum }) +% or die "unknown custnum $linknum"; +% $cust_main->apply_payments( 'manual' => 1 ); +% } +% if ( $link eq 'popup' ) { +% +<% header('Payment entered') %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + + </BODY></HTML> +% +% } elsif ( $link eq 'custnum' ) { +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$linknum") %> +% } else { +% die "unknown link $link"; +% } +% +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Post payment'); + +$cgi->param('linknum') =~ /^(\d+)$/ + or die "Illegal linknum: ". $cgi->param('linknum'); +my $linknum = $1; + +$cgi->param('link') =~ /^(custnum|invnum|popup)$/ + or die "Illegal link: ". $cgi->param('link'); +my $field = my $link = $1; +$field = 'custnum' if $field eq 'popup'; + +my $_date = parse_datetime($cgi->param('_date')); + +my $new = new FS::cust_pay ( { + $field => $linknum, + _date => $_date, + map { + $_, scalar($cgi->param($_)); + } qw( paid payby payinfo paybatch + pkgnum + ) + #} fields('cust_pay') +} ); + +my $error = $new->insert( 'manual' => 1 ); + +</%init> diff --git a/httemplate/edit/process/cust_pay_pending.html b/httemplate/edit/process/cust_pay_pending.html new file mode 100644 index 000000000..1bad6cffe --- /dev/null +++ b/httemplate/edit/process/cust_pay_pending.html @@ -0,0 +1,68 @@ +<% include('/elements/header-popup.html', $title ) %> +% if ( $error ) { + <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT> +% } else { + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> +% } +</BODY> +</HTML> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Edit customer pending payments'); + +$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action'; +my $action = $1; + +$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum'; +my $paypendingnum = $1; +my $cust_pay_pending = + qsearchs({ + 'select' => 'cust_pay_pending.*', + 'table' => 'cust_pay_pending', + 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'paypendingnum' => $paypendingnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, + }) + or die 'unknown paypendingnum'; + +my $error; +my $title; +if ( $action eq 'delete' ) { + + $error = $cust_pay_pending->delete; + if ( $error ) { + $title = 'Error deleting pending payment'; + } else { + $title = 'Pending payment deletion sucessful'; + } + +} elsif ( $action eq 'insert_cust_pay' ) { + + $error = $cust_pay_pending->insert_cust_pay; + if ( $error ) { + $title = 'Error completing pending payment'; + } else { + $title = 'Pending payment completed'; + } + +} elsif ( $action eq 'decline' ) { + + $error = $cust_pay_pending->decline; + if ( $error ) { + $title = 'Error declining pending payment'; + } else { + $title = 'Pending payment completed (decline)'; + } + +} else { + + die "unknown action $action"; + +} + +</%init> diff --git a/httemplate/edit/process/cust_pay_refund.cgi b/httemplate/edit/process/cust_pay_refund.cgi new file mode 100755 index 000000000..2616cad8c --- /dev/null +++ b/httemplate/edit/process/cust_pay_refund.cgi @@ -0,0 +1,13 @@ +<% include('elements/ApplicationCommon.html', + 'error_redirect' => 'cust_pay_refund.cgi', + 'src_table' => 'cust_pay', + 'src_thing' => 'payment', + 'link_table' => 'cust_pay_refund', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/process/cust_pkg.cgi b/httemplate/edit/process/cust_pkg.cgi new file mode 100755 index 000000000..c564c417e --- /dev/null +++ b/httemplate/edit/process/cust_pkg.cgi @@ -0,0 +1,42 @@ +% if ($error) { +% $cgi->param('error', $error); +% $cgi->redirect(popurl(3). 'edit/cust_pkg.cgi?'. $cgi->query_string ); +% } else { +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum") %> +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Bulk change customer packages'); + +my $error = ''; + +#untaint custnum +$cgi->param('custnum') =~ /^(\d+)$/; +my $custnum = $1; + +my @remove_pkgnums = map { + /^(\d+)$/ or die "Illegal remove_pkg value!"; + $1; +} $cgi->param('remove_pkg'); + +my( $action, $error_redirect ) = ( '', '' ); +my @pkgparts = (); + +foreach my $pkgpart ( map /^pkg(\d+)$/ ? $1 : (), $cgi->param ) { + if ( $cgi->param("pkg$pkgpart") =~ /^(\d+)$/ ) { + my $num_pkgs = $1; + while ( $num_pkgs-- ) { + push @pkgparts,$pkgpart; + } + } else { + $error = "Illegal quantity"; + last; + } +} + +$error ||= FS::cust_pkg::order($custnum,\@pkgparts,\@remove_pkgnums); + +</%init> diff --git a/httemplate/edit/process/cust_pkg_detail.html b/httemplate/edit/process/cust_pkg_detail.html new file mode 100644 index 000000000..132ff63c5 --- /dev/null +++ b/httemplate/edit/process/cust_pkg_detail.html @@ -0,0 +1,59 @@ +% if ( $error ) { +<% header('Error') %> +<FONT COLOR="#ff0000"><B><% $error |h %></B></FONT><BR><BR> +<CENTER><INPUT TYPE="BUTTON" VALUE="OK" onClick="parent.cClick()"></CENTER> +</BODY></HTML> +% } else { +<% header($action) %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +my %access_right = ( + 'I' => 'Edit customer package invoice details', + 'C' => 'Edit customer package comments', +); + +my %name = ( + 'I' => 'invoice details', + 'C' => 'package comments', +); + +my $curuser = $FS::CurrentUser::CurrentUser; + +$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype'; +my $detailtype = $1; + +my $right = $access_right{$detailtype}; +die "access denied" + unless $curuser->access_right($right); + +$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, +}); + + +my @orig_details = $cust_pkg->cust_pkg_detail($detailtype); + +my $action = ucfirst($name{$detailtype}). + ( scalar(@orig_details) ? ' changed ' : ' added ' ); + +my $param = $cgi->Vars; +my @details = (); +for ( my $row = 0; exists($param->{"detail$row"}); $row++ ) { + push @details, $param->{"detail$row"} + if $param->{"detail$row"} =~ /\S/; +} + +my $error = $cust_pkg->set_cust_pkg_detail($detailtype, @details); + +</%init> diff --git a/httemplate/edit/process/cust_pkg_discount.html b/httemplate/edit/process/cust_pkg_discount.html new file mode 100644 index 000000000..e3249d79c --- /dev/null +++ b/httemplate/edit/process/cust_pkg_discount.html @@ -0,0 +1,47 @@ +% if ($error) { +% $cgi->param('error', $error); +% $cgi->redirect(popurl(3). 'edit/cust_pkg_discount.html?'. $cgi->query_string ); +% } else { + + <% header("Discount applied") %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY> + </HTML> + +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Discount customer package'); + +#this search is really for security wrt agent virt... +#maybe move it to the cust_pkg_discount->insert call? +my $cust_pkg = qsearchs({ + #'select' => 'cust_pkg.*', + '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_pkg_discount = new FS::cust_pkg_discount { + 'pkgnum' => $cust_pkg->pkgnum, + 'discountnum' => scalar($cgi->param('discountnum')), + 'months_used' => 0, + 'end_date' => '', #XXX + 'otaker' => $curuser->username, + #for the create a new discount case + '_type' => scalar($cgi->param('discountnum__type')), + 'amount' => scalar($cgi->param('discountnum_amount')), + 'percent' => scalar($cgi->param('discountnum_percent')), + 'months' => scalar($cgi->param('discountnum_months')), + #'disabled' => $self->discountnum_disabled, +}; +my $error = $cust_pkg_discount->insert; + +</%init> diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi new file mode 100755 index 000000000..5749e5346 --- /dev/null +++ b/httemplate/edit/process/cust_refund.cgi @@ -0,0 +1,56 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "cust_refund.cgi?". $cgi->query_string ) %> +%} else { +% +% if ( $link eq 'popup' ) { +% +<% header('Refund entered') %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + + </BODY></HTML> +% } else { +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum") %> +% } +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Refund payment') + || $FS::CurrentUser::CurrentUser->access_right('Post refund'); + +$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!"; +my $custnum = $1; +my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) + or die "unknown custnum $custnum"; + +my $link = $cgi->param('popup') ? 'popup' : ''; + +my $error = ''; +if ( $cgi->param('payby') =~ /^(CARD|CHEK)$/ ) { + my %options = (); + my $bop = $FS::payby::payby2bop{$1}; + $cgi->param('refund') =~ /^(\d*)(\.\d{2})?$/ + or die "illegal refund amount ". $cgi->param('refund'); + my $refund = "$1$2"; + $cgi->param('paynum') =~ /^(\d*)$/ or die "Illegal paynum!"; + my $paynum = $1; + my $reason = $cgi->param('reason'); + my $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01'; + $options{'paydate'} = $paydate if $paydate =~ /^\d{2,4}-\d{1,2}-01$/; + $error = $cust_main->realtime_refund_bop( $bop, 'amount' => $refund, + 'paynum' => $paynum, + 'reason' => $reason, + %options ); +} else { + my $new = new FS::cust_refund ( { + map { + $_, scalar($cgi->param($_)); + } fields('cust_refund') #huh? , 'paynum' ) + } ); + $error = $new->insert; +} + +</%init> diff --git a/httemplate/edit/process/cust_svc.cgi b/httemplate/edit/process/cust_svc.cgi new file mode 100644 index 000000000..e22cbb201 --- /dev/null +++ b/httemplate/edit/process/cust_svc.cgi @@ -0,0 +1,30 @@ +%if ( $error ) { +% errorpage($error); +%} else { +% my $svcdb = $new->part_svc->svcdb; +<% $cgi->redirect(popurl(3). "view/$svcdb.cgi?$svcnum") %> +%} +<%init> + +die 'access deined' + unless $FS::CurrentUser::CurrentUser->access_right('Change customer service'); + +my $svcnum = $cgi->param('svcnum'); + +my $old = qsearchs('cust_svc',{'svcnum'=>$svcnum}) if $svcnum; + +my $new = new FS::cust_svc ( { + map { + $_, scalar($cgi->param($_)); + } fields('cust_svc') +} ); + +my $error; +if ( $svcnum ) { + $error=$new->replace($old); +} else { + $error=$new->insert; + $svcnum=$new->getfield('svcnum'); +} + +</%init> diff --git a/httemplate/edit/process/cust_tax_adjustment.html b/httemplate/edit/process/cust_tax_adjustment.html new file mode 100644 index 000000000..204b5b9f7 --- /dev/null +++ b/httemplate/edit/process/cust_tax_adjustment.html @@ -0,0 +1,41 @@ +% if ( $error ) { +% $cgi->param('error', $error ); +<% $cgi->redirect($p.'cust_tax_adjustment.html?'. $cgi->query_string) %> +% } else { +<% header("Tax adjustment added") %> + <SCRIPT TYPE="text/javascript"> + //window.top.location.reload(); + parent.cClick(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Add customer tax adjustment'); + +my $error = ''; +my $conf = new FS::conf; +my $param = $cgi->Vars; + +$param->{"custnum"} =~ /^(\d+)$/ + or $error .= "Illegal customer number " . $param->{"custnum"} . " "; +my $custnum = $1; + +$param->{"amount"} =~ /^\s*(\d*(?:\.?\d{1,2}))\s*$/ + or $error .= "Illegal amount " . $param->{"amount"} . " "; +my $amount = $1; + +unless ( $error ) { + + my $cust_tax_adjustment = new FS::cust_tax_adjustment { + 'custnum' => $custnum, + 'taxname' => $param->{'taxname'}, + 'amount' => $amount, + 'comment' => $param->{'comment'}, + }; + $error = $cust_tax_adjustment->insert; + +} + +</%init> diff --git a/httemplate/edit/process/discount.html b/httemplate/edit/process/discount.html new file mode 100644 index 000000000..eb4e92e9f --- /dev/null +++ b/httemplate/edit/process/discount.html @@ -0,0 +1,12 @@ +<% include( 'elements/process.html', + 'table' => 'discount', + 'viewall_dir' => 'browse', + 'fields' => [ fields('discount'), '_type' ], + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/domain_record.cgi b/httemplate/edit/process/domain_record.cgi new file mode 100755 index 000000000..2e427e4fb --- /dev/null +++ b/httemplate/edit/process/domain_record.cgi @@ -0,0 +1,30 @@ +%if ( $error ) { +% errorpage($error); +%} else { +% my $svcnum = $new->svcnum; +<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice'); + +my $recnum = $cgi->param('recnum'); + +my $old = qsearchs('agent',{'recnum'=>$recnum}) if $recnum; + +my $new = new FS::domain_record ( { + map { + $_, scalar($cgi->param($_)); + } fields('domain_record') +} ); + +my $error; +if ( $recnum ) { + $error=$new->replace($old); +} else { + $error=$new->insert; + $recnum=$new->getfield('recnum'); +} + +</%init> diff --git a/httemplate/edit/process/domreg.cgi b/httemplate/edit/process/domreg.cgi new file mode 100755 index 000000000..a95474e44 --- /dev/null +++ b/httemplate/edit/process/domreg.cgi @@ -0,0 +1,62 @@ +%if ($error) { +% $cgi->param('error', $error); +% errorpage($error); +%} else { +<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('op') =~ /^(register|transfer|revoke|renew)$/ or die "Illegal operation"; +my $operation = $1; +#my($query) = $cgi->keywords; +#$query =~ /^(\d+)$/; +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum = $1; +my $svc_domain = qsearchs({ + 'select' => 'svc_domain.*', + 'table' => 'svc_domain', + 'addl_from' => ' LEFT JOIN cust_svc USING ( svcnum ) '. + ' LEFT JOIN cust_pkg USING ( pkgnum ) '. + ' LEFT JOIN cust_main USING ( custnum ) ', + 'hashref' => {'svcnum'=>$svcnum}, + 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql, +}); +die "Unknown svcnum" unless $svc_domain; + +my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum}); +my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } ); +die "Unknown svcpart" unless $part_svc; + +my $error = ''; + +my @exports = $part_svc->part_export(); + +my $registrar; +my $export; + +# Find the first export that does domain registration +foreach (@exports) { + $export = $_ if $_->can('registrar'); +} + +my $period = 1; # Current OpenSRS export can only handle 1 year registrations + +# If we have a domain registration export, get the registrar object +if ($export) { + if ($operation eq 'register') { + $error = $export->register( $svc_domain, $period ); + } elsif ($operation eq 'transfer') { + $error = $export->transfer( $svc_domain ); + } elsif ($operation eq 'revoke') { + $error = $export->revoke( $svc_domain ); + } elsif ($operation eq 'renew') { + $cgi->param('period') =~ /^(\d+)$/ or die "Illegal renewal period!"; + $period = $1; + $error = $export->renew( $svc_domain, $period ); + } +} + +</%init> diff --git a/httemplate/edit/process/elements/ApplicationCommon.html b/httemplate/edit/process/elements/ApplicationCommon.html new file mode 100644 index 000000000..c7bdd3ea2 --- /dev/null +++ b/httemplate/edit/process/elements/ApplicationCommon.html @@ -0,0 +1,103 @@ +<%doc> + +Examples: + + #cust_bill_pay + include('elements/ApplicationCommon.html', + 'error_redirect' => 'cust_bill_pay.cgi', + 'src_table' => 'cust_pay', + 'src_thing' => 'payment', + 'link_table' => 'cust_bill_pay', + ) + + #cust_credit_bill + include('elements/ApplicationCommon.html', + 'error_redirect' => 'cust_credit_bill.cgi', + 'src_table' => 'cust_credit', + 'src_thing' => 'credit', + 'link_table' => 'cust_credit_bill', + ) + +</%doc> +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). $opt{error_redirect}. '?'. $cgi->query_string ) %> +%} else { +<% header("$src_thing application$to sucessful") %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY> + </HTML> +% } +<%init> + +my %opt = @_; + +my $error = ''; + +my $src_thing = ucfirst($opt{'src_thing'}); +my $src_table = $opt{'src_table'}; +my $src_pkey = dbdef->table($src_table)->primary_key; + +my $to = $opt{'link_table'} =~ /refund/ ? ' to Refund' : ''; + +$cgi->param($src_pkey) =~ /^(\d+)$/ or die "Illegal $src_pkey!"; +my $src_pkeyvalue = $1; + +my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } ) + or die "No such $src_pkey: $src_pkeyvalue"; + +my $cust_main = qsearchs('cust_main', { 'custnum' => $src->custnum } ) + or die "Bogus $src_thing: not attached to customer"; + +my $custnum = $cust_main->custnum; + +my @subnames = grep { /.+/ } map { /^subnum(\d+)$/ ? $1 : '' } $cgi->param; +my @subitems = map { [ $cgi->param("subnum$_"), $cgi->param("subamount$_"), $cgi->param("taxXlocationnum$_") ] } + @subnames; +{ local $^W = 0; @subitems = grep { $_->[1] + 0 } @subitems; } + +my %options = (); +$options{subitems} = \@subitems if scalar(@subitems); + +my $oldAutoCommit = $FS::UID::AutoCommit; +local $FS::UID::AutoCommit = 0; +my $dbh = dbh; + +my $new; +# $new = new FS::cust_refund ( { +# 'reason' => 'Refunding payment', #enter reason in UI +# 'refund' => $cgi->param('amount'), +# 'payby' => 'BILL', +# #'_date' => $cgi->param('_date'), +# 'payinfo' => 'Cash', #enter payinfo in UI +# 'paynum' => $paynum, +# } ); +#} else { + + if ($src->amount != $cgi->param('src_amount')) { + $src->amount($cgi->param('src_amount')); + $error = $src->replace; + } + + my $class = 'FS::'. $opt{link_table}; + + $new = $class->new( { + map { + $_ => scalar($cgi->param($_)); + } fields($opt{link_table}) + } ); + +#} + + +$options{manual} = 1; +$error ||= $new->insert( %options ); + +if ($error) { + $dbh->rollback if $oldAutoCommit; +} else { + $dbh->commit or die $dbh->errstr if $oldAutoCommit; +} +</%init> diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html new file mode 100644 index 000000000..53419cde0 --- /dev/null +++ b/httemplate/edit/process/elements/process.html @@ -0,0 +1,321 @@ +<%doc> + +Example: + + include( 'elements/process.html', + + ### + # required + ### + + 'table' => 'tablename', + + #? 'primary_key' => #required when the dbdef doesn't know...??? + #? 'fields' => [] #"" + + ### + # optional + ### + + 'viewall_dir' => '', #'search' or 'browse', defaults to 'search' + 'viewall_ext' => 'html', #'cgi' or 'html', defaults to 'html' + OR + 'redirect' => 'view/table.cgi?', # value of primary key is appended + # (string or coderef returning a string) + OR + 'popup_reload' => 'Momentary success message', #will reload parent window + + 'error_redirect' => popurl(2).'edit/table.cgi?', #query string appended + + 'edit_ext' => 'html', #defaults to 'html', you might want 'cgi' while the + #naming is still inconsistent + + 'copy_on_empty' => [ 'old_field_name', 'another_old_field', ... ], + + 'clear_on_error' => [ 'form_field1', 'form_field2', ... ], + + #pass an arrayref of hashrefs for multiple m2ms or m2names + #be certain you incorporate m2m_Common if you see error: param + + 'process_m2m' => { 'link_table' => 'link_table_name', + 'target_table' => 'target_table_name', + #optional (see m2m_Common::process_m2m), if not specified + # all CGI params will be passed) + 'params' => + }, + 'process_m2name' => { 'link_table' => 'link_table_name', + 'link_static' => { 'column' => 'value' }, + 'num_col' => 'column', #if column name is different in + #link_table than source_table + 'name_col' => 'name_column', + 'names_list' => [ 'list', 'names' ], + + 'param_style' => 'link_table.value checkboxes', + #or# + 'param_style' => 'name_colN values', + + + }, + 'process_o2m' => { 'table' => table_name', + 'num_col' => 'column', #if column name is different in + #link_table than source_table + }, + + #checks CGI params and whatever else before much else runs + #return an error string or empty for no error + 'precheck_callback' => sub { my( $cgi ) = @_; }, + + #after everything's inserted + 'noerror_callback' => sub { my( $cgi, $object ) = @_; }, + + #supplies arguments to insert() and replace() + # for use with tables that are FS::option_Common (among other things) + 'args_callback' => sub { my( $cgi, $object ) = @_; }, + + 'debug' => 1, #turns on debugging output + + #agent virtualization + 'agent_virt' => 1, + 'agent_null_right' => 'Access Right Name', + + ) + +</%doc> +%if ( $error ) { +% +% my $edit_ext = $opt{'edit_ext'} || 'html'; +% my $url = $opt{'error_redirect'} || popurl(2)."$table.$edit_ext"; +% if ( length($cgi->query_string) > 1920 ) { #stupid IE 2083 URL limit +% +% my $session = int(rand(4294967296)); #XXX +% my $pref = new FS::access_user_pref({ +% 'usernum' => $FS::CurrentUser::CurrentUser->usernum, +% 'prefname' => "redirect$session", +% 'prefvalue' => $cgi->query_string, +% 'expiration' => time + 3600, #1h? 1m? +% }); +% my $pref_error = $pref->insert; +% if ( $pref_error ) { +% die "FATAL: couldn't even set redirect cookie: $pref_error". +% " attempting to set redirect$session to ". $cgi->query_string."\n"; +% } +% +<% $cgi->redirect("$url?redirect=$session") %> +% +% } else { +% +<% $cgi->redirect("$url?". $cgi->query_string ) %> +% +% } +% +% #different ways of handling success +% +%} elsif ( $opt{'popup_reload'} ) { + + <% include('/elements/header-popup.html', $opt{'popup_reload'} ) %> + + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + + </BODY> + </HTML> + +%} else { +% +% $opt{'redirect'} = &{$opt{'redirect'}}($cgi, $new) +% if ref($opt{'redirect'}) eq 'CODE'; +% +% if ( $opt{'redirect'} ) { +% +<% $cgi->redirect( $opt{'redirect'}. $pkeyvalue ) %> +% +% } else { +% +% my $ext = $opt{'viewall_ext'} || 'html'; +% +<% $cgi->redirect( popurl(3). ($opt{viewall_dir}||'search'). "/$table.$ext" ) %> +% +% } +% +%} +% +<%init> + +my $me = 'process.html:'; + +my(%opt) = @_; + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $error = ''; +if ( $opt{'precheck_callback'} ) { + $error = &{ $opt{'precheck_callback'} }( $cgi ); +} + +#false laziness w/edit.html +my $table = $opt{'table'}; +my $class = "FS::$table"; +my $pkey = dbdef->table($table)->primary_key; #? $opt{'primary_key'} || +my $fields = $opt{'fields'} + #|| [ grep { $_ ne $pkey } dbdef->table($table)->columns ]; + || [ fields($table) ]; + +my $pkeyvalue = $cgi->param($pkey); + +my $old = ''; +if ( $pkeyvalue ) { + $old = qsearchs({ + 'table' => $table, + 'hashref' => { $pkey => $pkeyvalue }, + 'extra_sql' => ( $opt{'agent_virt'} + ? ' AND '. $curuser->agentnums_sql( + 'null_right' => $opt{'agent_null_right'} + ) + : '' + ), + }); +} + +my %hash = + map { my @entry = ( $_ => scalar($cgi->param($_)) ); + $opt{'value_callback'} ? ( $_ => &{ $opt{'value_callback'} }( @entry )) + : ( @entry ) + } @$fields; + +my $new = $class->new( \%hash ); + +if ($old && exists($opt{'copy_on_empty'})) { + foreach my $field (@{$opt{'copy_on_empty'}}) { + $new->set($field, $old->get($field)) + unless scalar($cgi->param($field)); + } +} + +if ( $opt{'agent_virt'} ) { + + if ( ! $new->agentnum + && ( ! $opt{'agent_null_right'} + || ! $curuser->access_right($opt{'agent_null_right'}) + ) + ) + { + + $error ||= 'Select an agent'; + + } else { + + die "illegal agentnum" + unless $curuser->agentnums_href->{$new->agentnum} + or $curuser->access_right('View customers of all agents') + or $opt{'agent_null_right'} + && ! $new->agentnum + && $curuser->access_right($opt{'agent_null_right'}); + + } + +} + +$error ||= $new->check; + +my @args = (); +if ( !$error && $opt{'args_callback'} ) { + @args = &{ $opt{'args_callback'} }( $cgi, $new ); +} + +if ( !$error && $opt{'debug'} ) { + warn "$me updating record in $table table using $class class\n"; + warn Dumper(\%hash); + warn "with args: \n". Dumper(\@args) if @args; +} + +if ( !$error ) { + if ( $pkeyvalue ) { + $error = $new->replace($old, @args); + } else { + $error = $new->insert(@args); + $pkeyvalue = $new->getfield($pkey); + } +} + +if ( !$error && $opt{'process_m2m'} ) { + + my @process_m2m = ref($opt{'process_m2m'}) eq 'ARRAY' + ? @{ $opt{'process_m2m'} } + : ( $opt{'process_m2m'} ); + + foreach my $process_m2m (@process_m2m) { + + $process_m2m->{'params'} ||= scalar($cgi->Vars); + + warn "$me processing m2m:\n". Dumper( %$process_m2m ) + if $opt{'debug'}; + + $error = $new->process_m2m( %$process_m2m ); + } + +} + +if ( !$error && $opt{'process_m2name'} ) { + + my @process_m2name = ref($opt{'process_m2name'}) eq 'ARRAY' + ? @{ $opt{'process_m2name'} } + : ( $opt{'process_m2name'} ); + + + foreach my $process_m2name (@process_m2name) { + + if ( $opt{'debug'} ) { + warn "$me processing m2name:\n". Dumper( %{ $process_m2name }, + 'params' => scalar($cgi->Vars), + ); + } + + $error = $new->process_m2name( %{ $process_m2name }, + 'params' => scalar($cgi->Vars), + ); + } + +} + +if ( !$error && $opt{'process_o2m'} ) { + + my @process_o2m = ref($opt{'process_o2m'}) eq 'ARRAY' + ? @{ $opt{'process_o2m'} } + : ( $opt{'process_o2m'} ); + + + foreach my $process_o2m (@process_o2m) { + + if ( $opt{'debug'} ) { + warn "$me processing o2m:\n". Dumper( %{ $process_o2m }, + 'params' => scalar($cgi->Vars), + ); + } + + $error = $new->process_o2m( %{ $process_o2m }, + 'params' => scalar($cgi->Vars), + ); + } + +} + + +if ( $error ) { + + $cgi->param('error', $error); + if ( $opt{'clear_on_error'} && scalar(@{$opt{'clear_on_error'}}) ) { + foreach my $field (@{$opt{'clear_on_error'}}) { + $cgi->param($field, '') + } + } + +} else { + + if ( $opt{'noerror_callback'} ) { + &{ $opt{'noerror_callback'} }( $cgi, $new ); + } + +} + +</%init> diff --git a/httemplate/edit/process/elements/svc_Common.html b/httemplate/edit/process/elements/svc_Common.html new file mode 100644 index 000000000..5a8afbd6c --- /dev/null +++ b/httemplate/edit/process/elements/svc_Common.html @@ -0,0 +1,14 @@ +<% include( 'process.html', + 'edit_ext' => 'cgi', + 'redirect' => popurl(3)."view/$table.cgi?", + %opt, + ) +%> +<%init> + +my %opt = @_; +my $table = $opt{'table'}; +$opt{'fields'} ||= [ fields($table) ]; +push @{ $opt{'fields'} }, qw( pkgnum svcpart ); + +</%init> diff --git a/httemplate/edit/process/generic.cgi b/httemplate/edit/process/generic.cgi new file mode 100644 index 000000000..642876386 --- /dev/null +++ b/httemplate/edit/process/generic.cgi @@ -0,0 +1,77 @@ +%if($error) { +% $cgi->param('error', $error); +<% $cgi->redirect($redirect_error . '?' . $cgi->query_string) %> +%} else { +<% $cgi->redirect($redirect_ok) %> +%} +<%doc> + +See elements/process.html, newer and somewhat along the same lines, +though it still makes you setup a process file for the table. +Perhaps safer, perhaps more of a pain in the ass. + +In any case, this is probably pretty deprecated; it is only used by +part_virtual_field.cgi, and so its ACL is hardcoded to 'Configuration'. + +Welcome to generic.cgi. + +This script provides a generic edit/process/ backend for simple table +editing. All it knows how to do is take the values entered into +the script and insert them into the table specified by $cgi->param('table'). +If there's an existing record with the same primary key, it will be +replaced. (Deletion will be added in the future.) + +Special cgi params for this script: +table: the name of the table to be edited. The script will die horribly + if it can't find the table. +redirect_ok: URL to be displayed after a successful edit. The value of + the record's primary key will be passed as a keyword. + Defaults to (freeside root)/view/$table.cgi. +redirect_error: URL to be displayed if there's an error. The original + query string, plus the error message, will be passed. + Defaults to $cgi->referer() (i.e. go back where you + came from). + +</%doc> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $error; +my $p2 = popurl(2); +my $p3 = popurl(3); +my $table = $cgi->param('table'); +my $dbdef = dbdef or die "Cannot fetch dbdef!"; + +my $dbdef_table = $dbdef->table($table) or die "Cannot fetch schema for $table"; + +my $pkey = $dbdef_table->primary_key or die "Cannot fetch pkey for $table"; +my $pkey_val = $cgi->param($pkey); + + +#warn "new FS::Record ( $table, (hashref) )"; +my $new = FS::Record::new ( "FS::$table", { + map { $_, scalar($cgi->param($_)) } fields($table) +} ); + +#warn 'created $new of class '.ref($new); + +if($pkey_val and (my $old = qsearchs($table, { $pkey, $pkey_val} ))) { + # edit + $error = $new->replace($old); +} else { + #add + $error = $new->insert; + $pkey_val = $new->getfield($pkey); + # New records usually don't have their primary keys set until after + # they've been checked/inserted, so grab the new $pkey_val so we can + # redirect to it. +} + +my $redirect_ok = (($cgi->param('redirect_ok')) ? + $cgi->param('redirect_ok') : $p3."browse/generic.cgi?$table"); +my $redirect_error = (($cgi->param('redirect_error')) ? + $cgi->param('redirect_error') : $cgi->referer()); + +</%init> diff --git a/httemplate/edit/process/inventory_class.html b/httemplate/edit/process/inventory_class.html new file mode 100644 index 000000000..dbf978e72 --- /dev/null +++ b/httemplate/edit/process/inventory_class.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'inventory_class', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/invoice_logo.html b/httemplate/edit/process/invoice_logo.html new file mode 100644 index 000000000..524d32542 --- /dev/null +++ b/httemplate/edit/process/invoice_logo.html @@ -0,0 +1,25 @@ +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Configuration'); + +my $conf = new FS::Conf; + +$cgi->param('type') =~ /^(png|eps)$/ or die "illegal type"; +my $type = $1; + +$cgi->param('name') =~ /^([^\.\/]*)$/ or die "illegal name"; +my $tname = my $name = $1; +$tname = "_$tname" if length($tname); + +$cgi->param('preview_session') =~ /^(\w*)$/ or die "illegal preview_session"; +my $session = $1; +my $data = decode_base64( $curuser->option("logo_preview$session") ); + +$conf->set_binary("logo$name.$type", $data); + +$cgi->redirect(popurl(3). "edit/invoice_logo.html?type=$type;name=$name;msg=Logo%20changed"); + +</%init> diff --git a/httemplate/edit/process/invoice_template.html b/httemplate/edit/process/invoice_template.html new file mode 100644 index 000000000..6c9371ad1 --- /dev/null +++ b/httemplate/edit/process/invoice_template.html @@ -0,0 +1,15 @@ +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; + +my $confname = $cgi->param('confname'); +my $value = $cgi->param('value'); + +$conf->set($confname, $value); + +$cgi->redirect(popurl(3). 'browse/invoice_template.html'); + +</%init> diff --git a/httemplate/edit/process/mailinglistmember.html b/httemplate/edit/process/mailinglistmember.html new file mode 100644 index 000000000..f1842b8ef --- /dev/null +++ b/httemplate/edit/process/mailinglistmember.html @@ -0,0 +1,6 @@ +<% include( 'elements/process.html', + 'table' => 'mailinglistmember', + 'popup_reload' => 'Member added', + ) +%> +%#XXX ACL diff --git a/httemplate/edit/process/msg_template.html b/httemplate/edit/process/msg_template.html new file mode 100644 index 000000000..70d451b72 --- /dev/null +++ b/httemplate/edit/process/msg_template.html @@ -0,0 +1,13 @@ +<% include( 'elements/process.html', + 'table' => 'msg_template', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit templates') + || $FS::CurrentUser::CurrentUser->access_right('Edit global templates') + || $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/msgcat.cgi b/httemplate/edit/process/msgcat.cgi new file mode 100644 index 000000000..7175fa2b3 --- /dev/null +++ b/httemplate/edit/process/msgcat.cgi @@ -0,0 +1,22 @@ +%if ( $error ) { +% $cgi->param('error',$error); +<% $cgi->redirect($p. "msgcat.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/msgcat.cgi") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $error; +foreach my $param ( grep { /^\d+$/ } $cgi->param ) { + my $old = qsearchs('msgcat', { msgnum=>$param } ); + next if $old->msg eq $cgi->param($param); #no need to update identical records + my $new = new FS::msgcat { $old->hash }; + $new->msg($cgi->param($param)); + $error = $new->replace($old); + last if $error; +} + +</%init> diff --git a/httemplate/edit/process/part_bill_event.cgi b/httemplate/edit/process/part_bill_event.cgi new file mode 100755 index 000000000..eb0529bb8 --- /dev/null +++ b/httemplate/edit/process/part_bill_event.cgi @@ -0,0 +1,106 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "part_bill_event.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3)."browse/part_bill_event.cgi") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $eventpart = $cgi->param('eventpart'); + +my $old = qsearchs('part_bill_event',{'eventpart'=>$eventpart}) if $eventpart; + +#s/days/seconds/ +$cgi->param('seconds', int( $cgi->param('days') * 86400 ) ); + +my $error; +if ( ! $cgi->param('plan_weight_eventcode') ) { + $error = "Must select an action"; +} else { + + $cgi->param('plan_weight_eventcode') =~ /^([\w\-]+):(\d+):(.*)$/s + or die "illegal plan_weight_eventcode:". + $cgi->param('plan_weight_eventcode'); + $cgi->param('plan', $1); + $cgi->param('weight', $2); + my $eventcode = $3; + my $plandata = ''; + + my $rnum; + my $rtype; + my $reasonm; + my $class = ''; + $class='c' if ($eventcode =~ /cancel/); + $class='s' if ($eventcode =~ /suspend/); + if ($class) { + $cgi->param("${class}reason") =~ /^(-?\d+)$/ + or $error = "Invalid ${class}reason"; + $rnum = $1; + if ($rnum == -1) { + $cgi->param("new${class}reasonT") =~ /^(\d+)$/ + or $error = "Invalid new${class}reasonT"; + $rtype = $1; + $cgi->param("new${class}reason") =~ /^([\s\w]+)$/ + or $error = "Invalid new${class}reason"; + $reasonm = $1; + } + } + + if ($rnum == -1 && !$error) { + my $reason = new FS::reason ({ 'reason' => $reasonm, + 'reason_type' => $rtype, + }); + $error = $reason->insert; + unless ($error) { + $rnum = $reason->reasonnum; + $cgi->param("${class}reason", $rnum); + $cgi->param("new${class}reason", ''); + $cgi->param("new${class}reasonT", ''); + } + } + + while ( $eventcode =~ /%%%(\w+)%%%/ ) { + my $field = $1; + my $value = join(', ', $cgi->param($field) ); + $cgi->param($field, $value); #in case it errors out + $eventcode =~ s/%%%$field%%%/$value/; + $plandata .= "$field $value\n"; + } + $cgi->param('eventcode', $eventcode); + $cgi->param('plandata', $plandata); + + unless($error) { + + if ( $eventpart ) { + + my $new = new FS::part_bill_event ( { + map { $_ => scalar($cgi->param($_)) } + fields('part_bill_event'), + } ); + $new->setfield('reason' => $rnum); + $error = $new->replace($old); + + } else { + + foreach my $payby ( $cgi->param('payby') ) { + my $new = new FS::part_bill_event ( { + map { $_ => scalar($cgi->param($_)) } + grep { $_ ne 'payby' } + fields('part_bill_event') + } ); + $new->setfield('payby' => $payby); + $new->setfield('reason' => $rnum ); + $error = $new->insert; + last if $error; + } + + } + + } + +} + +</%init> diff --git a/httemplate/edit/process/part_device.html b/httemplate/edit/process/part_device.html new file mode 100644 index 000000000..399991fc8 --- /dev/null +++ b/httemplate/edit/process/part_device.html @@ -0,0 +1,15 @@ +<% include( 'elements/process.html', + 'table' => 'part_device', + 'viewall_dir' => 'browse', + 'process_m2m' => { + 'link_table' => 'export_device', + 'target_table' => 'part_export', + }, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/part_event.html b/httemplate/edit/process/part_event.html new file mode 100644 index 000000000..428025fd1 --- /dev/null +++ b/httemplate/edit/process/part_event.html @@ -0,0 +1,86 @@ +<% include( 'elements/process.html', + #'debug' => 1, + 'table' => 'part_event', + 'viewall_dir' => 'browse', + 'process_m2name' => + { + 'link_table' => 'part_event_condition', + 'num_col' => 'eventpart', + 'name_col' => 'conditionname', + 'names_list' => [ FS::part_event_condition->all_conditionnames() ], + 'param_style' => 'name_colN values', + 'args_callback' => sub { # FS/FS/m2name_Common.pm + my( $object, $prefix, $params, $listref ) = @_; + #warn "$object $prefix $params $listref\n"; + + my $cond = $object->conditionname; + + my %option_fields = $object->option_fields; + + push @$listref, map { + my $field = $_; + + my $cgi_field = "$prefix$cond.$field"; + + my $value = $params->{$cgi_field}; + + my $info = $option_fields{$_}; + $info = { label=>$info, type=>'text' } + unless ref($info); + + if ( $info->{'type'} =~ + /^(select|checkbox)-?multiple$/ + or $info->{'type'} =~ /^select/ + && $info->{'multiple'} + ) + { + #special processing for compound fields + $value = { map { $_ => 1 } + split(/\0/, $value) + }; + } elsif ( $info->{'type'} eq 'freq' ) { + $value .= $params->{$cgi_field.'_units'}; + } + + #warn "value of $cgi_field is $value\n"; + + ( $field => $value ); + } + keys %option_fields; + }, + }, + + 'args_callback' => sub { + + my( $cgi, $object ) = @_; + + my $prefix = $object->action.'.'; + + map { my $option = $_; + #my $value = scalar( $cgi->param( "$prefix$option" ) ); + my $value = join(',', $cgi->param( "$prefix$option" ) ); + + if ( $option eq 'reasonnum' && $value == -1 ) { + $value = { + 'typenum' => scalar( $cgi->param( "new$prefix${option}T" ) ), + 'reason' => scalar( $cgi->param( "new$prefix${option}" ) ), + }; + } + + ( $option => $value ); + } + @{ $object->option_fields_listref }; + + }, + + 'agent_virt' => 1, + 'agent_null_right' => 'Edit global billing events', +) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit billing events') + || $FS::CurrentUser::CurrentUser->access_right('Edit global billing events'); + +</%init> diff --git a/httemplate/edit/process/part_export.cgi b/httemplate/edit/process/part_export.cgi new file mode 100644 index 000000000..209419f0b --- /dev/null +++ b/httemplate/edit/process/part_export.cgi @@ -0,0 +1,42 @@ +%if ( $error ) { +% $cgi->param('error', $error ); +<% $cgi->redirect(popurl(2). "part_export.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/part_export.cgi") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $exportnum = $cgi->param('exportnum'); + +my $old = qsearchs('part_export', { 'exportnum'=>$exportnum } ) if $exportnum; + +#fixup options +#warn join('-', split(',',$cgi->param('options'))); +my %options = map { + my @values = $cgi->param($_); + my $value = scalar(@values) > 1 ? join (' ', @values) : $values[0]; + $value =~ s/\r\n/\n/g; #browsers? (textarea) + $_ => $value; +} split(',', $cgi->param('options')); + +my $new = new FS::part_export ( { + map { + $_, scalar($cgi->param($_)); + } fields('part_export') +} ); + +my $error; +if ( $exportnum ) { + #warn $old; + #warn $exportnum; + #warn $new->machine; + $error = $new->replace($old,\%options); +} else { + $error = $new->insert(\%options); +# $exportnum = $new->exportnum; +} + +</%init> diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi new file mode 100755 index 000000000..c0febf828 --- /dev/null +++ b/httemplate/edit/process/part_pkg.cgi @@ -0,0 +1,222 @@ +<% include( 'elements/process.html', + #'debug' => 1, + 'table' => 'part_pkg', + 'agent_virt' => 1, + 'agent_null_right' => \@agent_null_right, + 'redirect' => $redirect_callback, + 'viewall_dir' => 'browse', + 'viewall_ext' => 'cgi', + 'edit_ext' => 'cgi', + 'precheck_callback' => $precheck_callback, + 'args_callback' => $args_callback, + 'process_m2m' => \@process_m2m, + ) +%> +<%init> + +my $customizing = ( ! $cgi->param('pkgpart') && $cgi->param('pkgnum') ); + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $edit_global = 'Edit global package definitions'; +my $customize = 'Customize customer package'; + +die "access denied" + unless $curuser->access_right('Edit package definitions') + || $curuser->access_right($edit_global) + || ( $customizing && $curuser->access_right($customize) ); + +my @agent_null_right = ( $edit_global ); +push @agent_null_right, $customize if $customizing; + + +my $precheck_callback = sub { + my( $cgi ) = @_; + + my $conf = new FS::Conf; + + foreach (qw( setuptax recurtax disabled )) { + $cgi->param($_, '') unless defined $cgi->param($_); + } + + return 'Must select a tax class' + if $cgi->param('taxclass') eq '(select)'; + + my @agents = (); + foreach ($cgi->param('agent_type')) { + /^(\d+)$/; + push @agents, $1 if $1; + } + return "At least one agent type must be specified." + unless scalar(@agents) + || ( $cgi->param('clone') && $cgi->param('clone') =~ /^\d+$/ ) + || ( !$cgi->param('pkgpart') && $conf->exists('agent-defaultpkg') ) + || $cgi->param('disabled') + || $cgi->param('agentnum'); + + return ''; + +}; + +my $custnum = ''; + +my $args_callback = sub { + my( $cgi, $new ) = @_; + + my @args = ( 'primary_svc' => scalar($cgi->param('pkg_svc_primary')) ); + + ## + #options + ## + + $cgi->param('plan') =~ /^(\w+)$/ or die 'unparsable plan'; + my $plan = $1; + + tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() }; + my $href = $plans{$plan}->{'fields'}; + + my $error = ''; + my $options = $cgi->param($plan."__OPTIONS"); + my @options = split(',', $options); + my %options = + map { my $optionname = $_; + my $param = $plan."__$optionname"; + my $parser = exists($href->{$optionname}{parse}) + ? $href->{$optionname}{parse} + : sub { shift }; + my $value = join(', ', &$parser($cgi->param($param))); + my $check = $href->{$optionname}{check}; + if ( $check && ! &$check($value) ) { + $value = join(', ', $cgi->param($param)); + $error ||= "Illegal ". + ($href->{$optionname}{name}||$optionname). ": $value"; + } + ( $optionname => $value ); + } + grep { $_ !~ /^report_option_/ } + @options; + + foreach ( split(',', $cgi->param('taxproductnums') ) ) { + my $value = $cgi->param("taxproductnum_$_"); + $error ||= "Illegal taxproductnum_$_: $value" + unless ( $value =~ /^\d*$/ ); + $options{"usage_taxproductnum_$_"} = $value; + } + + foreach ( $cgi->param('report_option') ) { + $error ||= "Illegal optional report class: $_" unless ( $_ =~ /^\d*$/ ); + $options{"report_option_$_"} = 1; + } + + $options{$_} = scalar( $cgi->param($_) ) + for (qw( setup_fee recur_fee )); + + push @args, 'options' => \%options; + + ### + #pkg_svc + ### + + my %pkg_svc = map { $_ => scalar($cgi->param("pkg_svc$_")) } + map { $_->svcpart } + qsearch('part_svc', {} ); + + push @args, 'pkg_svc' => \%pkg_svc; + + ### + # cust_pkg and custnum_ref (inserts only) + ### + unless ( $cgi->param('pkgpart') ) { + push @args, 'cust_pkg' => scalar($cgi->param('pkgnum')), + 'custnum_ref' => \$custnum; + } + + #warn "args: ".join('/', @args). "\n"; + + @args; + +}; + +my $redirect_callback = sub { + #my( $cgi, $new ) = @_; + return '' unless $custnum; + my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/ + ? '' + : ';show=packages'; + #my $frag = "cust_pkg$pkgnum"; #hack for IE ignoring real #fragment + + #can we link back to the specific customized package? it would be nice... + popurl(3). "view/cust_main.cgi?custnum=$custnum$show;dummy="; +}; + +#these should probably move to @args above and be processed by part_pkg.pm... + +$cgi->param('tax_override') =~ /^([\d,]+)$/; +my (@tax_overrides) = (grep "$_", split (",", $1)); + +my @process_m2m = ( + { + 'link_table' => 'part_pkg_taxoverride', + 'target_table' => 'tax_class', + 'params' => \@tax_overrides, + }, + { 'link_table' => 'part_pkg_link', + 'target_table' => 'part_pkg', + 'base_field' => 'src_pkgpart', + 'target_field' => 'dst_pkgpart', + 'hashref' => { 'link_type' => 'svc', 'hidden' => '' }, + 'params' => [ map $cgi->param($_), + grep /^svc_dst_pkgpart/, $cgi->param + ], + }, + map { + my $hidden = $_; + { 'link_table' => 'part_pkg_link', + 'target_table' => 'part_pkg', + 'base_field' => 'src_pkgpart', + 'target_field' => 'dst_pkgpart', + 'hashref' => { 'link_type' => 'bill', 'hidden' => $hidden }, + 'params' => [ map { $cgi->param($_) } + grep { my $param = "bill_dst_pkgpart__hidden"; + my $digit = ''; + (($digit) = /^bill_dst_pkgpart(\d+)/ ) && + $cgi->param("$param$digit") eq $hidden; + } + $cgi->param + ], + }, + } ( '', 'Y' ), +); + +foreach my $override_class ($cgi->param) { + next unless $override_class =~ /^tax_override_(\w+)$/; + my $class = $1; + + my (@tax_overrides) = (grep "$_", split (",", $1)) + if $cgi->param($override_class) =~ /^([\d,]+)$/; + + push @process_m2m, { + 'link_table' => 'part_pkg_taxoverride', + 'target_table' => 'tax_class', + 'hashref' => { 'usage_class' => $class }, + 'params' => [ @tax_overrides ], + }; + +} + +my $conf = new FS::Conf; + +if ( $cgi->param('pkgpart') || ! $conf->exists('agent_defaultpkg') ) { + my @agents = (); + foreach ($cgi->param('agent_type')) { + /^(\d+)$/; + push @agents, $1 if $1; + } + push @process_m2m, { + 'link_table' => 'type_pkgs', + 'target_table' => 'agent_type', + 'params' => \@agents, + }; +} + +</%init> diff --git a/httemplate/edit/process/part_pkg_report_option.html b/httemplate/edit/process/part_pkg_report_option.html new file mode 100644 index 000000000..052aabd72 --- /dev/null +++ b/httemplate/edit/process/part_pkg_report_option.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'part_pkg_report_option', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/part_pkg_taxclass.html b/httemplate/edit/process/part_pkg_taxclass.html new file mode 100644 index 000000000..b37279fb3 --- /dev/null +++ b/httemplate/edit/process/part_pkg_taxclass.html @@ -0,0 +1,17 @@ +<% include( 'elements/process.html', + 'table' => 'part_pkg_taxclass', + 'redirect' => sub { + my( $cgi, $part_pkg_taxclass ) = @_; + + popurl(3). 'browse/cust_main_county.cgi?'. + 'taxclass='. uri_escape($part_pkg_taxclass->taxclass). + ';dummy='; + }, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/part_referral.html b/httemplate/edit/process/part_referral.html new file mode 100755 index 000000000..40cbc97bf --- /dev/null +++ b/httemplate/edit/process/part_referral.html @@ -0,0 +1,12 @@ +<% include( 'elements/process.html', + 'table' => 'part_referral', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit advertising sources') + || $FS::CurrentUser::CurrentUser->access_right('Edit global advertising sources'); + +</%init> diff --git a/httemplate/edit/process/part_svc.cgi b/httemplate/edit/process/part_svc.cgi new file mode 100755 index 000000000..65de3fc6c --- /dev/null +++ b/httemplate/edit/process/part_svc.cgi @@ -0,0 +1,9 @@ +<% $server->process %> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $server = new FS::UI::Web::JSRPC 'FS::part_svc::process', $cgi; + +</%init> diff --git a/httemplate/edit/process/part_tag.html b/httemplate/edit/process/part_tag.html new file mode 100644 index 000000000..077dc4cd9 --- /dev/null +++ b/httemplate/edit/process/part_tag.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'part_tag', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/payment_gateway.html b/httemplate/edit/process/payment_gateway.html new file mode 100644 index 000000000..812c988c5 --- /dev/null +++ b/httemplate/edit/process/payment_gateway.html @@ -0,0 +1,22 @@ +<% include( 'elements/process.html', + 'table' => 'payment_gateway', + 'viewall_dir' => 'browse', + 'args_callback' => $args_callback, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $args_callback = sub { + my ( $cgi, $new ) = @_; + + my @options = split(/\r?\n/, $cgi->param('gateway_options') ); + pop @options + if scalar(@options) % 2 && $options[-1] =~ /^\s*$/; + (@options) +}; + + +</%init> diff --git a/httemplate/edit/process/phone_device.html b/httemplate/edit/process/phone_device.html new file mode 100644 index 000000000..df9d5e793 --- /dev/null +++ b/httemplate/edit/process/phone_device.html @@ -0,0 +1,18 @@ +<% include( 'elements/process.html', + 'table' => 'phone_device', + 'redirect' => sub { + my( $cgi, $phone_device ) = @_; + popurl(3).'view/svc_phone.cgi?'. + 'svcnum='. $phone_device->svcnum. + ';devicenum='; + }, + ) +%> +<%init> + +# :/ 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/pkg_category.html b/httemplate/edit/process/pkg_category.html new file mode 100644 index 000000000..50cd5cb29 --- /dev/null +++ b/httemplate/edit/process/pkg_category.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'pkg_category', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/pkg_class.html b/httemplate/edit/process/pkg_class.html new file mode 100644 index 000000000..b196df3f7 --- /dev/null +++ b/httemplate/edit/process/pkg_class.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'pkg_class', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/prepay_credit.cgi b/httemplate/edit/process/prepay_credit.cgi new file mode 100644 index 000000000..a55522b86 --- /dev/null +++ b/httemplate/edit/process/prepay_credit.cgi @@ -0,0 +1,63 @@ +%unless ( ref($error) ) { +% $cgi->param('error', $error ); +<% $cgi->redirect(popurl(3). "edit/prepay_credit.cgi?". $cgi->query_string ) %> +% } else { + +<% include('/elements/header.html', "$num prepaid cards generated". + ( $agent ? ' for '.$agent->agent : '' ) + ) +%> + +<FONT SIZE="+1"> +% foreach my $card ( @$error ) { + + <code><% $card %></code> + - + <% $hashref->{amount} ? sprintf('$%.2f', $hashref->{amount} ) : '' %> + <% $hashref->{amount} && $hashref->{seconds} ? 'and' : '' %> + <% $hashref->{seconds} ? duration_exact($hashref->{seconds}) : '' %> + <% $hashref->{upbytes} ? FS::UI::bytecount::bytecount_unexact($hashref->{upbytes}) : '' %> + <% $hashref->{downbytes} ? FS::UI::bytecount::bytecount_unexact($hashref->{downbytes}) : '' %> + <% $hashref->{totalbytes} ? FS::UI::bytecount::bytecount_unexact($hashref->{totalbytes}) : '' %> + <br> +% } + +</FONT> + +<% include('/elements/footer.html') %> + +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $hashref = {}; + +my $agent = ''; +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { + $agent = qsearchs('agent', { 'agentnum' => $hashref->{agentnum}=$1 } ); +} + +my $error = ''; + +my $num = 0; +if ( $cgi->param('num') =~ /^\s*(\d+)\s*$/ ) { + $num = $1; +} else { + $error = 'Illegal number of prepaid cards: '. $cgi->param('num'); +} + +$hashref->{amount} = $cgi->param('amount'); +$hashref->{seconds} = $cgi->param('seconds') * $cgi->param('multiplier'); +$hashref->{upbytes} = $cgi->param('upbytes') * $cgi->param('upmultiplier'); +$hashref->{downbytes} = $cgi->param('downbytes') * $cgi->param('downmultiplier'); +$hashref->{totalbytes} = $cgi->param('totalbytes') * $cgi->param('totalmultiplier'); + +$error ||= FS::prepay_credit::generate( $num, + scalar($cgi->param('type')), + $cgi->param('length'), + $hashref + ); + +</%init> diff --git a/httemplate/edit/process/prospect_main.html b/httemplate/edit/process/prospect_main.html new file mode 100644 index 000000000..34d26421b --- /dev/null +++ b/httemplate/edit/process/prospect_main.html @@ -0,0 +1,34 @@ +<% include('elements/process.html', + 'table' => 'prospect_main', + 'args_callback' => $args_callback, + 'agent_virt' => 1, + 'process_o2m' => { + 'table' => 'contact', + 'fields' => [qw( first last title comment )], + }, + 'redirect' => popurl(3). 'view/prospect_main.html?', + ) +%> +<%init> + +my $args_callback = sub { + my( $cgi, $object ) = @_; + + $cgi->param('locationnum') =~ /^(\-?\d*)$/ + or die 'illegal locationnum '. $cgi->param('locationnum'); + my $locationnum = $1; + + return ( 'cust_location' => '' ) unless $locationnum; + + my $cust_location = new FS::cust_location { + map { $_ => scalar($cgi->param($_)) } + qw( address1 address2 city county state zip country ) + }; + + $cust_location->locationnum($locationnum) unless $locationnum == -1; + + ( 'cust_location' => $cust_location ); + +}; + +</%init> diff --git a/httemplate/edit/process/quick-charge.cgi b/httemplate/edit/process/quick-charge.cgi new file mode 100644 index 000000000..484f6fcc6 --- /dev/null +++ b/httemplate/edit/process/quick-charge.cgi @@ -0,0 +1,75 @@ +% if ( $error ) { +% $cgi->param('error', $error ); +<% $cgi->redirect($p.'quick-charge.html?'. $cgi->query_string) %> +% } else { +<% header("One-time charge added") %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('One-time charge'); + +my $error = ''; +my $conf = new FS::conf; +my $param = $cgi->Vars; + +my @description = (); +for ( my $row = 0; exists($param->{"description$row"}); $row++ ) { + push @description, $param->{"description$row"} + if ($param->{"description$row"} =~ /\S/); +} + +$param->{"custnum"} =~ /^(\d+)$/ + or $error .= "Illegal customer number " . $param->{"custnum"} . " "; +my $custnum = $1; + +$param->{"amount"} =~ /^\s*(\d*(?:\.?\d{1,2}))\s*$/ + or $error .= "Illegal amount " . $param->{"amount"} . " "; +my $amount = $1; + +my $quantity = 1; +if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) { + $quantity = $1; +} + +$param->{'tax_override'} =~ /^\s*([,\d]*)\s*$/ + or $error .= "Illegal tax override " . $param->{"tax_override"} . " "; +my $override = $1; + +if ( $param->{'taxclass'} eq '(select)' ) { + $error .= "Must select a tax class. " + unless ($conf->exists('enable_taxproducts') && + ( $override || $param->{taxproductnum} ) + ); + $cgi->param('taxclass', ''); +} + +unless ( $error ) { + my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) + or $error .= "Unknown customer number $custnum. "; + + $error ||= $cust_main->charge( { + 'amount' => $amount, + 'quantity' => $quantity, + 'bill_now' => scalar($cgi->param('bill_now')), + 'invoice_terms' => scalar($cgi->param('invoice_terms')), + 'start_date' => ( scalar($cgi->param('start_date')) + ? parse_datetime($cgi->param('start_date')) + : '' + ), + 'no_auto' => scalar($cgi->param('no_auto')), + 'pkg' => scalar($cgi->param('pkg')), + 'setuptax' => scalar($cgi->param('setuptax')), + 'taxclass' => scalar($cgi->param('taxclass')), + 'taxproductnum' => scalar($cgi->param('taxproductnum')), + 'tax_override' => $override, + 'classnum' => scalar($cgi->param('classnum')), + 'additional' => \@description, + } ); +} + +</%init> diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi new file mode 100644 index 000000000..2fde17f5e --- /dev/null +++ b/httemplate/edit/process/quick-cust_pkg.cgi @@ -0,0 +1,84 @@ +%if ($error) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(3). 'misc/order_pkg.html?'. $cgi->query_string ) %> +%} else { +% my $frag = "cust_pkg". $cust_pkg->pkgnum; +% my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/ +% ? '' +% : ';show=packages'; +<% header('Package ordered') %> + <SCRIPT TYPE="text/javascript"> + // XXX fancy ajax rebuild table at some point, but a page reload will do for now + + // XXX chop off trailing #target and replace... ? + window.top.location = '<% popurl(3). "view/cust_main.cgi?custnum=$custnum$show;fragment=$frag#$frag" %>'; + + </SCRIPT> + + </BODY></HTML> +%} +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Order customer package'); + +#untaint custnum (probably not necessary, searching for it is escape enough) +$cgi->param('custnum') =~ /^(\d+)$/ + or die 'illegal custnum '. $cgi->param('custnum'); +my $custnum = $1; +my $cust_main = qsearchs({ + 'table' => 'cust_main', + 'hashref' => { 'custnum' => $custnum }, + 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql, +}); +die 'unknown custnum' unless $cust_main; + +#probably not necessary, taken care of by cust_pkg::check +$cgi->param('pkgpart') =~ /^(\d+)$/ + or die 'illegal pkgpart '. $cgi->param('pkgpart'); +my $pkgpart = $1; +$cgi->param('refnum') =~ /^(\d*)$/ + or die 'illegal refnum '. $cgi->param('refnum'); +my $refnum = $1; +$cgi->param('locationnum') =~ /^(\-?\d*)$/ + or die 'illegal locationnum '. $cgi->param('locationnum'); +my $locationnum = $1; +$cgi->param('discountnum') =~ /^(\-?\d*)$/ + or die 'illegal discountnum '. $cgi->param('discountnum'); +my $discountnum = $1; + + +my $cust_pkg = new FS::cust_pkg { + 'custnum' => $custnum, + 'pkgpart' => $pkgpart, + 'start_date' => ( scalar($cgi->param('start_date')) + ? parse_datetime($cgi->param('start_date')) + : '' + ), + 'no_auto' => scalar($cgi->param('no_auto')), + 'refnum' => $refnum, + 'locationnum' => $locationnum, + 'discountnum' => $discountnum, + #for the create a new discount case + 'discountnum__type' => scalar($cgi->param('discountnum__type')), + 'discountnum_amount' => scalar($cgi->param('discountnum_amount')), + 'discountnum_percent' => scalar($cgi->param('discountnum_percent')), + 'discountnum_months' => scalar($cgi->param('discountnum_months')), + #'discountnum_disabled' => scalar($cgi->param('discountnum_disabled')), +}; + +my %opt = ( 'cust_pkg' => $cust_pkg ); + +if ( $locationnum == -1 ) { + my $cust_location = new FS::cust_location { + map { $_ => scalar($cgi->param($_)) } + qw( custnum address1 address2 city county state zip country ) + }; + $opt{'cust_location'} = $cust_location; +} + +my $error = $cust_main->order_pkg( %opt ); + +</%init> diff --git a/httemplate/edit/process/rate.cgi b/httemplate/edit/process/rate.cgi new file mode 100755 index 000000000..48d9322ca --- /dev/null +++ b/httemplate/edit/process/rate.cgi @@ -0,0 +1,9 @@ +<% $server->process %> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $server = new FS::UI::Web::JSRPC 'FS::rate::process', $cgi; + +</%init> diff --git a/httemplate/edit/process/rate_detail.html b/httemplate/edit/process/rate_detail.html new file mode 100644 index 000000000..6200d615f --- /dev/null +++ b/httemplate/edit/process/rate_detail.html @@ -0,0 +1,13 @@ +<% include( 'elements/process.html', + 'table' => 'rate_detail', + 'popup_reload' => 'Rate changed', #a popup "parent reload" for now + #someday change the individual element and go away instead + ) +%> +<%init> + +my $conf = new FS::Conf; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/rate_region.cgi b/httemplate/edit/process/rate_region.cgi new file mode 100755 index 000000000..d342e605a --- /dev/null +++ b/httemplate/edit/process/rate_region.cgi @@ -0,0 +1,48 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "rate_region.cgi?". $cgi->query_string ) %> +%} elsif ( $action eq 'Add' ) { +<% $cgi->redirect(popurl(2). "rate_region.cgi?$regionnum") %> +%} else { +<% $cgi->redirect(popurl(3). "browse/rate_region.html") %> +%} +<%init> + +my $conf = new FS::Conf; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $regionnum = $cgi->param('regionnum'); +my $action = $regionnum ? 'Edit' : 'Add'; + +my $old = qsearchs('rate_region', { 'regionnum' => $regionnum } ) if $regionnum; + +my $new = new FS::rate_region ( { + map { + $_, scalar($cgi->param($_)); + } ( fields('rate_region') ) +} ); + +my $countrycode = $cgi->param('countrycode'); +my @npa = split(/\s*,\s*/, $cgi->param('npa')); +$npa[0] = '' unless @npa; +my @rate_prefix = map { + #my($npa,$nxx) = split('-', $_); + s/\D//g; + new FS::rate_prefix { + 'countrycode' => $countrycode, + #'npa' => $npa, + #'nxx' => $nxx, + 'npa' => $_, + } + } @npa; +# we no longer process dest_detail records here +my $error; +if ( $regionnum ) { + $error = $new->replace($old, 'rate_prefix' => \@rate_prefix ); +} else { + $error = $new->insert( 'rate_prefix' => \@rate_prefix ); + $regionnum = $new->getfield('regionnum'); +} + +</%init> diff --git a/httemplate/edit/process/rate_time.cgi b/httemplate/edit/process/rate_time.cgi new file mode 100644 index 000000000..4fa78ce6d --- /dev/null +++ b/httemplate/edit/process/rate_time.cgi @@ -0,0 +1,94 @@ +% if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "rate_time.cgi?". $cgi->query_string ) %> +% } else { +<% $cgi->redirect(popurl(3). "browse/rate_time.html" ) %> +% } +%# dumper_html(\%vars, \%old_ints, {$rate_time->intervals}) %> +<%init> +my $error = ''; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); +my $ratetimenum = $cgi->param('ratetimenum'); +my $ratetimename = $cgi->param('ratetimename'); +my $delete = $cgi->param('delete'); + +my %vars = $cgi->Vars; +#warn Dumper(\%vars)."\n"; + +my $rate_time; + +my %old_ints; +if( $ratetimenum ) { + # editing + $rate_time = FS::rate_time->by_key($ratetimenum); + + # make a list of existing intervals that will be deleted + foreach ($rate_time->intervals) { + $old_ints{$_->intervalnum} = $_; + } + + if ( $delete ) { + $error = $rate_time->delete; + # intervals will be deleted later + } + elsif( $ratetimename ne $rate_time->ratetimename ) { + # the only case where the rate_time itself must be replaced + $rate_time->ratetimename($ratetimename); + $error = $rate_time->replace; + } +} +else { #!$ratetimenum, adding new + $rate_time = FS::rate_time->new({ ratetimename => $ratetimename }); + $error = $rate_time->insert; + $ratetimenum = $rate_time->ratetimenum; +} + +my @new_ints; +if(!$delete and !$error) { + foreach my $i (map { /^sd(\d+)$/ } keys(%vars)) { + my $stime = l2wtime(@vars{"sd$i", "sh$i", "sm$i", "sa$i"}); + my $etime = l2wtime(@vars{"ed$i", "eh$i", "em$i", "ea$i"}); + #warn "$i: $stime - $etime"; + next if !defined($stime) or !defined($etime) or $etime == $stime; + # try to avoid needlessly wiping and replacing intervals every + # time this is edited. + if( %old_ints ) { + my $this_int = qsearchs('rate_time_interval', + { ratetimenum => $ratetimenum, + stime => $stime, + etime => $etime, } ); + if($this_int) { + delete $old_ints{$this_int->intervalnum}; + #warn "not deleting $stime-$etime\n"; + next; #$i + } + } + push @new_ints, FS::rate_time_interval->new({ ratetimenum => $ratetimenum, + stime => $stime, + etime => $etime, } ); + } +} +if(!$error) { + foreach (values(%old_ints)) { + $error = $_->delete; + #warn "deleting ".$_->stime.' '.$_->etime."\n"; + last if $error; + } +} +if(!$error) { + # do this last to avoid overlap errors with deleted intervals + foreach (@new_ints) { + $error = $_->insert; + #warn "inserting $stime-$etime\n"; + last if $error; + } +} + +sub l2wtime { + my ($d, $h, $m, $a) = @_; + $h += 24*$d + 12*$a; + $m += 60*$h; + return 60*$m +} +</%init> diff --git a/httemplate/edit/process/reason.html b/httemplate/edit/process/reason.html new file mode 100644 index 000000000..cb79ed254 --- /dev/null +++ b/httemplate/edit/process/reason.html @@ -0,0 +1,12 @@ +<% include( 'elements/process.html', + 'table' => 'reason', + 'redirect' => popurl(3) . 'browse/reason.html?class=' . + $cgi->param('class') . '&', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/reason_type.html b/httemplate/edit/process/reason_type.html new file mode 100644 index 000000000..3172b27c4 --- /dev/null +++ b/httemplate/edit/process/reason_type.html @@ -0,0 +1,12 @@ +<% include( 'elements/process.html', + 'table' => 'reason_type', + 'redirect' => popurl(3) . 'browse/reason_type.html?class=' . + $cgi->param('class') . '&', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/reg_code.cgi b/httemplate/edit/process/reg_code.cgi new file mode 100644 index 000000000..035e10b90 --- /dev/null +++ b/httemplate/edit/process/reg_code.cgi @@ -0,0 +1,45 @@ +%unless ( ref($error) ) { +% $cgi->param('error'. $error ); +<% $cgi->redirect(popurl(3). "edit/reg_code.cgi?". $cgi->query_string ) %> +% } else { + +<% include("/elements/header.html","$num registration codes generated for ". $agent->agent, menubar( + 'View all agents' => popurl(3). 'browse/agent.cgi', +) ) %> + +<PRE><FONT SIZE="+1"> +% foreach my $code ( @$error ) { + <% $code %> +% } +</FONT></PRE> + +<% include('/elements/footer.html') %> +% } +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +$cgi->param('agentnum') =~ /^(\d+)$/ + or errorpage('illegal agentnum '. $cgi->param('agentnum')); +my $agentnum = $1; +my $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); + +my $error = ''; + +my $num = 0; +if ( $cgi->param('num') =~ /^\s*(\d+)\s*$/ ) { + $num = $1; +} else { + $error = 'Illegal number of codes: '. $cgi->param('num'); +} + +my @pkgparts = + map { /^pkgpart(.*)$/; $1 } + grep { $cgi->param($_) } + grep { /^pkgpart/ } + $cgi->param; + +$error ||= $agent->generate_reg_codes($num, \@pkgparts); + +</%init> diff --git a/httemplate/edit/process/router.cgi b/httemplate/edit/process/router.cgi new file mode 100644 index 000000000..3cbb8c58e --- /dev/null +++ b/httemplate/edit/process/router.cgi @@ -0,0 +1,20 @@ +<% include('elements/process.html', + 'table' => 'router', + 'viewall_dir' => 'browse', + 'viewall_ext' => 'cgi', + 'edit_ext' => 'cgi', + 'process_m2m' => { 'link_table' => 'part_svc_router', + 'target_table' => 'part_svc', + }, + 'agent_virt' => 1, + 'agent_null_right' => 'Broadband global configuration', + ) +%> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +</%init> diff --git a/httemplate/edit/process/svc_Common.html b/httemplate/edit/process/svc_Common.html new file mode 100644 index 000000000..cf5f01f71 --- /dev/null +++ b/httemplate/edit/process/svc_Common.html @@ -0,0 +1,16 @@ +<% include( 'elements/svc_Common.html', + 'table' => $table, + 'redirect' => popurl(3)."view/svc_Common.html?svcdb=$table;svcnum=", + 'error_redirect' => popurl(3)."edit/svc_Common.html?svcdb=$table;", + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unparsable svcdb"; +my $table = $1; +require "FS/$table.pm"; + +</%init> diff --git a/httemplate/edit/process/svc_acct.cgi b/httemplate/edit/process/svc_acct.cgi new file mode 100755 index 000000000..0b272b5b1 --- /dev/null +++ b/httemplate/edit/process/svc_acct.cgi @@ -0,0 +1,83 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "svc_acct.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "view/svc_acct.cgi?" . $svcnum ) %> +%} +<%init> +use CGI::Carp; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum = $1; + +my $old; +if ( $svcnum ) { + $old = qsearchs('svc_acct', { 'svcnum' => $svcnum } ) + or die "fatal: can't find account (svcnum $svcnum)!"; +} else { + $old = ''; +} + +#unmunge popnum +$cgi->param('popnum', (split(/:/, $cgi->param('popnum') ))[0] ); + +#unmunge usergroup +$cgi->param('usergroup', [ $cgi->param('radius_usergroup') ] ); + +#unmunge bytecounts +foreach (map { $_,$_."_threshold" } qw( upbytes downbytes totalbytes )) { + $cgi->param($_, FS::UI::bytecount::parse_bytecount($cgi->param($_)) ); +} + +#unmunge cgp_accessmodes (falze laziness-ish w/part_svc.pm::process &svc_domain) +unless ( $cgi->param('cgp_accessmodes') ) { + $cgi->param('cgp_accessmodes', + join(' ', + sort map { /^cgp_accessmodes_([\w\/]+)$/ or die "no way"; $1; } + grep $cgi->param($_), + grep /^cgp_accessmodes_([\w\/]+)$/, + $cgi->param() + ) + ); +} + +my %hash = $svcnum ? $old->hash : (); +map { + $hash{$_} = scalar($cgi->param($_)); + #} qw(svcnum pkgnum svcpart username _password popnum uid gid finger dir + # shell quota slipip) + } (fields('svc_acct'), qw ( pkgnum svcpart usergroup )); +my $new = new FS::svc_acct ( \%hash ); + +my $error = ''; + +$new->_password($old->_password) if $old; +if ( $cgi->param('clear_password') eq '*HIDDEN*' + || $cgi->param('clear_password') =~ /^\(.* encrypted\)$/ ) { + die "fatal: no previous account to recall hidden password from!" unless $old; +} else { + $error = $new->set_password($cgi->param('clear_password')); +} + +if ( $svcnum ) { + foreach ( grep { $old->$_ != $new->$_ } + qw( seconds upbytes downbytes totalbytes ) + ) + { + my %hash = map { $_ => $new->$_ } + grep { $new->$_ } + qw( seconds upbytes downbytes totalbytes ); + + $error ||= "invalid $_" foreach grep { $hash{$_} !~ /^\d+$/ } keys %hash; + $error ||= $new->set_usage(\%hash); #unoverlimit and trigger radius changes + last; #once is enough + } + $error ||= $new->replace($old); +} else { + $error ||= $new->insert; + $svcnum = $new->svcnum; +} + +</%init> diff --git a/httemplate/edit/process/svc_acct_pop.cgi b/httemplate/edit/process/svc_acct_pop.cgi new file mode 100755 index 000000000..6e823a824 --- /dev/null +++ b/httemplate/edit/process/svc_acct_pop.cgi @@ -0,0 +1,33 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "svc_acct_pop.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/svc_acct_pop.cgi") %> +%} +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Dialup configuration') + || $curuser->access_right('Dialup global configuration'); + +my $popnum = $cgi->param('popnum'); + +my $old = qsearchs('svc_acct_pop',{'popnum'=>$popnum}) if $popnum; + +my $new = new FS::svc_acct_pop ( { + map { + $_, scalar($cgi->param($_)); + } fields('svc_acct_pop') +} ); + +my $error = ''; +if ( $popnum ) { + $error = $new->replace($old); +} else { + $error = $new->insert; + $popnum=$new->getfield('popnum'); +} + +</%init> diff --git a/httemplate/edit/process/svc_broadband.cgi b/httemplate/edit/process/svc_broadband.cgi new file mode 100644 index 000000000..d5c9820bb --- /dev/null +++ b/httemplate/edit/process/svc_broadband.cgi @@ -0,0 +1,8 @@ +<% include('elements/svc_Common.html', 'table' => 'svc_broadband') %> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Provision customer service'); #something else more specific? + +</%init> diff --git a/httemplate/edit/process/svc_domain.cgi b/httemplate/edit/process/svc_domain.cgi new file mode 100755 index 000000000..381339bc8 --- /dev/null +++ b/httemplate/edit/process/svc_domain.cgi @@ -0,0 +1,57 @@ +%if ($error) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "svc_domain.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +#remove this to actually test the domains! +$FS::svc_domain::whois_hack = 1; + +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum = $1; + +#unmunge cgp_accessmodes (falze laziness-ish w/part_svc.pm::process & svc_acct) +unless ( $cgi->param('cgp_accessmodes') ) { + $cgi->param('cgp_accessmodes', + join(' ', + sort map { /^cgp_accessmodes_([\w\/]+)$/ or die "no way"; $1; } + grep $cgi->param($_), + grep /^cgp_accessmodes_([\w\/]+)$/, + $cgi->param() + ) + ); +} + +#unmunge acct_def_cgp_accessmodes (falze laziness-ahoy) +unless ( $cgi->param('acct_def_cgp_accessmodes') ) { + $cgi->param('acct_def_cgp_accessmodes', + join(' ', + sort map { /^acct_def_cgp_accessmodes_([\w\/]+)$/ or die "no way"; $1; } + grep $cgi->param($_), + grep /^acct_def_cgp_accessmodes_([\w\/]+)$/, + $cgi->param() + ) + ); +} + +my $new = new FS::svc_domain ( { + map { + $_, scalar($cgi->param($_)); + #} qw(svcnum pkgnum svcpart domain action) + } ( fields('svc_domain'), qw( pkgnum svcpart action ) ) +} ); + +my $error = ''; +if ($cgi->param('svcnum')) { + $error = $new->replace(); +} else { + $error = $new->insert; + $svcnum = $new->svcnum; +} + +</%init> diff --git a/httemplate/edit/process/svc_external.cgi b/httemplate/edit/process/svc_external.cgi new file mode 100755 index 000000000..673e5a5a0 --- /dev/null +++ b/httemplate/edit/process/svc_external.cgi @@ -0,0 +1,31 @@ +%if ($error) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "svc_external.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "view/svc_external.cgi?$svcnum") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum =$1; + +my $old = qsearchs('svc_external',{'svcnum'=>$svcnum}) if $svcnum; + +my $new = new FS::svc_external ( { + map { + ($_, scalar($cgi->param($_))); + } ( fields('svc_external'), qw( pkgnum svcpart ) ) +} ); + +my $error = ''; +if ( $svcnum ) { + $error = $new->replace($old); +} else { + $error = $new->insert; + $svcnum = $new->getfield('svcnum'); +} + +</%init> diff --git a/httemplate/edit/process/svc_external.html b/httemplate/edit/process/svc_external.html new file mode 100644 index 000000000..3515afc4b --- /dev/null +++ b/httemplate/edit/process/svc_external.html @@ -0,0 +1,10 @@ +<% include( 'elements/svc_Common.html', + 'table' => 'svc_external', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +</%init> diff --git a/httemplate/edit/process/svc_forward.cgi b/httemplate/edit/process/svc_forward.cgi new file mode 100755 index 000000000..fffad84d6 --- /dev/null +++ b/httemplate/edit/process/svc_forward.cgi @@ -0,0 +1,31 @@ +%if ($error) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "svc_forward.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "view/svc_forward.cgi?$svcnum") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum =$1; + +my $old = qsearchs('svc_forward',{'svcnum'=>$svcnum}) if $svcnum; + +my $new = new FS::svc_forward ( { + map { + ($_, scalar($cgi->param($_))); + } ( fields('svc_forward'), qw( pkgnum svcpart ) ) +} ); + +my $error = ''; +if ( $svcnum ) { + $error = $new->replace($old); +} else { + $error = $new->insert; + $svcnum = $new->getfield('svcnum'); +} + +</%init> diff --git a/httemplate/edit/process/svc_mailinglist.html b/httemplate/edit/process/svc_mailinglist.html new file mode 100644 index 000000000..580f6ccbd --- /dev/null +++ b/httemplate/edit/process/svc_mailinglist.html @@ -0,0 +1,11 @@ +<% include( 'elements/svc_Common.html', + 'table' => 'svc_mailinglist', + 'fields' => [ fields('svc_mailinglist'), 'listname' ], + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +</%init> diff --git a/httemplate/edit/process/svc_phone.html b/httemplate/edit/process/svc_phone.html new file mode 100644 index 000000000..e02ec5ccc --- /dev/null +++ b/httemplate/edit/process/svc_phone.html @@ -0,0 +1,29 @@ +<% include( 'elements/svc_Common.html', + 'table' => 'svc_phone', + 'args_callback' => $args_callback, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my $args_callback = sub { + my( $cgi, $object ) = @_; + + my %opt = (); + if ( $cgi->param('locationnum') == -1 ) { + my $cust_location = new FS::cust_location { + map { $_ => scalar($cgi->param($_)) } + qw( custnum address1 address2 city county state zip country ) + }; + $opt{'cust_location'} = $cust_location; + } + + %opt; + +}; + + + +</%init> diff --git a/httemplate/edit/process/svc_www.cgi b/httemplate/edit/process/svc_www.cgi new file mode 100644 index 000000000..f02d25305 --- /dev/null +++ b/httemplate/edit/process/svc_www.cgi @@ -0,0 +1,38 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "svc_www.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "view/svc_www.cgi?" . $svcnum ) %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum = $1; + +my $old; +if ( $svcnum ) { + $old = qsearchs('svc_www', { 'svcnum' => $svcnum } ) + or die "fatal: can't find website (svcnum $svcnum)!"; +} else { + $old = ''; +} + +my $new = new FS::svc_www ( { + map { + ($_, scalar($cgi->param($_))); + #} qw(svcnum pkgnum svcpart recnum usersvc) + } ( fields('svc_www'), qw( pkgnum svcpart ) ) +} ); + +my $error; +if ( $svcnum ) { + $error = $new->replace($old); +} else { + $error = $new->insert; + $svcnum = $new->svcnum; +} + +</%init> diff --git a/httemplate/edit/process/tax_class.html b/httemplate/edit/process/tax_class.html new file mode 100644 index 000000000..339c9083e --- /dev/null +++ b/httemplate/edit/process/tax_class.html @@ -0,0 +1,49 @@ +% if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "tax_class.html?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/tax_rate.cgi?taxclassnum=". uri_escape($tax_class->taxclassnum) ) %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $tax_class = new FS::tax_class { + 'taxclass' => $cgi->param('taxclass'), + 'description' => $cgi->param('description'), +}; + +#maybe this whole thing should be in a transaction. at some point, no biggie +#none of the follow-up stuff will fail unless there's a more serious problem +#than a hanging record in tax_class... + +my $error = $tax_class->insert; + +# all of this is highly dubious at the moment + +#unless ( $error ) { +# #auto-add the new taxclass to any regions that have taxclasses already +# +# my $sth = dbh->prepare(" +# SELECT geocode FROM tax_rate +# WHERE taxclass IS NOT NULL AND taxclass != '' +# GROUP BY geocode +# ") or die dbh->errstr; +# $sth->execute or die $sth->errstr; +# +# while ( my $row = $sth->fetchrow_hashref ) { +# warn "inserting for $row"; +# my $cust_main_county = new FS::tax_rate { +# 'geocode' => $row->{geocode}, +# 'tax' => 0, +# 'taxclassnum' => $tax_class->taxclassnum, +# }; +# $error = $cust_main_county->insert; +# #last if $error; +# die $error if $error; +# } +# +#} + +</%init> diff --git a/httemplate/edit/process/tax_rate.html b/httemplate/edit/process/tax_rate.html new file mode 100644 index 000000000..431e54264 --- /dev/null +++ b/httemplate/edit/process/tax_rate.html @@ -0,0 +1,22 @@ +<% include( 'elements/process.html', + 'table' => 'tax_rate', + 'value_callback' => $value_callback, + 'popup_reload' => 'Tax changed', #a popup "parent reload" for now + #someday change the individual element and go away instead + ) +%> +<%once> + +my $value_callback = sub { my ($field, $value) = @_; + ($field =~ /^(tax|excessrate|usetax|useexcessrate)$/) + ? $value/100 + : $value + }; +</%once> +<%init> + +my $conf = new FS::Conf; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/usage_class.html b/httemplate/edit/process/usage_class.html new file mode 100644 index 000000000..cf50cb762 --- /dev/null +++ b/httemplate/edit/process/usage_class.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'usage_class', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/prospect_main.html b/httemplate/edit/prospect_main.html new file mode 100644 index 000000000..e867907ed --- /dev/null +++ b/httemplate/edit/prospect_main.html @@ -0,0 +1,114 @@ +<% include('elements/edit.html', + 'name_singular' => 'prospect', + 'table' => 'prospect_main', + 'labels' => { 'prospectnum' => 'Prospect', + 'agentnum' => 'Agent', + 'company' => 'Company', + 'contactnum' => 'Contact', + }, + 'fields' => [ + { 'field' => 'agentnum', + 'type' => 'select-agent', + 'empty_label' => 'Select agent', + }, + { 'field' => 'company', + 'type' => 'text', + 'size' => 50, + }, + { 'field' => 'contactnum', + 'type' => 'contact', + 'colspan' => 6, + ##actually o2m, but this seems to be working for edit so far + #'m2name_table' => 'contact', + #'m2name_namecol' => 'contactnum', + #'m2_label' => 'Contact', + #'m2_error_callback' => $m2_error_callback, + + 'o2m_table' => 'contact', + 'm2_label' => 'Contact', + 'm2_error_callback' => $m2_error_callback, + + }, + { 'field' => 'locationnum', + 'type' => 'select-cust_location', + 'empty_label' => 'No address', + }, + ], + 'edit_callback' => $edit_callback, + 'error_callbacck' => $error_callback, + 'agent_virt' => 1, + ) +%> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $prospectnum; +if ( $cgi->param('error') ) { + $prospectnum = scalar($cgi->param('prospectnum')); + + die "access denied" + unless $curuser->access_right(($prospectnum ? 'Edit' : 'New'). ' prospect'); + +} elsif ( $cgi->keywords ) { #editing + + die "access denied" + unless $curuser->access_right('Edit prospect'); + +} else { #new prospect + + die "access denied" + unless $curuser->access_right('New prospect'); + +} + +my $edit_callback = sub { + #my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_; + my( $cgi, $prospect_main ) = @_; + my @cust_location = + qsearch('cust_location', { 'prospectnum' => $prospect_main->prospectnum } ); + die 'multiple locations for prospect '. $prospect_main->prospectnum + if scalar(@cust_location) > 1; + $prospect_main->set('locationnum', $cust_location[0]->locationnum) + if scalar(@cust_location); + #warn 'prospect_main.locationnum '.$prospect_main->get('locationnum'); +}; + +my $error_callback = sub { + #my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_; + my( $cgi, $prospect_main ) = @_; + $cgi->param('locationnum') =~ /^(\-?\d*)$/ + or die 'illegal locationnum '. $cgi->param('locationnum'); + my $locationnum = $1; + $prospect_main->set('locationnum', $locationnum); +}; + +my $m2_error_callback = sub { + my($cgi, $object) = @_; + + #process_o2m fields in process/prospect_main.html + my @fields = qw( first last title comment ); + my @gfields = ( '', map "_$_", @fields ); + + map { + if ( /^contactnum(\d+)$/ ) { + my $num = $1; + if ( grep $cgi->param("contactnum$num$_"), @gfields ) { + my $x = new FS::contact { + 'contactnum' => $cgi->param("contactnum$num"), + map { $_ => scalar($cgi->param("contactnum${num}_$_")) } @fields, + }; + $x; + } else { + (); + } + } else { + (); + } + } + $cgi->param; +}; + +#my @agentnums = $FS::CurrentUser::CurrentUser->agentnums; + +</%init> diff --git a/httemplate/edit/quick-charge.html b/httemplate/edit/quick-charge.html new file mode 100644 index 000000000..a472915a1 --- /dev/null +++ b/httemplate/edit/quick-charge.html @@ -0,0 +1,294 @@ +<% include("/elements/header-popup.html", 'One-time charge', '', + ( $cgi->param('error') ? '' : 'onload="addRow()"' ), + ) +%> + +<LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2"> +<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar_stripped.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-en.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT> + +<% include('/elements/error.html') %> + +<SCRIPT TYPE="text/javascript"> + +function enable_quick_charge () { + if ( document.QuickChargeForm.amount.value + && document.QuickChargeForm.pkg.value ) { + document.QuickChargeForm.submit.disabled = false; + } else { + document.QuickChargeForm.submit.disabled = true; + } +} + +function validate_quick_charge () { + var pkg = document.QuickChargeForm.pkg.value; + var pkg_regex = /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ ; + var amount = document.QuickChargeForm.amount.value; + var amount_regex = /^\s*\$?\s*(\d*(\.?\d{1,2}))\s*$/ ; + var rval = true; + + if ( ! amount_regex.test(amount) ) { + alert('Illegal amount - enter an amount to charge, for example, "5" or "43" or "21.46".'); + return false; + } + if ( String(pkg).length < 1 ) { + rval = false; + } + if ( ! pkg_regex.test(pkg) ) { + rval = false; + } + var i=0; + for (i=0; i < rownum; i++) { + if (! eval('pkg_regex.test(document.QuickChargeForm.description' + i + '.value)')){ + rval = false; + break; + } + } + if (rval == true) { + return true; + } + + if ( ! pkg ) { + alert('Enter a description for the one-time charge'); + return false; + } + + alert('Illegal description - spaces, letters, numbers, and the following punctuation characters are allowed: . , ! ? @ # $ % & ( ) - + ; : ' + "'" + ' " = [ ]' ); + return false; +} + +function bill_now_changed (what) { + var form = what.form; + if ( what.checked ) { + form.start_date_text.disabled = true; + form.start_date.style.backgroundColor = '#dddddd'; + form.start_date_button.style.display = 'none'; + form.start_date_button_disabled.style.display = ''; + form.invoice_terms.disabled = false; + } else { + form.start_date_text.disabled = false; + form.start_date.style.backgroundColor = '#ffffff'; + form.start_date_button.style.display = ''; + form.start_date_button_disabled.style.display = 'none'; + form.invoice_terms.disabled = true; + } +} + +</SCRIPT> + +<FORM ACTION="process/quick-charge.cgi" NAME="QuickChargeForm" ID="QuickChargeForm" METHOD="POST" onsubmit="document.QuickChargeForm.submit.disabled=true;return validate_quick_charge();"> + +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> + +<TABLE ID="QuickChargeTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 STYLE="background-color: #cccccc"> + +<TR> + <TD ALIGN="right">Amount </TD> + <TD> + $<INPUT TYPE="text" NAME="amount" SIZE=6 VALUE="<% $amount %>" onChange="enable_quick_charge()" onKeyPress="enable_quick_charge()"> + </TD> +</TR> + +% if ( $conf->exists('invoice-unitprice') ) { + <TR> + <TD ALIGN="right">Quantity </TD> + <TD> + <INPUT TYPE="text" NAME="quantity" SIZE=4 VALUE="<% $quantity %>"> + </TD> + </TR> +% } + +<% include('/elements/tr-select-pkg_class.html', 'curr_value' => $cgi->param('classnum') ) %> + +<TR> + <TD ALIGN="right">Invoice now</TD> + <TD> + <INPUT TYPE = "checkbox" + NAME = "bill_now" + VALUE = "1" + <% $cgi->param('bill_now') ? 'CHECKED' : '' %> + onChange = "bill_now_changed(this);" + > + with terms + <% include('/elements/select-terms.html', + 'curr_value' => scalar($cgi->param('invoice_terms')), + 'empty_value' => $default_terms, + 'disabled' => ( $cgi->param('bill_now') ? 0 : 1 ), + ) + %> + </TD> +</TR> + +%# false laziness w/misc/order_pkg.html +<TR> + <TD ALIGN="right">Charge date </TD> + <TD> + <INPUT TYPE = "text" + NAME = "start_date" + SIZE = 32 + ID = "start_date_text" + VALUE = "<% $start_date %>" + <% $cgi->param('bill_now') ? 'STYLE = "background-color:#dddddd" DISABLED' : '' %> + > + <IMG SRC = "<%$fsurl%>images/calendar.png" + ID = "start_date_button" + TITLE = "Select date" + STYLE = "cursor:pointer<% $cgi->param('bill_now') ? ';display:none' : '' %>" + > + <IMG SRC = "<%$fsurl%>images/calendar-disabled.png" + ID = "start_date_button_disabled" + <% $cgi->param('bill_now') ? '' : 'STYLE="display:none"' %> + > + <FONT SIZE=-1>(leave blank to charge immediately)</FONT> + </TD> +</TR> + +<SCRIPT TYPE="text/javascript"> + Calendar.setup({ + inputField: "start_date_text", + ifFormat: "<% $date_format %>", + button: "start_date_button", + align: "BR" + }); +</SCRIPT> + +% if ( $cust_main->payby =~ /^(CARD|CHEK)$/ ) { +% my $what = lc(FS::payby->shortname($cust_main->payby)); + <TR> + <TD ALIGN="right">Disable automatic <% $what %> charge </TD> + <TD COLSPAN=6><INPUT TYPE="checkbox" NAME="no_auto" VALUE="Y"></TD> + </TR> +% } + +<TR> + <TD ALIGN="right">Tax exempt </TD> + <TD><INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y" <% $cgi->param('setuptax') ? 'CHECKED' : '' %>></TD> +</TR> + +<% include('/elements/tr-select-taxclass.html', 'curr_value' => $cgi->param('taxclass') ) %> + +<% include('/elements/tr-select-taxproduct.html', 'label' => 'Tax product', 'onclick' => 'parent.taxproductmagic(this);', 'curr_value' => $cgi->param('taxproductnum') ) %> + +<% include('/elements/tr-select-taxoverride.html', 'onclick' => 'parent.taxoverridemagic(this);', 'curr_value' => $cgi->param('tax_override') ) %> + +<TR> + <TD ALIGN="right">Description </TD> + <TD> + <INPUT TYPE="text" NAME="pkg" SIZE="50" MAXLENGTH="50" VALUE="<% $pkg %>" onChange="enable_quick_charge()" onKeyPress="enable_quick_charge()"> + </TD> +</TR> + +<TR> + <TD></TD> + <TD><FONT SIZE="-1">Optional additional description (also printed on invoice): </FONT></TD> +</TR> + +% my $row = 0; +% if ( $cgi->param('error') || $cgi->param('magic') ) { +% my $param = $cgi->Vars; +% +% for ( $row = 0; exists($param->{"description$row"}); $row++ ) { + + <TR> + <TD></TD> + <TD> + <INPUT TYPE="text" NAME="description<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $param->{"description$row"} |h %>" rownum="<% $row %>" onkeyup = "possiblyAddRow;" > + </TD> + </TR> +% } +% } + + +</TABLE> + +<BR> +<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="Add one-time charge" <% $cgi->param('error') ? '' :' DISABLED' %>> + +</FORM> + + +<SCRIPT TYPE="text/javascript"> + + var rownum = <% $row %>; + + function possiblyAddRow() { + if ( ( rownum - this.getAttribute('rownum') ) == 1 ) { + addRow(); + } + } + + function addRow() { + + var table = document.getElementById('QuickChargeTable'); + var tablebody = table.getElementsByTagName('tbody').item(0); + + var row = document.createElement('TR'); + + var empty_cell = document.createElement('TD'); + row.appendChild(empty_cell); + + var description_cell = document.createElement('TD'); + + var description_input = document.createElement('INPUT'); + description_input.setAttribute('name', 'description'+rownum); + description_input.setAttribute('id', 'description'+rownum); + description_input.setAttribute('size', 60); + description_input.setAttribute('maxLength', 65); + description_input.setAttribute('rownum', rownum); + description_input.onkeyup = possiblyAddRow; + description_cell.appendChild(description_input); + + row.appendChild(description_cell); + + tablebody.appendChild(row); + + rownum++; + + } + +</SCRIPT> + +</BODY> +</HTML> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('One-time charge'); + +my $conf = new FS::Conf; +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('cust_main', { 'custnum' => $custnum } ); #XXX agent-virt + +my $format = "%m/%d/%Y %T %z (%Z)"; #false laziness w/REAL_cust_pkg.cgi? +my $start_date = $cust_main->next_bill_date; +$start_date = $start_date ? time2str($format, $start_date) : ''; + +my $amount = ''; +if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) { + $amount = $1; +} + +my $quantity = 1; +if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) { + $quantity = $1; +} + +$cgi->param('pkg') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ + or die 'illegal description'; +my $pkg = $1; + +my $default_terms; +if ( $cust_main->invoice_terms ) { + $default_terms = 'Customer default ('. $cust_main->invoice_terms. ')'; +} else { + $default_terms = + 'Default ('. + ($conf->config('invoice_default_terms') || 'Payable upon receipt'). + ')'; +} + +</%init> diff --git a/httemplate/edit/rate.cgi b/httemplate/edit/rate.cgi new file mode 100644 index 000000000..13717dc1a --- /dev/null +++ b/httemplate/edit/rate.cgi @@ -0,0 +1,51 @@ +<% include("/elements/header.html","$action Rate plan", menubar( + 'View all rate plans' => "${p}browse/rate.cgi", + )) +%> + +<% include('/elements/progress-init.html', + 'OneTrueForm', + [ 'rate', 'preserve_rate_detail' ], # 'rate', 'min_', 'sec_' ], + 'process/rate.cgi', + $p.'browse/rate.cgi', + ) +%> +<FORM NAME="OneTrueForm"> +<INPUT TYPE="hidden" NAME="ratenum" VALUE="<% $rate->ratenum %>"> + +Rate plan +<INPUT TYPE="text" NAME="ratename" SIZE=32 VALUE="<% $rate->ratename %>"> +<BR><BR> + +<INPUT TYPE="hidden" NAME="preserve_rate_detail" VALUE="1"> + +<INPUT NAME="submit" TYPE="button" VALUE="<% + $rate->ratenum ? "Apply changes" : "Add rate plan" +%>" onClick="document.OneTrueForm.submit.disabled=true; process();"> +</FORM> + +% if($rate->ratenum) { +<BR><BR><FONT SIZE="+2">Rates in this plan</FONT> +<% include('/edit/elements/rate_detail.html', + 'ratenum' => $rate->ratenum +) %> +% } + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $rate; +if ( $cgi->keywords ) { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/; + $rate = qsearchs( 'rate', { 'ratenum' => $1 } ); +} else { #adding + $rate = new FS::rate {}; +} +my $action = $rate->ratenum ? 'Edit' : 'Add'; + +</%init> diff --git a/httemplate/edit/rate_detail.html b/httemplate/edit/rate_detail.html new file mode 100644 index 000000000..ec04e4cbb --- /dev/null +++ b/httemplate/edit/rate_detail.html @@ -0,0 +1,81 @@ +<% include('elements/edit.html', + 'popup' => 1, + 'name' => $name, + 'table' => 'rate_detail', + 'labels' => { 'ratedetailnum' => 'Rate', #should hide... + 'dest_regionname' => 'Region', + 'dest_prefixes_short' => 'Prefix(es)', + 'rate_time_name' => 'Time period', + 'min_included' => 'Included minutes/calls', + 'conn_charge' => 'Connection charge', + 'conn_sec' => 'For', + 'min_charge' => 'Charge per minute/call', + 'sec_granularity' => 'Granularity', + 'classnum' => 'Usage class', + }, + 'fields' => [ + { field=>'ratenum', type=>'hidden', }, + { field=>'orig_regionnum', type=>'hidden', }, + { field=>'dest_regionnum', type=>'hidden', }, + { field=>'ratetimenum', type=>'hidden', }, + { field=>'dest_regionname', type=>'fixed', }, + { field=>'dest_prefixes_short', type=>'fixed', }, + { field=>'rate_time_name', type=>'fixed', }, + { field=>'min_included', type=>'text', size=>5 }, + { field=>'conn_charge', type=>'money', size=>4 }, + { field =>'conn_sec', + type =>'select', + options => [ keys %conn_secs ], + labels => \%conn_secs, + disable_empty => 1, + }, + { field=>'min_charge', type=>'money', size=>4 }, + { field =>'sec_granularity', + type =>'select', + options => [ keys %granularity ], + labels => \%granularity, + disable_empty => 1, + }, + { field =>'classnum', + type =>'select-table', + table =>'usage_class', + name_col =>'classname', + empty_label =>'(default)', + hashref =>{ disabled => '' }, + }, + + ], + 'new_hashref_callback' => sub { + { ratenum => $cgi->param('ratenum'), + dest_regionnum => $cgi->param('dest_regionnum'), + ratetimenum => $cgi->param('ratetimenum'), + } + }, + ) +%> +<%once> + +tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); +tie my %conn_secs, 'Tie::IxHash', FS::rate_detail::conn_secs(); + +</%once> +<%init> + +my $conf = new FS::Conf; +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +#slightly inefficient, i suppose an edit+error callback would be better +my $name = 'rate'; +my ($keywords) = $cgi->keywords; +if ( $keywords =~ /^(\d+)$/ + || $cgi->param('ratedetailnum') =~ /^(\d+)$/ ) { + my $rate_detail = qsearchs('rate_detail', { 'ratedetailnum' => $1 } ) + or die "unknown ratedetailnum $1"; + $name = + $rate_detail->rate->ratename. ' rate for '. $rate_detail->dest_regionname; +} + +#sec_granularity should default to 60! for new rates when this gets used for em + +</%init> diff --git a/httemplate/edit/rate_region.cgi b/httemplate/edit/rate_region.cgi new file mode 100644 index 000000000..cae30030a --- /dev/null +++ b/httemplate/edit/rate_region.cgi @@ -0,0 +1,95 @@ +<% include("/elements/header.html","$action Region", menubar( + 'View all regions' => "${p}browse/rate_region.html", + )) +%> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/rate_region.cgi" METHOD=POST> + +<INPUT TYPE="hidden" NAME="regionnum" VALUE="<% $rate_region->regionnum %>"> + +%# region info + +<% ntable('#cccccc') %> + + <TR> + <TH ALIGN="right">Region name</TH> + <TD><INPUT TYPE="text" NAME="regionname" SIZE=32 VALUE="<% $rate_region->regionname %>"></TR> + </TR> + + <TR> + <TH ALIGN="right">Country code</TH> + <TD><INPUT TYPE="text" NAME="countrycode" SIZE=4 MAXLENGTH=3 VALUE="<% $countrycode %>"></TR> + </TR> + + <TR> + <TD ALIGN="right"> + <B>Prefixes</B> + <BR><FONT SIZE="-1">(comma-separated)</FONT> + </TD> + <TD> + <TEXTAREA NAME="npa" WRAP=SOFT><% join(', ', map { $_->npa. (length($_->nxx) ? '-'.$_->nxx : '') } @rate_prefix ) %></TEXTAREA> + </TD> + </TR> + +</TABLE> + +<BR><BR> +<INPUT TYPE="submit" VALUE="<% $rate_region->regionnum ? "Apply changes" : "Add region" %>"> +</FORM> +%# rate plan info, if the region has been created yet + +% if($rate_region->regionnum) { +<BR> +<BR> +<FONT SIZE="+2">Rates in this region</FONT> +<% include('/edit/elements/rate_detail.html', + 'regionnum' => $rate_region->regionnum, +) %> +% } + +<% include('/elements/footer.html') %> +<%once> + +tie my %conn_secs, 'Tie::IxHash', FS::rate_detail::conn_secs(); + +</%once> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; + +my $rate_region; +if ( $cgi->param('error') ) { + $rate_region = new FS::rate_region ( { + map { $_, scalar($cgi->param($_)) } fields('rate_region') + } ); +} elsif ( $cgi->keywords ) { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die "unparsable regionnum"; + $rate_region = qsearchs( 'rate_region', { 'regionnum' => $1 } ) + or die "unknown regionnum $1\n"; +} else { #adding + $rate_region = new FS::rate_region {}; +} +my $action = $rate_region->regionnum ? 'Edit' : 'Add'; + +my $p1 = popurl(1); + +tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); + +my @rate_prefix = $rate_region->rate_prefix; +my $countrycode = ''; +if ( @rate_prefix ) { + $countrycode = $rate_prefix[0]->countrycode; + foreach my $rate_prefix ( @rate_prefix ) { + errorpage('multiple country codes per region not yet supported by web UI') + unless $rate_prefix->countrycode eq $countrycode; + } +} + +</%init> diff --git a/httemplate/edit/rate_time.cgi b/httemplate/edit/rate_time.cgi new file mode 100644 index 000000000..eca8fbb61 --- /dev/null +++ b/httemplate/edit/rate_time.cgi @@ -0,0 +1,69 @@ +<% include("/elements/header.html", { title => "$action Time Period" }) %> +<% include("/elements/menubar.html", + 'Rate plans' => "${p}browse/rate.cgi", + ) %> +<BR> +<% include('/elements/error.html') %> +<BR> + +<FORM METHOD="POST" ACTION="<% "${p}edit/process/rate_time.cgi" %>"> +<INPUT TYPE="hidden" NAME="ratetimenum" VALUE="<% $ratetimenum %>"> +<% ntable('#cccccc',2) %> +<TABLE> + <TR> + <TH ALIGN="right">Period name</TH> + <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, + ) %> +<INPUT TYPE="submit" VALUE="<% $rate_time ? 'Apply changes' : 'Add period'%>"> +</FORM> +<BR> +<A HREF="<% "${p}edit/process/rate_time.cgi?ratetimenum=$ratetimenum;delete=1" %>">Delete this period</A> +<% include('/elements/footer.html') %> + +<%init> +my $ratetimenum = ($cgi->keywords)[0] || $cgi->param('ratetimenum') || ''; +my $action = 'Add'; +my $rate_time; +my @data = (); +my $day = [ 0 => 'Sun', + 1 => 'Mon', + 2 => 'Tue', + 3 => 'Wed', + 4 => 'Thu', + 5 => 'Fri', + 6 => 'Sat', ]; +my $hour = [ map( {$_, sprintf('%02d',$_) } 0..11 )]; +my $min = [ map( {$_, sprintf('%02d',$_) } 0,30 )]; +my $ampm = [ 0 => 'AM', 1 => 'PM' ]; + +if($ratetimenum) { + $action = 'Edit'; + $rate_time = qsearchs('rate_time', {ratetimenum => $ratetimenum}) + or die "ratetimenum $ratetimenum not found"; + if($cgi->param('error')) { + my %vars = $cgi->Vars; + foreach my $i (sort {$a <=> $b } map { /^sd(\d+)$/ } keys(%vars)) { + push @data, [ @vars{"sd$i", "sh$i", "sm$i", "sa$i", + "ed$i", "eh$i", "em$i", "ea$i"} ]; + } + } + else { + foreach my $interval ($rate_time->intervals) { + push @data, [ map { int($_/86400) % 7, + int($_/3600) % 12, + int($_/60) % 60, + int($_/43200) % 2, } + ( $interval->stime, $interval->etime ) + ]; + } + } +} + +</%init> diff --git a/httemplate/edit/reason.html b/httemplate/edit/reason.html new file mode 100644 index 000000000..620a2ea15 --- /dev/null +++ b/httemplate/edit/reason.html @@ -0,0 +1,50 @@ +% +% $cgi->param('class') =~ /^(\w)$/ or die "illegal class"; +% my $class=$1; +% +% my $classname = $FS::reason_type::class_name{$class}; +% +% my (@types) = qsearch( 'reason_type', { 'class' => $class } ); +% +% unless (scalar(@types)) { +% print $cgi->redirect( "reason_type.html?class=$class" ); +% } +<% include( 'elements/edit.html', + 'name' => ucfirst($classname) . ' Reason', + 'table' => 'reason', + 'labels' => { + 'reasonnum' => ucfirst($classname) . ' Reason', + 'reason_type' => ucfirst($classname) . ' Reason type', + 'reason' => ucfirst($classname) . ' Reason', + 'disabled' => 'Disabled', + 'class' => '', + }, + 'fields' => [ + { 'field' => 'reason_type', + 'type' => 'select', + #XXX use something more sane than a hashref + #then fix tr-select.html + 'value' => { 'vcolumn' => 'typenum', + 'ccolumn' => 'type', + 'values' => \@types, + }, + }, + 'reason', + { 'field' => 'class', + 'type' => 'hidden', + 'value' => $class, + }, + { 'field' => 'disabled', + 'type' => 'checkbox', + 'value' => 'Y' + }, + ], + 'viewall_url' => $p . "browse/reason.html?class=$class", + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/reason_type.html b/httemplate/edit/reason_type.html new file mode 100644 index 000000000..ea5650ec3 --- /dev/null +++ b/httemplate/edit/reason_type.html @@ -0,0 +1,29 @@ +<% include( 'elements/edit.html', + 'name' => $classname . ' Reason Type', + 'table' => 'reason_type', + 'labels' => { + 'typenum' => $classname . ' reason type', + 'type' => $classname . ' reason type name', + 'class' => '', + }, + 'fields' => [ + 'type', + { 'field' => 'class', + 'type' => 'hidden', + }, + ], + 'viewall_url' => $p . "browse/reason_type.html?class=$class", + 'new_hashref_callback' => sub {{ 'class' => $class }}, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +$cgi->param('class') =~ /^(\w)$/; +my $class = $1; + +my $classname = $FS::reason_type::class_name{$class}; + +</%init> diff --git a/httemplate/edit/reg_code.cgi b/httemplate/edit/reg_code.cgi new file mode 100644 index 000000000..76790ab02 --- /dev/null +++ b/httemplate/edit/reg_code.cgi @@ -0,0 +1,44 @@ +<% include('/elements/header.html', 'Generate registration codes for '. $agent->agent) %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%popurl(1)%>process/reg_code.cgi" METHOD="POST" NAME="OneTrueForm" onSubmit="document.OneTrueForm.submit.disabled=true"> +<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>"> + +Generate +% my $num = ''; +% if ( $cgi->param('num') =~ /^\s*(\d+)\s*$/ ) { +% $num = $1; +% } +<INPUT TYPE="text" NAME="num" VALUE="<% $num %>" SIZE=5 MAXLENGTH=4> +registration codes for <B><% $agent->agent %></B> allowing the following packages: +<BR><BR> + +% foreach my $part_pkg ( qsearch('part_pkg', { 'disabled' => '' } ) ) { +% my $pkgpart = $part_pkg->pkgpart; + + <INPUT TYPE="checkbox" NAME="pkgpart<% $pkgpart %>" <% $cgi->param("pkgpart$pkgpart") ? 'CHECKED' : '' %>> + <% $part_pkg->pkg_comment %> + <BR> + +% } + + +<BR> +<INPUT TYPE="submit" NAME="submit" VALUE="Generate"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $agentnum = $cgi->param('agentnum'); +$agentnum =~ /^(\d+)$/ or errorpage("illegal agentnum $agentnum"); +$agentnum = $1; +my $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); + +</%init> diff --git a/httemplate/edit/router.cgi b/httemplate/edit/router.cgi new file mode 100755 index 000000000..70eaa4576 --- /dev/null +++ b/httemplate/edit/router.cgi @@ -0,0 +1,54 @@ +<% include('elements/edit.html', + 'post_url' => popurl(1).'process/router.cgi', + 'name' => 'router', + 'table' => 'router', + 'viewall_url' => "${p}browse/router.cgi", + 'labels' => { 'routernum' => 'Router', + 'routername' => 'Name', + 'svc_part' => 'Service', + }, + 'fields' => [ + { 'field'=>'routername', 'type'=>'text', 'size'=>32 }, + { 'field'=>'agentnum', 'type'=>'select-agent' }, + { 'field'=>'svcnum', 'type'=>'hidden' }, + ], + 'error_callback' => $callback, + 'edit_callback' => $callback, + 'new_callback' => $callback, + 'html_table_bottom' => $html_table_bottom, + ) +%> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Broadband configuration') + || $curuser->access_right('Broadband global configuration'); + +my $callback = sub { + my ($cgi, $object, $fields) = (shift, shift, shift); + unless ($object->svcnum) { + push @{$fields}, + { 'type' => 'tablebreak-tr-title', + 'value' => 'Select the service types available on this router', + }, + { 'field' => 'svc_part', + 'type' => 'checkboxes-table', + 'target_table' => 'part_svc', + 'link_table' => 'part_svc_router', + 'name_col' => 'svc', + 'hashref' => { 'svcdb' => 'svc_broadband', 'disabled' => '' }, + }; + } +}; + +my $html_table_bottom = sub { + my $router = shift; + my $html = ''; + foreach my $field ($router->virtual_fields) { + $html .= $router->pvf($field)->widget('HTML', 'edit', $router->get($field)); + } + $html; +}; +</%init> diff --git a/httemplate/edit/svc_Common.html b/httemplate/edit/svc_Common.html new file mode 100644 index 000000000..3da72d2e8 --- /dev/null +++ b/httemplate/edit/svc_Common.html @@ -0,0 +1,33 @@ +<% include('elements/svc_Common.html', + 'table' => $table, + 'post_url' => popurl(1). "process/svc_Common.html", + %opt, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +# false laziness w/view/svc_Common.html + +$cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unparsable svcdb"; +my $table = $1; +require "FS/$table.pm"; + +my %opt; +if ( UNIVERSAL::can("FS::$table", 'table_info') ) { + $opt{'name'} = "FS::$table"->table_info->{'name'}; + + my $fields = "FS::$table"->table_info->{'fields'}; + my %labels = map { $_ => ( ref($fields->{$_}) + ? $fields->{$_}{'label'} + : $fields->{$_} + ); + } + keys %$fields; + $opt{'labels'} = \%labels; + +} + +</%init> diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi new file mode 100755 index 000000000..59b5d10f3 --- /dev/null +++ b/httemplate/edit/svc_acct.cgi @@ -0,0 +1,698 @@ +<% include('/elements/header.html', "$action $svc account") %> + +<% include('/elements/error.html') %> + +% if ( $cust_main ) { + + <% include( '/elements/small_custview.html', $cust_main, '', 1, + popurl(2) . "view/cust_main.cgi") %> + <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 %>"> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>"> + +Service # <% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR> + +<% ntable("#cccccc",2) %> + +<TR> + <TD ALIGN="right">Service</TD> + <TD BGCOLOR="#eeeeee"><% $part_svc->svc %></TD> +</TR> + +<TR> + <TD ALIGN="right">Username</TD> + <TD> + <INPUT TYPE="text" NAME="username" VALUE="<% $username %>" SIZE=<% $ulen2 %> MAXLENGTH=<% $ulen %>> + </TD> +</TR> + +%if ( $part_svc->part_svc_column('_password')->columnflag ne 'F' ) { +<TR> + <TD ALIGN="right">Password</TD> + <TD> + <INPUT TYPE="text" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>> + <INPUT TYPE="button" VALUE="Generate" onclick="randomPass();"> + </TD> +</TR> +%}else{ + <INPUT TYPE="hidden" NAME="clear_password" VALUE="<% $password %>"> +%} +<INPUT TYPE="hidden" NAME="_password_encoding" VALUE="<% $svc_acct->_password_encoding %>"> +% +%my $sec_phrase = $svc_acct->sec_phrase; +%if ( $conf->exists('security_phrase') +% && $part_svc->part_svc_column('sec_phrase')->columnflag ne 'F' ) { +% + + + <TR> + <TD ALIGN="right">Security phrase</TD> + <TD> + <INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $sec_phrase %>" SIZE=32> + (for forgotten passwords) + </TD> + </TD> +% } else { + + + <INPUT TYPE="hidden" NAME="sec_phrase" VALUE="<% $sec_phrase %>"> +% } +% +%#domain +%my $domsvc = $svc_acct->domsvc || 0; +%if ( $part_svc->part_svc_column('domsvc')->columnflag eq 'F' ) { +% + + + <INPUT TYPE="hidden" NAME="domsvc" VALUE="<% $domsvc %>"> +% } else { +% +% my %svc_domain = (); +% +% if ( $domsvc ) { +% my $svc_domain = qsearchs('svc_domain', { 'svcnum' => $domsvc, } ); +% if ( $svc_domain ) { +% $svc_domain{$svc_domain->svcnum} = $svc_domain; +% } else { +% warn "unknown svc_domain.svcnum for svc_acct.domsvc: $domsvc"; +% } +% } +% +% %svc_domain = (%svc_domain, +% domain_select_hash FS::svc_acct('svcpart' => $svcpart, +% 'pkgnum' => $pkgnum, +% ) +% ); +% + + + <TR> + <TD ALIGN="right">Domain</TD> + <TD> + <SELECT NAME="domsvc" SIZE=1> +% foreach my $svcnum ( +% sort { $svc_domain{$a} cmp $svc_domain{$b} } +% keys %svc_domain +% ) { +% my $svc_domain = $svc_domain{$svcnum}; +% + + + <OPTION VALUE="<% $svcnum %>" <% $svcnum == $domsvc ? ' SELECTED' : '' %>><% $svc_domain{$svcnum} %> +% } + + </SELECT> + </TD> + </TR> +% } + + +% if ( $communigate ) { + + <TR> + <TD ALIGN="right">Aliases</TD> + <TD><INPUT TYPE="text" NAME="cgp_aliases" VALUE="<% $svc_acct->cgp_aliases %>"></TD> + </TR> + +% } else { + <INPUT TYPE="hidden" NAME="cgp_aliases" VALUE="<% $svc_acct->cgp_aliases %>"> +% } + + +<% include('/elements/tr-select-svc_pbx.html', + 'curr_value' => $svc_acct->pbxsvc, + 'part_svc' => $part_svc, + 'cust_pkg' => $cust_pkg, + ) +%> + +%#pop +%my $popnum = $svc_acct->popnum || 0; +%if ( $part_svc->part_svc_column('popnum')->columnflag eq 'F' ) { +% + + + <INPUT TYPE="hidden" NAME="popnum" VALUE="<% $popnum %>"> +% } else { + + + <TR> + <TD ALIGN="right">Access number</TD> + <TD><% FS::svc_acct_pop::popselector($popnum) %></TD> + </TR> +% } +% #uid/gid +% foreach my $xid (qw( uid gid )) { +% +% if ( $part_svc->part_svc_column($xid)->columnflag =~ /^[FA]$/ +% || ! $conf->exists("svc_acct-edit_$xid") +% ) { +% +% if ( length($svc_acct->$xid()) ) { + + + <TR> + <TD ALIGN="right"><% uc($xid) %></TD> + <TD BGCOLOR="#eeeeee"><% $svc_acct->$xid() %></TD> + <TD> + </TD> + </TR> +% } + + + <INPUT TYPE="hidden" NAME="<% $xid %>" VALUE="<% $svc_acct->$xid() %>"> +% } else { + + + <TR> + <TD ALIGN="right"><% uc($xid) %></TD> + <TD> + <INPUT TYPE="text" NAME="<% $xid %>" SIZE=8 MAXLENGTH=6 VALUE="<% $svc_acct->$xid() %>"> + </TD> + </TR> +% } +% } +% +%#finger +%if ( $part_svc->part_svc_column('uid')->columnflag eq 'F' +% && ! $svc_acct->finger ) { +% + + + <INPUT TYPE="hidden" NAME="finger" VALUE=""> +% } else { + + + <TR> + <TD ALIGN="right">Real Name</TD> + <TD> + <INPUT TYPE="text" NAME="finger" VALUE="<% $svc_acct->finger %>"> + </TD> + </TR> +% } +% +%#dir +%if ( $part_svc->part_svc_column('dir')->columnflag eq 'F' +% || !$curuser->access_right('Edit home dir') +% ) { + + +<INPUT TYPE="hidden" NAME="dir" VALUE="<% $svc_acct->dir %>"> +% } else { + + + <TR> + <TD ALIGN="right">Home directory</TD> + <TD><INPUT TYPE="text" NAME="dir" VALUE="<% $svc_acct->dir %>"></TD> + </TR> +% } +% +%#shell +%my $shell = $svc_acct->shell; +%if ( $part_svc->part_svc_column('shell')->columnflag eq 'F' +% || ( !$shell && $part_svc->part_svc_column('uid')->columnflag eq 'F' ) +% ) { +% + + + <INPUT TYPE="hidden" NAME="shell" VALUE="<% $shell %>"> +% } else { + + + <TR> + <TD ALIGN="right">Shell</TD> + <TD> + <SELECT NAME="shell" SIZE=1> +% +% my($etc_shell); +% foreach $etc_shell (@shells) { +% + + + <OPTION<% $etc_shell eq $shell ? ' SELECTED' : '' %>><% $etc_shell %> +% } + + + </SELECT> + </TD> + </TR> +% } + + +% if ( $communigate +% && $part_svc->part_svc_column('cgp_type')->columnflag ne 'F' ) +% { + +% # settings + + <TR> + <TD ALIGN="right">Mailbox type</TD> + <TD> + <SELECT NAME="cgp_type"> +% foreach my $option (qw( MultiMailbox TextMailbox MailDirMailbox +% AGrade BGrade CGrade )) { + <OPTION VALUE="<% $option %>" + <% $option eq $svc_acct->cgp_type() ? 'SELECTED' : '' %> + ><% $option %> +% } + </SELECT> + </TD> + </TR> + +% } else { + <INPUT TYPE="hidden" NAME="cgp_type" VALUE="<% $svc_acct->cgp_type() %>"> +% } + + +% #false laziness w/svc_domain +% if ( $communigate +% && $part_svc->part_svc_column('cgp_accessmodes')->columnflag ne 'F' ) +% { + + <TR> + <TD ALIGN="right">Enabled services</TD> + <TD> + <% include( '/elements/communigate_pro-accessmodes.html', + 'curr_value' => $svc_acct->cgp_accessmodes, + ) + %> + </TD> + </TR> + +% } else { + <INPUT TYPE="hidden" NAME="cgp_accessmodes" VALUE="<% $svc_acct->cgp_accessmodes() |h %>"> +% } + + +% if ( $part_svc->part_svc_column('quota')->columnflag eq 'F' ) { + <INPUT TYPE="hidden" NAME="quota" VALUE="<% $svc_acct->quota %>"> +% } else { +% my $quota_label = $communigate ? 'Mail storage limit' : 'Quota'; + <TR> + <TD ALIGN="right"><% $quota_label %></TD> + <TD><INPUT TYPE="text" NAME="quota" VALUE="<% $svc_acct->quota %>"></TD> + </TR> +% } + +% tie my %cgp_label, 'Tie::IxHash', +% 'file_quota' => 'File storage limit', +% 'file_maxnum' => 'Number of files limit', +% 'file_maxsize' => 'File size limit', +% ; +% +% foreach my $key (keys %cgp_label) { +% +% if ( !$communigate || $part_svc->part_svc_column($key)->columnflag eq 'F' ){ + <INPUT TYPE="hidden" NAME="<%$key%>" VALUE="<% $svc_acct->$key() |h %>"> +% } else { + + <TR> + <TD ALIGN="right"><% $cgp_label{$key} %></TD> + <TD><INPUT TYPE="text" NAME="<% $key %>" VALUE="<% $svc_acct->$key() |h %>"></TD> + </TR> + +% } +% } + +% if ( $communigate ) { + + <% include('/elements/tr-checkbox.html', + 'label' => 'Password recovery', + 'field' => 'password_recover', + 'curr_value' => $svc_acct->password_recover, + 'value' => 'Y', + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Allowed mail rules', + 'field' => 'cgp_rulesallowed', + 'options' => [ '', 'No', 'Filter Only', 'All But Exec', 'Any' ], + 'labels' => { + '' => 'default (No)', #No always the default? + }, + 'curr_value' => $svc_acct->cgp_rulesallowed, + ) + %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'RPOP modifications', + 'field' => 'cgp_rpopallowed', + 'curr_value' => $svc_acct->cgp_rpopallowed, + 'value' => 'Y', + ) + %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'Accepts mail to "all"', + 'field' => 'cgp_mailtoall', + 'curr_value' => $svc_acct->cgp_mailtoall, + 'value' => 'Y', + ) + %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'Add trailer to sent mail', + 'field' => 'cgp_addmailtrailer', + 'curr_value' => $svc_acct->cgp_addmailtrailer, + 'value' => 'Y', + ) + %> + +% #preferences + +%# false laziness w/svc_domain acct_def + <TR> + <TD ALIGN="right">Message delete method</TD> + <TD> + <SELECT NAME="cgp_deletemode"> +% for ( 'Move To Trash', 'Immediately', 'Mark' ) { + <OPTION VALUE="<% $_ %>" + <% $_ eq $svc_acct->cgp_deletemode ? 'SELECTED' : '' %> + ><% $_ %> +% } + </SELECT> + </TD> + </TR> + + <% include('/elements/tr-select.html', + 'label' => 'On logout remove trash', + 'field' => 'cgp_emptytrash', + 'options' => $svc_acct->cgp_emptytrash_values, + 'labels' => { + '' => 'default (92 days)', #right? + }, + 'curr_value' => $svc_acct->cgp_emptytrash, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Language', + 'field' => 'cgp_language', + 'options' => [ '', qw( English Arabic Chinese Dutch French German Hebrew Italian Japanese Portuguese Russian Slovak Spanish Thai ) ], + 'labels' => { + '' => 'default (English)', + }, + 'curr_value' => $svc_acct->cgp_language, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Time zone', + 'field' => 'cgp_timezone', + 'options' => $svc_acct->cgp_timezone_values, + 'labels' => { + '' => 'default (HostOS)', + }, + 'curr_value' => $svc_acct->cgp_timezone, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Layout', + 'field' => 'cgp_skinname', + 'options' => [ '', '***', 'GoldFleece', 'Skin2' ], + 'labels' => { + '' => 'default (***)', + }, + 'curr_value' => $svc_acct->cgp_skinname, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Pronto style', + 'field' => 'cgp_prontoskinname', + 'options' => [ '', 'Pronto', 'Pronto-darkflame', 'Pronto-steel', 'Pronto-twilight', ], + + 'curr_value' => $svc_acct->cgp_prontoskinname, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Send read receipts', + 'field' => 'cgp_sendmdnmode', + 'options' => [ '', 'Never', 'Manually', 'Automatically' ], + 'labels' => { + '' => 'default (Automatically)', + }, + 'curr_value' => $svc_acct->cgp_language, + ) + %> + +%#XXX vacation message, redirect all mail, mail rules + +% } else { + +% for (qw( password_recover cgp_rulesallowed cgp_rpopallowed cgp_mailtoall +% cgp_addmailtrailer +% cgp_deletemode cgp_emptytrash cgp_language cgp_timezone +% cgp_skinname cgp_sendmdnmode +% ) ) { + + <INPUT TYPE="hidden" NAME="<% $_ %>" VALUE="<% $svc_acct->$_() %>"> +% } + +% } + + +% if ( $part_svc->part_svc_column('slipip')->columnflag =~ /^[FA]$/ ) { + <INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>"> +% } else { + <TR> + <TD ALIGN="right">IP</TD> + <TD><INPUT TYPE="text" NAME="slipip" VALUE="<% $svc_acct->slipip %>"></TD> + </TR> +% } + +% my %label = ( seconds => 'Time', +% upbytes => 'Upload bytes', +% downbytes => 'Download bytes', +% totalbytes => 'Total bytes', +% ); +% foreach my $uf (keys %label) { +% my $tf = $uf . "_threshold"; +% if ( $curuser->access_right('Edit usage') ) { + <TR> + <TD ALIGN="right"><% $label{$uf} %> remaining</TD> + <TD><INPUT TYPE="text" NAME="<% $uf %>" VALUE="<% $svc_acct->$uf %>">(blank disables)</TD> + </TR> + <TR> + <TD ALIGN="right"><% $label{$uf} %> threshold</TD> + <TD><INPUT TYPE="text" NAME="<% $tf %>" VALUE="<% $svc_acct->$tf %>">(blank disables)</TD> + </TR> +% }else{ + <INPUT TYPE="hidden" NAME="<% $uf %>" VALUE="<% $svc_acct->$uf %>"> + <INPUT TYPE="hidden" NAME="<% $tf %>" VALUE="<% $svc_acct->$tf %>"> +% } +% } +% +%foreach my $r ( grep { /^r(adius|[cr])_/ } fields('svc_acct') ) { +% $r =~ /^^r(adius|[cr])_(.+)$/ or next; #? +% my $a = $2; +% +% if ( $part_svc->part_svc_column($r)->columnflag =~ /^[FA]$/ ) { + + + <INPUT TYPE="hidden" NAME="<% $r %>" VALUE="<% $svc_acct->getfield($r) %>"> +% } else { + + + <TR> + <TD ALIGN="right"><% $FS::raddb::attrib{$a} %></TD> + <TD><INPUT TYPE="text" NAME="<% $r %>" VALUE="<% $svc_acct->getfield($r) %>"></TD> + </TR> +% } +% } + + + +<TR> + <TD ALIGN="right">RADIUS groups</TD> +% if ( $part_svc->part_svc_column('usergroup')->columnflag eq 'F' ) { + + + <TD BGCOLOR="#eeeeee"><% join('<BR>', @groups) %></TD> +% } else { + + + <TD><% FS::svc_acct::radius_usergroup_selector( \@groups ) %></TD> +% } + + +</TR> +% foreach my $field ($svc_acct->virtual_fields) { +% # If the flag is X, it won't even show up in $svc_acct->virtual_fields. +% if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) { + + + <% $svc_acct->pvf($field)->widget('HTML', 'edit', $svc_acct->getfield($field)) %> +% } +% } + + +</TABLE> +<BR> + +<INPUT TYPE="submit" VALUE="Submit"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my $conf = new FS::Conf; +my @shells = $conf->config('shells'); + +my $curuser = $FS::CurrentUser::CurrentUser; + +my($svcnum, $pkgnum, $svcpart, $part_svc, $svc_acct, @groups); +if ( $cgi->param('error') ) { + + $svc_acct = new FS::svc_acct ( { + map { $_, scalar($cgi->param($_)) } fields('svc_acct') + } ); + $svcnum = $svc_acct->svcnum; + $pkgnum = $cgi->param('pkgnum'); + $svcpart = $cgi->param('svcpart'); + $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } ); + die "No part_svc entry for svcpart $svcpart!" unless $part_svc; + @groups = $cgi->param('radius_usergroup'); + +} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding + + $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; + $pkgnum = $1; + $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; + $svcpart = $1; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $svc_acct = new FS::svc_acct({svcpart => $svcpart}); + + $svcnum=''; + + $svc_acct->password_recover('Y'); #default. hmm. + +} else { #editing + + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die "unparsable svcnum"; + $svcnum=$1; + $svc_acct=qsearchs('svc_acct',{'svcnum'=>$svcnum}) + or die "Unknown (svc_acct) svcnum!"; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum=$cust_svc->pkgnum; + $svcpart=$cust_svc->svcpart; + + $part_svc = qsearchs( 'part_svc', { 'svcpart' => $svcpart } ); + die "No part_svc entry for svcpart $svcpart!" unless $part_svc; + + @groups = $svc_acct->radius_groups; + +} + +my $communigate = scalar($part_svc->part_export('communigate_pro')); + # || scalar($part_svc->part_export('communigate_pro_singledomain')); + +my( $cust_pkg, $cust_main ) = ( '', '' ); +if ( $pkgnum ) { + $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $pkgnum } ); + $cust_main = $cust_pkg->cust_main; +} + +unless ( $svcnum || $cgi->param('error') ) { #adding + + #set gecos + if ($cust_main) { + unless ( $part_svc->part_svc_column('uid')->columnflag eq 'F' ) { + $svc_acct->setfield('finger', + $cust_main->getfield('first') . " " . $cust_main->getfield('last') + ); + } + } + + $svc_acct->set_default_and_fixed( { + #false laziness w/svc-acct::_fieldhandlers + 'usergroup' => sub { + my( $self, $groups ) = @_; + if ( ref($groups) eq 'ARRAY' ) { + @groups = @$groups; + $groups; + } elsif ( length($groups) ) { + @groups = split(/\s*,\s*/, $groups); + [ @groups ]; + } else { + @groups = (); + []; + } + } + } ); + +} + +#fixed radius groups always override & display +if ( $part_svc->part_svc_column('usergroup')->columnflag eq 'F' ) { + @groups = split(',', $part_svc->part_svc_column('usergroup')->columnvalue); +} + +my $action = $svcnum ? 'Edit' : 'Add'; + +my $svc = $part_svc->getfield('svc'); + +my $otaker = getotaker; + +my $username = $svc_acct->username; + +my $password = ''; +if ( $cgi->param('error') ) { + $password = $cgi->param('clear_password'); +} elsif ( $svcnum ) { + my $password_encryption = $svc_acct->_password_encryption; + if ( $password = $svc_acct->get_cleartext_password ) { + $password = '*HIDDEN*' unless $conf->exists('showpasswords'); + } elsif( $svc_acct->_password and $password_encryption ne 'plain' ) { + $password = "(".uc($password_encryption)." encrypted)"; + } +} + +my $ulen = + $conf->exists('usernamemax') + ? $conf->config('usernamemax') + : dbdef->table('svc_acct')->column('username')->length; +my $ulen2 = $ulen+2; + +my $pmax = max($conf->config('passwordmax') || 13); +my $pmax2 = $pmax+2; + +my $p1 = popurl(1); + +sub max { + (sort(@_))[-1] +} + +</%init> diff --git a/httemplate/edit/svc_acct_pop.cgi b/httemplate/edit/svc_acct_pop.cgi new file mode 100755 index 000000000..5930a38be --- /dev/null +++ b/httemplate/edit/svc_acct_pop.cgi @@ -0,0 +1,53 @@ +<% include('/elements/header.html', "$action Access Number", menubar( + 'View all Access Numbers' => popurl(2). "browse/svc_acct_pop.cgi", + )) +%> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/svc_acct_pop.cgi" METHOD=POST> + +<INPUT TYPE="hidden" NAME="popnum" VALUE="<% $hashref->{popnum} %>"> +Access Number #<% $hashref->{popnum} ? $hashref->{popnum} : "(NEW)" %> + +<PRE> +City <INPUT TYPE="text" NAME="city" SIZE=32 VALUE="<% $hashref->{city} %>"> +State <INPUT TYPE="text" NAME="state" SIZE=16 MAXLENGTH=16 VALUE="<% $hashref->{state} %>"> +Area Code <INPUT TYPE="text" NAME="ac" SIZE=4 MAXLENGTH=3 VALUE="<% $hashref->{ac} %>"> +Exchange <INPUT TYPE="text" NAME="exch" SIZE=4 MAXLENGTH=3 VALUE="<% $hashref->{exch} %>"> +Local <INPUT TYPE="text" NAME="loc" SIZE=5 MAXLENGTH=4 VALUE="<% $hashref->{loc} %>"> +</PRE> + +<BR> +<INPUT TYPE="submit" VALUE="<% $hashref->{popnum} ? "Apply changes" : "Add Access Number" %>"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Dialup configuration') + || $curuser->access_right('Dialup global configuration'); + +my $svc_acct_pop; +if ( $cgi->param('error') ) { + $svc_acct_pop = new FS::svc_acct_pop ( { + map { $_, scalar($cgi->param($_)) } fields('svc_acct_pop') + } ); +} elsif ( $cgi->keywords ) { #editing + my($query)=$cgi->keywords; + $query =~ /^(\d+)$/; + $svc_acct_pop=qsearchs('svc_acct_pop',{'popnum'=>$1}); +} else { #adding + $svc_acct_pop = new FS::svc_acct_pop {}; +} +my $action = $svc_acct_pop->popnum ? 'Edit' : 'Add'; +my $hashref = $svc_acct_pop->hashref; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi new file mode 100644 index 000000000..b11fec7a7 --- /dev/null +++ b/httemplate/edit/svc_broadband.cgi @@ -0,0 +1,107 @@ +<% include('elements/svc_Common.html', + 'post_url' => popurl(1). 'process/svc_broadband.cgi', + 'name' => 'broadband service', + 'table' => 'svc_broadband', + 'labels' => { 'svcnum' => 'Service', + 'description' => 'Description', + 'ip_addr' => 'IP address', + 'speed_down' => 'Download speed', + 'speed_up' => 'Upload speed', + 'blocknum' => 'Router/Block', + 'block_label' => 'Router/Block', + 'mac_addr' => 'MAC address', + 'latitude' => 'Latitude', + 'longitude' => 'Longitude', + 'altitude' => 'Altitude', + 'vlan_profile' => 'VLAN profile', + 'performance_profile' => 'Performance profile', + 'authkey' => 'Authentication key', + }, + 'fields' => \@fields, + 'field_callback' => $callback, + 'dummy' => $cgi->query_string, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +# If it's stupid but it works, it's still stupid. +# -Kristian + +my $conf = new FS::Conf; + +my @fields = ( + qw( description ip_addr speed_down speed_up blocknum ), + { field=>'block_label', type=>'fixed' }, + qw( mac_addr latitude longitude altitude vlan_profile performance_profile authkey ) +); + +my $fixedblock = ''; + +my $callback = sub { + my ($cgi, $object, $fieldref) = @_; + + my $svcpart = $object->svcnum ? $object->cust_svc->svcpart + : $cgi->param('svcpart'); + + my $part_svc = qsearchs( 'part_svc', { svcpart => $svcpart } ); + die "No part_svc entry!" unless $part_svc; + + my $columndef = $part_svc->part_svc_column($fieldref->{'field'}); + if ($columndef->columnflag eq 'F') { + $fieldref->{'type'} = 'fixed'; + $fieldref->{'value'} = $columndef->columnvalue; + $fixedblock = $fieldref->{value} + if $fieldref->{field} eq 'blocknum'; + } + + if ($object->svcnum) { + + $fieldref->{type} = 'hidden' + if $fieldref->{field} eq 'blocknum'; + + $fieldref->{value} = $object->addr_block->label + if $fieldref->{field} eq 'block_label' && $object->addr_block; + + } else { + + if ($fieldref->{field} eq 'block_label') { + if ($fixedblock && $object->addr_block) { + $object->blocknum($fixedblock); + $fieldref->{value} = $object->addr_block->label; + }else{ + $fieldref->{type} = 'hidden'; + } + } + + if ($fieldref->{field} eq 'blocknum') { + if ( $fixedblock or $conf->exists('auto_router') ) { + $fieldref->{type} = 'hidden'; + $fieldref->{value} = $fixedblock; + return; + } + + my $cust_pkg = qsearchs( 'cust_pkg', {pkgnum => $cgi->param('pkgnum')} ); + die "No cust_pkg entry!" unless $cust_pkg; + + $object->svcpart($part_svc->svcpart); + my @addr_block = + grep { ! $_->agentnum + || $cust_pkg->cust_main->agentnum == $_->agentnum + && $FS::CurrentUser::CurrentUser->agentnum($_->agentnum) + } + map { $_->addr_block } $object->allowed_routers; + my @options = map { $_->blocknum } + sort { $a->label cmp $b->label } @addr_block; + my %option_labels = map { ( $_->blocknum => $_->label ) } @addr_block; + $fieldref->{type} = 'select'; + $fieldref->{options} = \@options; + $fieldref->{labels} = \%option_labels; + } + + } +}; + +</%init> diff --git a/httemplate/edit/svc_domain.cgi b/httemplate/edit/svc_domain.cgi new file mode 100755 index 000000000..5abe9d6b5 --- /dev/null +++ b/httemplate/edit/svc_domain.cgi @@ -0,0 +1,386 @@ +<% include('/elements/header.html', "$action $svc", '') %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/svc_domain.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>"> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>"> + +<% ntable("#cccccc",2) %> + +<TR> + <TD ALIGN="right">Domain</TD> + <TD> +% if ( !$svcnum || $conf->exists('svc_domain-edit_domain') ) { + <INPUT TYPE="text" NAME="domain" VALUE="<% $domain %>" SIZE=28 MAXLENGTH=63> +% } else { + <B><% $domain %></B> +% } + +% if ($export) { +<BR> +Available top-level domains: <% $export->option('tlds') %> +</TR> + +<TR> +<INPUT TYPE="radio" NAME="action" VALUE="N"<% $kludge_action eq 'N' ? ' CHECKED' : '' %>>Register at <% $registrar->{'name'} %> +<BR> + +<INPUT TYPE="radio" NAME="action" VALUE="M"<% $kludge_action eq 'M' ? ' CHECKED' : '' %>>Transfer to <% $registrar->{'name'} %> +<BR> + +<INPUT TYPE="radio" NAME="action" VALUE="I"<% $kludge_action eq 'I' ? ' CHECKED' : '' %>>Registered elsewhere + +</TR> + +% } + </TD> +</TR> + +% if ( $communigate ) { + <TR> + <TD ALIGN="right">Administrator domain</TD> + <TD> + <% include('/elements/select-domain.html', + 'element_name' => 'parent_svcnum', + 'curr_value' => $svc_domain->parent_svcnum, + 'empty_label' => '(none)', + ) + %> + </TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="parent_svcnum" VALUE="<% $svc_domain->parent_svcnum %>"> +% } + +% if ( $communigate +% && $part_svc->part_svc_column('cgp_aliases')->columnflag !~ /^[FA]$/ ) { + + <TR> + <TD ALIGN="right">Aliases</TD> + <TD><INPUT TYPE="text" NAME="cgp_aliases" VALUE="<% $svc_domain->cgp_aliases %>"></TD> + </TR> + +% } else { + <INPUT TYPE="hidden" NAME="cgp_aliases" VALUE="<% $svc_domain->cgp_aliases %>"> +% } + +% if ( $part_svc->part_svc_column('max_accounts')->columnflag =~ /^[FA]$/ ) { + <INPUT TYPE="hidden" NAME="max_accounts" VALUE="<% $svc_domain->max_accounts %>"> +% } else { + <TR> + <TD ALIGN="right">Maximum number of accounts</TD> + <TD> + <INPUT TYPE="text" NAME="max_accounts" SIZE=5 MAXLENGTH=6 VALUE="<% $svc_domain->max_accounts %>"> + </TD> + </TR> +% } + +% if ( $communigate +% && $part_svc->part_svc_column('cgp_accessmodes')->columnflag ne 'F' ) +% { + + <TR> + <TD ALIGN="right">Enabled services</TD> + <TD> + <% include( '/elements/communigate_pro-accessmodes.html', + 'curr_value' => $svc_domain->cgp_accessmodes, + ) + %> + </TD> + </TR> + +% } else { + <INPUT TYPE="hidden" NAME="cgp_accessmodes" VALUE="<% $svc_domain->cgp_accessmodes() |h %>"> +% } + +% if ( $communigate +% && $part_svc->part_svc_column('trailer')->columnflag ne 'F' ) +% { + + <TR> + <TD ALIGN="right">Mail trailer</TD> + <TD> + <TEXTAREA NAME="trailer" ROWS=5 COLS=60><% $svc_domain->trailer() |h %></TEXTAREA> + </TD> + </TR> + +% } else { + <INPUT TYPE="hidden" NAME="trailer" VALUE="<% $svc_domain->trailer() |h %>"> +% } + + +</TABLE> +<BR> + +% if ( $communigate ) { + +Account defaults +<% ntable("#cccccc",2) %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'Password modification', + 'field' => 'acct_def_password_selfchange', + 'curr_value' => $svc_domain->acct_def_password_selfchange, + 'value' => 'Y', + ) + %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'Password recovery', + 'field' => 'acct_def_password_recover', + 'curr_value' => $svc_domain->acct_def_password_recover, + 'value' => 'Y', + ) + %> + + <TR> + <TD ALIGN="right">Enabled services + </TD> + <TD><% include('/elements/communigate_pro-accessmodes.html', + 'element_name_prefix' => 'acct_def_cgp_accessmodes_', + 'curr_value' => $svc_domain->acct_def_cgp_accessmodes, + ) + %> + </TD> + </TR> + + <% include('/elements/tr-input-text.html', + 'label' => 'Mail storage limit', + 'field' => 'acct_def_quota', + 'curr_value' => $svc_domain->acct_def_quota, + ) + %> + <% include('/elements/tr-input-text.html', + 'label' => 'File storage limit', + 'field' => 'acct_def_file_quota', + 'curr_value' => $svc_domain->acct_def_file_quota, + ) + %> + <% include('/elements/tr-input-text.html', + 'label' => 'Files limit', + 'field' => 'acct_def_file_maxnum', + 'curr_value' => $svc_domain->acct_def_file_maxnum, + ) + %> + <% include('/elements/tr-input-text.html', + 'label' => 'File size limit', + 'field' => 'acct_def_file_maxsize', + 'curr_value' => $svc_domain->acct_def_file_maxsize, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Allowed mail rules', + 'field' => 'acct_def_cgp_rulesallowed', + 'options' => [ '', 'No', 'Filter Only', 'All But Exec', 'Any' ], + 'labels' => { + '' => 'default (No)', #No always the default? + }, + 'curr_value' => $svc_domain->acct_def_cgp_rulesallowed, + ) + %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'RPOP modifications', + 'field' => 'acct_def_cgp_rpopallowed', + 'curr_value' => $svc_domain->acct_def_cgp_rpopallowed, + 'value' => 'Y', + ) + %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'Accepts mail to "all"', + 'field' => 'acct_def_cgp_mailtoall', + 'curr_value' => $svc_domain->acct_def_cgp_mailtoall, + 'value' => 'Y', + ) + %> + + <% include('/elements/tr-checkbox.html', + 'label' => 'Add trailer to sent mail', + 'field' => 'acct_def_cgp_addmailtrailer', + 'curr_value' => $svc_domain->acct_def_cgp_addmailtrailer, + 'value' => 'Y', + ) + %> + +%# false laziness w/svc_acct acct_def + <TR> + <TD ALIGN="right">Message delete method</TD> + <TD> + <SELECT NAME="acct_def_cgp_deletemode"> +% for ( 'Move To Trash', 'Immediately', 'Mark' ) { + <OPTION VALUE="<% $_ %>" + <% $_ eq $svc_domain->acct_def_cgp_deletemode ? 'SELECTED' : '' %> + ><% $_ %> +% } + </SELECT> + </TD> + </TR> + + <% include('/elements/tr-select.html', + 'label' => 'On logout remove trash', + 'field' => 'acct_def_cgp_emptytrash', + 'options' => $svc_domain->cgp_emptytrash_values, + 'labels' => { + '' => 'default (92 days)', #right? + }, + 'curr_value' => $svc_domain->acct_def_cgp_emptytrash, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Language', + 'field' => 'acct_def_cgp_language', + 'options' => [ '', qw( English Arabic Chinese Dutch French German Hebrew Italian Japanese Portuguese Russian Slovak Spanish Thai ) ], + 'labels' => { + '' => 'default (English)', + }, + 'curr_value' => $svc_domain->acct_def_cgp_language, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Time zone', + 'field' => 'acct_def_cgp_timezone', + 'options' => $svc_domain->cgp_timezone_values, + 'labels' => { + '' => 'default (HostOS)', + }, + 'curr_value' => $svc_domain->acct_def_cgp_timezone, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Layout', + 'field' => 'acct_def_cgp_skinname', + 'options' => [ '', '***', 'GoldFleece', 'Skin2' ], + 'labels' => { + '' => 'default (***)', + }, + 'curr_value' => $svc_domain->acct_def_cgp_skinname, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Pronto style', + 'field' => 'acct_def_cgp_prontoskinname', + 'options' => [ '', 'Pronto', 'Pronto-darkflame', 'Pronto-steel', 'Pronto-twilight', ], + 'curr_value' => $svc_domain->acct_def_cgp_prontoskinname, + ) + %> + + <% include('/elements/tr-select.html', + 'label' => 'Send read receipts', + 'field' => 'acct_def_cgp_sendmdnmode', + 'options' => [ '', 'Never', 'Manually', 'Automatically' ], + 'labels' => { + '' => 'default (Automatically)', + }, + 'curr_value' => $svc_domain->acct_def_cgp_language, + ) + %> + +% #XXX rules, archive rule, spam foldering rule(s) + +</TABLE> +<BR> + +% } else { + +% foreach my $f (qw( password_selfchange password_recover cgp_accessmodes +% quota file_quota file_maxnum file_maxsize +% cgp_rulesallowed cgp_rpopallowed cgp_mailtoall +% cgp_addmailtrailer +% cgp_deletemode cgp_emptytrash cgp_language +% cgp_timezone cgp_skinname cgp_sendmdnmode +% )) { + <INPUT TYPE="hidden" NAME="acct_def_<%$f%>" VALUE="<% $svc_domain->get("acct_def_$f") %>"> +% } + +% } + +<INPUT TYPE="submit" VALUE="Submit"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my $conf = new FS::Conf; + +my($svcnum, $pkgnum, $svcpart, $kludge_action, $part_svc, + $svc_domain); +if ( $cgi->param('error') ) { + + $svc_domain = new FS::svc_domain ( { + map { $_, scalar($cgi->param($_)) } fields('svc_domain') + } ); + $svcnum = $svc_domain->svcnum; + $pkgnum = $cgi->param('pkgnum'); + $svcpart = $cgi->param('svcpart'); + $kludge_action = $cgi->param('action'); + $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } ); + die "No part_svc entry!" unless $part_svc; + +} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding + + $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; + $pkgnum = $1; + $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; + $svcpart = $1; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $svc_domain = new FS::svc_domain({}); + + $svcnum=''; + + $svc_domain->set_default_and_fixed; + +} else { #editing + + $kludge_action = ''; + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die "unparsable svcnum"; + $svcnum=$1; + $svc_domain=qsearchs('svc_domain',{'svcnum'=>$svcnum}) + or die "Unknown (svc_domain) svcnum!"; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum=$cust_svc->pkgnum; + $svcpart=$cust_svc->svcpart; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + +} +my $action = $svcnum ? 'Edit' : 'Add'; + +my $svc = $part_svc->getfield('svc'); + +my $communigate = scalar($part_svc->part_export('communigate_pro')); + # || scalar($part_svc->part_export('communigate_pro_singledomain')); + +# Find the first export that does domain registration +my @exports = grep $_->can('registrar'), $part_svc->part_export; +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); + +</%init> diff --git a/httemplate/edit/svc_external.cgi b/httemplate/edit/svc_external.cgi new file mode 100644 index 000000000..54aa11f42 --- /dev/null +++ b/httemplate/edit/svc_external.cgi @@ -0,0 +1 @@ +<% include( 'elements/svc_Common.html', 'table'=>'svc_external' ) %> diff --git a/httemplate/edit/svc_forward.cgi b/httemplate/edit/svc_forward.cgi new file mode 100755 index 000000000..73f6465b9 --- /dev/null +++ b/httemplate/edit/svc_forward.cgi @@ -0,0 +1,186 @@ +<% include('/elements/header.html', "Mail Forward $action") %> + +<% include('/elements/error.html') %> + +Service #<% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR> +Service: <B><% $part_svc->svc %></B><BR><BR> + +<FORM ACTION="process/svc_forward.cgi" METHOD="POST"> +<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>"> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>"> + +<SCRIPT TYPE="text/javascript"> +function srcchanged(what) { + if ( what.options[what.selectedIndex].value == 0 ) { + what.form.src.disabled = false; + what.form.src.style.backgroundColor = "white"; + } else { + what.form.src.disabled = true; + what.form.src.style.backgroundColor = "lightgrey"; + } +} +function dstchanged(what) { + if ( what.options[what.selectedIndex].value == 0 ) { + what.form.dst.disabled = false; + what.form.dst.style.backgroundColor = "white"; + } else { + what.form.dst.disabled = true; + what.form.dst.style.backgroundColor = "lightgrey"; + } +} +</SCRIPT> + +<% ntable("#cccccc",2) %> + +<TR> + <TD ALIGN="right">Email to</TD> + <TD> +% if ( $conf->exists('svc_forward-no_srcsvc') ) { + <INPUT NAME="srcsrc" TYPE="hidden" VALUE="0"> +% } else { + <SELECT NAME="srcsvc" SIZE=1 onChange="srcchanged(this)"> +% foreach $_ (keys %email) { + <OPTION VALUE="<% $_ %>" + <% $_ eq $srcsvc ? 'SELECTED' : '' %> + ><% $email{$_} %></OPTION> +% } + <OPTION VALUE="0" <% $src ? 'SELECTED' : '' %> + >(other email address)</OPTION> + </SELECT> +% } + +% my $src_disabled = $src +% || $conf->exists('svc_forward-no_srcsvc') +% || !scalar(%email); + <INPUT NAME = "src" + TYPE = "text" + VALUE = "<% $src %>" + <% $src_disabled ? '' : 'DISABLED STYLE="background-color: lightgrey"' %> + > + + </TD> +</TR> + +<TR><TD ALIGN="right">Forwards to</TD> +<TD><SELECT NAME="dstsvc" SIZE=1 onChange="dstchanged(this)"> +% foreach $_ (keys %email) { + + <OPTION<% $_ eq $dstsvc ? " SELECTED" : "" %> VALUE="<% $_ %>"><% $email{$_} %></OPTION> +% } + +<OPTION <% $dst ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION> +</SELECT> +<INPUT TYPE="text" NAME="dst" VALUE="<% $dst %>" <% ( $dst || !scalar(%email) ) ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>> +</TD></TR> + </TABLE> +<BR><INPUT TYPE="submit" VALUE="Submit"> +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my $conf = new FS::Conf; + +my($svcnum, $pkgnum, $svcpart, $part_svc, $svc_forward); +if ( $cgi->param('error') ) { + $svc_forward = new FS::svc_forward ( { + map { $_, scalar($cgi->param($_)) } fields('svc_forward') + } ); + $svcnum = $svc_forward->svcnum; + $pkgnum = $cgi->param('pkgnum'); + $svcpart = $cgi->param('svcpart'); + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + +} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding + + $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; + $pkgnum = $1; + $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; + $svcpart = $1; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $svc_forward = new FS::svc_forward({}); + + $svcnum=''; + + $svc_forward->set_default_and_fixed; + +} else { #editing + + my($query) = $cgi->keywords; + + $query =~ /^(\d+)$/ or die "unparsable svcnum"; + $svcnum=$1; + $svc_forward=qsearchs('svc_forward',{'svcnum'=>$svcnum}) + or die "Unknown (svc_forward) svcnum!"; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum=$cust_svc->pkgnum; + $svcpart=$cust_svc->svcpart; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + +} +my $action = $svc_forward->svcnum ? 'Edit' : 'Add'; + +my %email; + +#starting with those currently attached +foreach my $method (qw( srcsvc_acct dstsvc_acct )) { + my $svc_acct = $svc_forward->$method(); + $email{$svc_acct->svcnum} = $svc_acct->email if $svc_acct; +} + +if ($pkgnum) { + + #find all possible user svcnums (and emails) + + #and including the rest for this customer + my($u_part_svc,@u_acct_svcparts); + foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) { + push @u_acct_svcparts,$u_part_svc->getfield('svcpart'); + } + + my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + my($custnum)=$cust_pkg->getfield('custnum'); + my($i_cust_pkg); + foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) { + my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum'); + my($acct_svcpart); + foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding + #record(s) in cust_svc ( for this + #pkgnum ! ) + foreach my $i_cust_svc ( + qsearch( 'cust_svc', { 'pkgnum' => $cust_pkgnum, + 'svcpart' => $acct_svcpart } ) + ) { + my $svc_acct = + qsearchs( 'svc_acct', { 'svcnum' => $i_cust_svc->svcnum } ); + $email{$svc_acct->svcnum} = $svc_acct->email; + } + } + } + +} elsif ( $action eq 'Add' ) { + die "\$action eq Add, but \$pkgnum is null!\n"; +} + +my($srcsvc,$dstsvc,$dst)=( + $svc_forward->srcsvc, + $svc_forward->dstsvc, + $svc_forward->dst, +); +my $src = $svc_forward->dbdef_table->column('src') ? $svc_forward->src : ''; + +</%init> diff --git a/httemplate/edit/svc_mailinglist.cgi b/httemplate/edit/svc_mailinglist.cgi new file mode 100644 index 000000000..c7c739daa --- /dev/null +++ b/httemplate/edit/svc_mailinglist.cgi @@ -0,0 +1,25 @@ +<% include( 'elements/svc_Common.html', + 'table' => 'svc_mailinglist', + 'fields' => \@fields, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my @fields = ( + 'username', + { field=>'domsvc', type=>'select-svc-domain', + #label => 'List address domain', + }, + { field=>'listnum', type=>'hidden', }, + { field=>'listname', type=>'text', }, + { field=>'reply_to', type=>'checkbox', value=>'Y' }, + { field=>'remove_from', type=>'checkbox', value=>'Y' }, + { field=>'reject_auto', type=>'checkbox', value=>'Y' }, + { field=>'remove_to_and_cc', type=>'checkbox', value=>'Y' }, + +); + +</%init> diff --git a/httemplate/edit/svc_phone.cgi b/httemplate/edit/svc_phone.cgi new file mode 100644 index 000000000..b77d96233 --- /dev/null +++ b/httemplate/edit/svc_phone.cgi @@ -0,0 +1,77 @@ +<% include( 'elements/svc_Common.html', + 'table' => 'svc_phone', + 'fields' => \@fields, + 'labels' => { + 'svcnum' => 'Service', + 'countrycode' => 'Country code', + 'phonenum' => 'Phone number', + 'domsvc' => 'Domain', + 'sip_password' => 'SIP password', + 'pin' => 'Voicemail PIN', + 'phone_name' => 'Name', + 'pbxsvc' => 'PBX', + 'locationnum' => 'E911 location', + }, + 'svc_new_callback' => sub { + my( $cgi, $svc_x, $part_svc, $cust_pkg, $fields, $opt ) = @_; + $svc_x->locationnum($cust_pkg->locationnum) if $cust_pkg; + }, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my $conf = new FS::Conf; + +my @fields = ( 'countrycode', + { field => 'phonenum', + type => 'select-did', + label => 'Phone number', + }, + ); + +push @fields, { field => 'domsvc', + type => 'select-svc-domain', + label => 'Domain', + } + if $conf->exists('svc_phone-domain'); + +push @fields, { field => 'pbxsvc', + type => 'select-svc_pbx', + label => 'PBX', + }, + 'sip_password', + 'pin', + { field => 'phone_name', + type => 'text', + maxlength => $conf->config('svc_phone-phone_name-max_length'), + }, + + { value => 'E911 Information', + type => 'tablebreak-tr-title', + colspan => 7, + }, + { field => 'locationnum', + type => 'select-cust_location', + label => 'E911 location', + include_opt_callback => sub { + my $svc_phone = shift; + my $pkgnum = $svc_phone->get('pkgnum') + || $cgi->param('pkgnum') + || $svc_phone->cust_svc->pkgnum; #hua? + #cross agent location exposure? sheesh + my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum}); + my $cust_main = $cust_pkg ? $cust_pkg->cust_main : ''; + ( 'no_bold' => 1, + 'cust_pkg' => $cust_pkg, + 'cust_main' => $cust_main, + ); + }, + }, + { field => 'custnum', type=> 'hidden' }, #for new cust_locations +; + + +</%init> diff --git a/httemplate/edit/svc_www.cgi b/httemplate/edit/svc_www.cgi new file mode 100644 index 000000000..cd4db7545 --- /dev/null +++ b/httemplate/edit/svc_www.cgi @@ -0,0 +1,240 @@ +<% include('/elements/header.html', "Web Hosting $action") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/svc_www.cgi" METHOD=POST> + +<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>"> +Service #<B><% $svcnum ? $svcnum : "(NEW)" %></B> +<BR><BR> + +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> + +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>"> + +% my $recnum = $svc_www->recnum; +% my $usersvc = $svc_www->usersvc; + +<% &ntable("#cccccc",2) %> + + <TR> + <TD ALIGN="right">Zone</TD> + <TD> + <SELECT NAME="recnum" SIZE=1> +% foreach $_ (keys %arec) { + <OPTION<% $_ eq $recnum ? " SELECTED" : "" %> VALUE="<%$_%>"><%$arec{$_}%> +% } + </SELECT> + </TD> + </TR> + +% if ( $part_svc->part_svc_column('usersvc')->columnflag ne 'F' +% || $part_svc->part_svc_column('usersvc')->columnvalue !~ /^\s*$/) { + <TR> + <TD ALIGN="right">Username</TD> + <TD> + <SELECT NAME="usersvc" SIZE=1> + <OPTION VALUE="">(none) +% foreach $_ (keys %svc_acct) { + <OPTION<% ($_ eq $usersvc) ? " SELECTED" : "" %> VALUE="<%$_%>"><% $svc_acct{$_} %> +% } + </SELECT> + </TD> + </TR> +% } + +% if ( $part_svc->part_svc_column('config')->columnflag ne 'F' && +% $FS::CurrentUser::CurrentUser->access_right('Edit www config') ) { + <TR> + <TD ALIGN="right">Config lines</TD> + <TD> + <TEXTAREA NAME="config" rows="15" cols="80"><% $config |h %></TEXTAREA> + </TD> + </TR> +% } else { + <INPUT TYPE="hidden" NAME="config" VALUE="<% $config |h %>"> +%} + +% foreach my $field ($svc_www->virtual_fields) { +% if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) { +% # If the flag is X, it won't even show up in $svc_acct->virtual_fields. + <% $svc_www->pvf($field)->widget( 'HTML', 'edit', + $svc_www->getfield($field) + ) + %> +% } +% } + +</TABLE> +<BR> + +<INPUT TYPE="submit" VALUE="Submit"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my $conf = new FS::Conf; + +my( $svcnum, $pkgnum, $svcpart, $part_svc, $svc_www, $config ); + +if ( $cgi->param('error') ) { + + $svc_www = new FS::svc_www ( { + map { $_, scalar($cgi->param($_)) } fields('svc_www') + } ); + $svcnum = $svc_www->svcnum; + $pkgnum = $cgi->param('pkgnum'); + $svcpart = $cgi->param('svcpart'); + $config = $cgi->param('config'); + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + +} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding + + $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; + $pkgnum = $1; + $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; + $svcpart = $1; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + + $svc_www = new FS::svc_www { svcpart => $svcpart }; + + $svcnum=''; + + $svc_www->set_default_and_fixed; + +} else { #editing + + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die "unparsable svcnum"; + $svcnum=$1; + $svc_www=qsearchs('svc_www',{'svcnum'=>$svcnum}) + or die "Unknown (svc_www) svcnum!"; + + my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) + or die "Unknown (cust_svc) svcnum!"; + + $pkgnum = $cust_svc->pkgnum; + $svcpart = $cust_svc->svcpart; + $config = $svc_www->config; + + $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); + die "No part_svc entry!" unless $part_svc; + +} +my $action = $svc_www->svcnum ? 'Edit' : 'Add'; + +my( %svc_acct, %arec ); +if ($pkgnum) { + + my @u_acct_svcparts; + foreach my $svcpart ( + map { $_->svcpart } qsearch( 'part_svc', { 'svcdb' => 'svc_acct' } ) + ) { + next if $conf->exists('svc_www-usersvc_svcpart') + && ! grep { $svcpart == $_ } + $conf->config('svc_www-usersvc_svcpart'); + push @u_acct_svcparts, $svcpart; + } + + my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + my($custnum)=$cust_pkg->getfield('custnum'); + my($i_cust_pkg); + foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) { + my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum'); + my($acct_svcpart); + foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding + #record(s) in cust_svc ( for this + #pkgnum ! ) + my($i_cust_svc); + foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) { + my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')}); + $svc_acct{$svc_acct->getfield('svcnum')}= + $svc_acct->cust_svc->part_svc->svc. ': '. $svc_acct->email; + } + } + } + + + my($d_part_svc,@d_acct_svcparts); + foreach $d_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_domain'}) ) { + push @d_acct_svcparts,$d_part_svc->getfield('svcpart'); + } + + foreach $i_cust_pkg ( qsearch( 'cust_pkg', { 'custnum' => $custnum } ) ) { + my $cust_pkgnum = $i_cust_pkg->pkgnum; + + foreach my $acct_svcpart (@d_acct_svcparts) { + + foreach my $i_cust_svc ( + qsearch( 'cust_svc', { 'pkgnum' => $cust_pkgnum, + 'svcpart' => $acct_svcpart } ) + ) { + my $svc_domain = + qsearchs( 'svc_domain', { 'svcnum' => $i_cust_svc->svcnum } ); + + my $extra_sql = "AND ( rectype = 'A' OR rectype = 'CNAME' )"; + unless ( $conf->exists('svc_www-enable_subdomains') ) { + $extra_sql .= " AND ( reczone = '\@' OR reczone = '". + $svc_domain->domain. ".' )"; + } + + foreach my $domain_rec ( + qsearch( 'domain_record', + { + 'svcnum' => $svc_domain->svcnum, + }, + '', + $extra_sql, + ) + ) { + $arec{$domain_rec->recnum} = $domain_rec->zone; + } + + if ( $conf->exists('svc_www-enable_subdomains') ) { + $arec{'www.'. $svc_domain->domain} = 'www.'. $svc_domain->domain + unless qsearchs( 'domain_record', { + svcnum => $svc_domain->svcnum, + reczone => 'www', + } ) + || qsearchs( 'domain_record', { + svcnum => $svc_domain->svcnum, + reczone => 'www.'.$svc_domain->domain.'.', + } ); + } + + $arec{'@.'. $svc_domain->domain} = $svc_domain->domain + unless qsearchs('domain_record', { + svcnum => $svc_domain->svcnum, + reczone => '@', + } ) + || qsearchs('domain_record', { + svcnum => $svc_domain->svcnum, + reczone => $svc_domain->domain.'.', + } ); + + } + + } + } + +} elsif ( $action eq 'Edit' ) { + + my($domain_rec) = qsearchs('domain_record', { 'recnum'=>$svc_www->recnum }); + $arec{$svc_www->recnum} = join '.', $domain_rec->recdata, $domain_rec->reczone; + +} else { + die "\$action eq Add, but \$pkgnum is null!\n"; +} + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/tax_class.html b/httemplate/edit/tax_class.html new file mode 100644 index 000000000..d3e2e821f --- /dev/null +++ b/httemplate/edit/tax_class.html @@ -0,0 +1,36 @@ +<% include('/elements/header.html', "$action taxclass") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/tax_class.html" METHOD=POST> + +<INPUT TYPE="hidden" NAME="taxclassnum" VALUE=""> +<INPUT TYPE="hidden" NAME="data_vendor" VALUE=""> + +Tax class <INPUT TYPE="text" NAME="taxclass" VALUE="<% $taxclass |h %>"><BR> +Description <INPUT TYPE="text" NAME="description" VALUE="<% $description |h %>"> + +<BR><BR> +<INPUT TYPE="submit" VALUE="<% $action %> taxclass"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $taxclass = ''; +my $description = ''; +if ( $cgi->param('error') ) { + $taxclass = $cgi->param('taxclass'); + $description = $cgi->param('description'); +} + +my $action = 'Add'; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/tax_rate.html b/httemplate/edit/tax_rate.html new file mode 100644 index 000000000..bff699946 --- /dev/null +++ b/httemplate/edit/tax_rate.html @@ -0,0 +1,106 @@ +<% include('elements/edit.html', + 'popup' => 1, + 'name' => 'Tax rate', #Edit tax rate + 'table' => 'tax_rate', + 'labels' => $labels, + 'fields' => \@fields, + 'value_callback' => $value_callback, + ) +%> +<%once> + +my $conf = new FS::Conf; +my $value_callback = + sub { my ( $field, $value ) = @_; + ( $field =~ /^(tax|excessrate|usetax|useexcessrate)$/ ) + ? $value*100 + : $value; + }; + +</%once> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $taxnum; +if ( $cgi->param('error') ) { + $cgi->param('taxnum') =~ /^(\d+)$/ or die 'error, but no taxnum'; + $taxnum = $1; +} else { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die 'no taxnum'; + $taxnum = $1; +} + +my $tax_rate = qsearchs('tax_rate', { 'taxnum' => $taxnum }) + or die "unknown taxnum $1"; + +my $labels = { 'taxnum' => 'Tax', + 'data_vendor' => 'Data vendor', + 'geocode' => 'Vendor location code', + 'location' => 'Tax auth loc code', + 'taxclass_description' => 'Tax class', + 'taxname' => 'Tax name', + 'effective_date' => 'Effective date', + 'tax' => 'Tax rate (1st bracket)', + 'excessrate' => 'Tax rate (2nd bracket)', + 'taxbase' => 'First bracket', + 'taxmax' => 'Max tax', + 'usetax' => 'Use tax rate (1st bracket)', + 'useexcessrate' => 'Use tax rate (2nd bracket)', + 'unittype_name' => 'Units', + 'fee' => 'Fee per unit (1st bracket)', + 'excessfee' => 'Fee per unit (2st bracket)', + 'feebase' => 'Units in first bracket', + 'feemax' => 'Max Units', + 'maxtype_name' => 'Threshold accumulation', + 'taxauth_name', => 'Tax authority', + 'basetype_name' => 'Basis', + 'passtype_name' => 'Passthru', + 'passflag' => 'Passable', + 'setuptax' => 'This tax not applicable to setup fees', + 'recurtax' => 'This tax not applicable to recurring fees', + }; + +my @fields = ( + { type=>'tablebreak-tr-title', value=>'Location' }, + { field=>'data_vendor', type=>'hidden',}, + { field=>'geocode', type=>'fixed' }, + { field=>'taxclassnum', type=>'hidden' } , + { field=>'taxclass_description', type=>'fixed' } , + { field=>'taxname', type=>'text' } , + { field=>'effective_date', type=>'fixed' } , + { field=>'location', type=>'text' }, + { type=>'tablebreak-tr-title', value=>'Money based rates' }, + { field=>'tax', type=>'percentage' } , + { field=>'excessrate', type=>'percentage' } , + { field=>'taxbase', type=>'money' } , + { field=>'taxmax', type=>'money' } , + { field=>'usetax', type=>'percentage' } , + { field=>'useexcessrate', type=>'percentage' } , + { type=>'tablebreak-tr-title', value=>'Service based rates' }, + { field=>'unittype', type=>'hidden' } , + { field=>'unittype_name', type=>'fixed' } , + { field=>'fee', type=>'money' } , + { field=>'excessfee', type=>'money' } , + { field=>'feebase', type=>'text' } , + { field=>'feemax', type=>'text' } , + { type=>'tablebreak-tr-title', value=>'Taxation rules' }, + { field=>'maxtype', type=>'hidden' } , + { field=>'maxtype_name', type=>'fixed' } , + { field=>'taxauth', type=>'hidden' } , + { field=>'taxauth_name', type=>'fixed' } , + { field=>'basetype', type=>'hidden' } , + { field=>'basetype_name', type=>'fixed' } , + { field=>'passtype', type=>'hidden' } , + { field=>'passtype_name', type=>'fixed' } , + { field=>'passflag', type=>'fixed' } , + { field=>'setuptax', type=>'checkbox', value=>'Y' } , + { field=>'recurtax', type=>'checkbox', value=>'Y' } , + { field=>'disabled', type=>'checkbox', value=>'Y' } , + { field=>'manual', type=>'hidden', value=>'Y' } , +); + +</%init> diff --git a/httemplate/edit/usage_class.html b/httemplate/edit/usage_class.html new file mode 100644 index 000000000..be01d2e67 --- /dev/null +++ b/httemplate/edit/usage_class.html @@ -0,0 +1,42 @@ +<% include( 'elements/edit.html', + 'name_singular' => 'Usage Class', + 'table' => 'usage_class', + 'fields' => [ + 'classname', + 'weight', + { field => 'format', + type => $useformat ? 'select' : 'hidden', + ( $useformat + ? ( 'options' => [ keys %labels ], + 'labels' => \%labels, + ) + : () + ), + }, + { field => 'disabled', + type => 'checkbox', + value => 'Y', + }, + ], + 'labels' => { + 'classnum' => 'Class number', + 'classname' => 'Class name', + 'weight' => 'Weight', + 'format' => 'Format', + 'disabled' => 'Disable class', + }, + 'viewall_dir' => 'browse', + ) + +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; +my $useformat = $conf->exists('usage_class_as_a_section'); + +my %labels = &FS::usage_class::summary_formats_labelhash(); + +</%init> |