diff options
Diffstat (limited to 'httemplate/edit')
138 files changed, 12735 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..b2c89c32c --- /dev/null +++ b/httemplate/edit/REAL_cust_pkg.cgi @@ -0,0 +1,186 @@ +<% 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">Comment</TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD> + </TR> + + <TR> + <TD ALIGN="right">Order taker</TD> + <TD BGCOLOR="#ffffff"><% $cust_pkg->otaker %></TD> + </TR> + + <& .row_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: "%m/%d/%Y", + 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') %> + +<%once> + +#my $format = "%c %z (%Z)"; +my $format = "%m/%d/%Y %T %z (%Z)"; + +#false laziness w/view/cust_main/packages.html +#my( $billed_or_prepaid, + +</%once> +<%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') eq '_bill_areyousure' ) { + if ( $cgi->param('bill') =~ /^([\s\d\/\:\-\(\w\)]*)$/ ) { + my $bill = $1; + $cgi->param('error', ''); + $error = "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">'; + } + } + + #get package record + $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); + die "No package!" unless $cust_pkg; + + foreach my $col (qw( setup last_bill bill adjourn expire )) { + my $value = $cgi->param($col); + $cust_pkg->set( $col, $value ? str2time($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..73488ef9a --- /dev/null +++ b/httemplate/edit/access_user.html @@ -0,0 +1,50 @@ +<% include( 'elements/edit.html', + 'name' => 'Employee', + 'table' => 'access_user', + 'fields' => [ + 'username', + { field=>'_password', type=>'password' }, + { field=>'_password2', type=>'password' }, + 'last', + 'first', + { field=>'disabled', type=>'checkbox', value=>'Y' }, + ], + 'labels' => { + 'usernum' => 'User number', + 'username' => 'Username', + '_password' => 'Password', + '_password2'=> 'Re-enter Password', + 'last' => 'Last name', + 'first' => 'First name', + '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>' + ; + }, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/agent.cgi b/httemplate/edit/agent.cgi new file mode 100755 index 000000000..215542de3 --- /dev/null +++ b/httemplate/edit/agent.cgi @@ -0,0 +1,123 @@ +<% include("/elements/header.html","$action Agent", menubar( + 'View all agents' => $p. 'browse/agent.cgi', +)) %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%popurl(1)%>process/agent.cgi" METHOD=POST> +<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> + + <% include('/elements/tr-select-invoice_template.html', + 'label' => 'Invoice template', + 'field' => 'invoice_template', + 'curr_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..abf4bf89f --- /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. ' - '. $_[0]->comment; }, + '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..bb57fc5d3 --- /dev/null +++ b/httemplate/edit/bulk-cust_main_county.html @@ -0,0 +1,130 @@ +<% include('/elements/header-popup.html', 'Bulk Tax rate') %> + +<FORM ACTION="<% popurl(1)."process/bulk-cust_main_county.html" %>" METHOD="POST"> + +<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 add tax"> + +<%init> + +my $conf = new FS::Conf; + +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 = split(',', $1); +} else { + my($query) = $cgi->keywords; + $query =~ /^([\d,]+)$/ + or die 'Nothing selected'; #XXX better error when nothing selected + @taxnum = split(',', $1); +} + +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/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_credit.cgi b/httemplate/edit/cust_credit.cgi new file mode 100755 index 000000000..c9ca31ff3 --- /dev/null +++ b/httemplate/edit/cust_credit.cgi @@ -0,0 +1,68 @@ +<% 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 %>"> + +Credit +<% ntable("#cccccc", 2) %> + + <TR> + <TD ALIGN="right">Date</TD> + <TD BGCOLOR="#ffffff"><% time2str("%D",$_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">Auto-apply<BR>to invoices</TD> + <TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED>yes<OPTION>no</SELECT></TD> + </TR> + +</TABLE> + +<BR> + +<CENTER><INPUT TYPE="submit" ID="confirm_credit_button" VALUE="Enter credit" DISABLED></CENTER> + +</FORM> +</BODY> +</HTML> +<%once> + +my $conf = new FS::Conf; + +</%once> +<%init> + +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..d3004f1c6 --- /dev/null +++ b/httemplate/edit/cust_main.cgi @@ -0,0 +1,780 @@ +<% include('/elements/header.html', + "Customer $action", + '', + ' onUnload="myclose()"' +) %> + +<% include('/elements/init_overlib.html') %> + +<% include('/elements/error.html') %> + +<FORM NAME="topform" STYLE="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> +% } + +<% &ntable("#cccccc") %> + +%# 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 ), + ) +%> + +%# 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 %>"> + +% } + +%# 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 } ) +%) { + + <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', + ) + %> + </TD> + </TR> +% } else { + + + <INPUT TYPE="hidden" NAME="referral_custnum" VALUE=""> +% } + + +</TABLE> + +<!-- birthdate --> + +% if ( $conf->exists('cust_main-enable_birthdate') ) { + + <BR> + <% 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> + +% } + +<!-- contact info --> + +% my $same_checked = ''; +% my $ship_disabled = ''; +% unless ( $cust_main->ship_last && $same ne 'Y' ) { +% $same_checked = 'CHECKED'; +% $ship_disabled = 'DISABLED 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><BR> +Billing address +<% 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_county() { + what.form.ship_county.selectedIndex = what.form.county.selectedIndex; + } + + 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); + +% for (qw( last first company address1 address2 city county state zip country daytime night fax )) { + 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 (qw( last first company address1 address2 city county state zip country daytime night fax )) { + 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> +Service address +(<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, + ) +%> + + +<!-- billing info --> + +<% include( 'cust_main/billing.html', $cust_main, + 'payinfo' => $payinfo, + 'invoicing_list' => \@invoicing_list, + ) +%> + +<% include( '/elements/xmlhttp.html', + 'url' => $p.'misc/xmlhttp-cust_main-address_standardize.html', + 'subs' => [ 'address_standardize' ], + #'method' => 'POST', #could get too long? + ) +%> + +<SCRIPT> +function bottomfixup(what) { + + //i don't think we need to copy things between two forms anymore, modern + //browsers are fine with DIVs inside FORMs + + var topvars = new Array( + 'birthdate', + + 'custnum', 'agentnum', 'agent_custid', 'refnum', 'referral_custnum', + + 'last', 'first', 'ss', 'company', + 'address1', 'address2', 'city', + 'county', 'state', 'zip', 'country', + 'daytime', 'night', 'fax', + 'stateid', 'stateid_state', + + 'same', + + 'ship_last', 'ship_first', 'ship_company', + 'ship_address1', 'ship_address2', 'ship_city', + 'ship_county', 'ship_state', 'ship_zip', 'ship_country', + 'ship_daytime','ship_night', 'ship_fax', + + 'geocode', + + 'select' // XXX key + ); + + var layervars = new Array( + 'payauto', + 'payinfo', 'payinfo1', 'payinfo2', 'paytype', + 'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv', + 'paystart_month', 'paystart_year', 'payissue', + 'payip', + 'paid' + ); + + var billing_bottomvars = new Array( + 'tax', + 'invoicing_list', 'invoicing_list_POST', 'invoicing_list_FAX', + 'invoice_terms', + 'spool_cdr', + 'squelch_cdr' + ); + + for ( f=0; f < topvars.length; f++ ) { + var field = topvars[f]; + copyelement( document.topform.elements[field], + document.bottomform.elements[field] + ); + } + + var layerform = document.topform.select.options[document.topform.select.selectedIndex].value; + for ( f=0; f < layervars.length; f++ ) { + var field = layervars[f]; + copyelement( document.forms[layerform].elements[field], + document.bottomform.elements[field] + ); + } + + for ( f=0; f < billing_bottomvars.length; f++ ) { + var field = billing_bottomvars[f]; + copyelement( document.billing_bottomform.elements[field], + document.bottomform.elements[field] + ); + } + + //this part does USPS address correction + + // XXX should this be first and should we update the form fields that are + // displayed??? + + //var state_el = document.bottomform.elements['state']; + + //address_standardize( + var cust_main = new Array( + 'company', document.bottomform.elements['company'].value, + 'address1', document.bottomform.elements['address1'].value, + 'address2', document.bottomform.elements['address2'].value, + 'city', document.bottomform.elements['city'].value, + 'state', document.bottomform.elements['state'].value, + //'state', state_el.options[ state_el.selectedIndex ].value, + 'zip', document.bottomform.elements['zip'].value, + + 'ship_company', document.bottomform.elements['ship_company'].value, + 'ship_address1', document.bottomform.elements['ship_address1'].value, + 'ship_address2', document.bottomform.elements['ship_address2'].value, + 'ship_city', document.bottomform.elements['ship_city'].value, + 'ship_state', document.bottomform.elements['ship_state'].value, + //'ship_state', state_el.options[ state_el.selectedIndex ].value, + 'ship_zip', document.bottomform.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 () { + + if ( changed ) { + document.bottomform.elements['company'].value = argsHash['new_company']; + document.bottomform.elements['address1'].value = argsHash['new_address1']; + document.bottomform.elements['address2'].value = argsHash['new_address2']; + document.bottomform.elements['city'].value = argsHash['new_city']; + document.bottomform.elements['state'].value = argsHash['new_state']; + //'state', state_el.options[ state_el.selectedIndex ].value, + document.bottomform.elements['zip'].value = argsHash['new_zip']; + } + + if ( ship_changed ) { + document.bottomform.elements['ship_company'].value = argsHash['new_ship_company']; + document.bottomform.elements['ship_address1'].value = argsHash['new_ship_address1']; + document.bottomform.elements['ship_address2'].value = argsHash['new_ship_address2']; + document.bottomform.elements['ship_city'].value = argsHash['new_ship_city']; + document.bottomform.elements['ship_state'].value = argsHash['new_ship_state']; + //'state', state_el.options[ state_el.selectedIndex ].value, + document.bottomform.elements['ship_zip'].value = argsHash['new_ship_zip']; + } + + } + +% if ( $conf->exists('enable_taxproducts') ) { + + if ( <% $taxpre %>error ) { + + if ( document.bottomform.elements['country'].value == 'CA' || + document.bottomform.elements['country'].value == 'US' + ) + { + + var url = "cust_main/choose_tax_location.html?data_vendor=cch-zip;city="+document.bottomform.elements['city'].value+";state="+document.bottomform.elements['state'].value+";zip="+document.bottomform.elements['zip'].value+";country="+document.bottomform.elements['country'].value+";"; + // popup a chooser + OLgetAJAX( url, update_geocode, 300 ); + + } else { + + document.bottomform.elements['geocode'].value = 'DEFAULT'; + document.bottomform.submit(); + + } + + } else + +% } + + if ( changed || ship_changed ) { + +% if ( $conf->exists('cust_main-auto_standardize_address') ) { + + standardize_address(); + document.bottomform.submit(); + +% } 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="document.bottomform.submit();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' + + '</TD><TD>' + + '<BUTTON TYPE="button" onClick="standardize_address(); document.bottomform.submit();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' + + '</TD></TR>' + + '<TR><TD COLSPAN=2 ALIGN="center">' + + '<BUTTON TYPE="button" onClick="document.bottomform.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 { + + document.bottomform.submit(); + + } + +} + +function update_geocode() { + + //yay closures + set_geocode = function (what) { + + //alert(what.options[what.selectedIndex].value); + var argsHash = eval('(' + what.options[what.selectedIndex].value + ')'); + document.bottomform.elements['city'].value = argsHash['city']; + document.bottomform.elements['state'].value = argsHash['state']; + document.bottomform.elements['zip'].value = argsHash['zip']; + document.bottomform.elements['geocode'].value = argsHash['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 ); + +} + +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); +} + +</SCRIPT> + +<FORM ACTION="<% popurl(1) %>process/cust_main.cgi" METHOD=POST NAME="bottomform" STYLE="margin-top: 0; margin-bottom: 0"> +% foreach my $hidden ( +% 'birthdate', +% +% 'custnum', 'agentnum', 'agent_custid', 'refnum', 'referral_custnum', +% 'last', 'first', 'ss', 'company', +% 'address1', 'address2', 'city', +% 'county', 'state', 'zip', 'country', +% 'daytime', 'night', 'fax', +% 'stateid', 'stateid_state', +% +% 'same', +% +% 'ship_last', 'ship_first', 'ship_company', +% 'ship_address1', 'ship_address2', 'ship_city', +% 'ship_county', 'ship_state', 'ship_zip', 'ship_country', +% 'ship_daytime','ship_night', 'ship_fax', +% +% 'geocode', +% +% 'select', #XXX key +% +% 'payauto', +% 'payinfo', 'payinfo1', 'payinfo2', 'paytype', +% 'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv', +% 'paystart_month', 'paystart_year', 'payissue', +% 'payip', +% 'paid', +% +% 'tax', +% 'invoicing_list', 'invoicing_list_POST', 'invoicing_list_FAX', +% 'invoice_terms', +% 'spool_cdr', +% 'squelch_cdr' +% ) { +% + + <INPUT TYPE="hidden" NAME="<% $hidden %>" VALUE=""> +% } +% +% my $ro_comments = $conf->exists('cust_main-use_comments')?'':'readonly'; +% if (!$ro_comments || $cust_main->comments) { + +<BR>Comments +<% &ntable("#cccccc") %> + <TR> + <TD> + <TEXTAREA COLS=80 ROWS=5 WRAP="HARD" NAME="comments" <%$ro_comments%>><% $cust_main->comments %></TEXTAREA> + </TD> + </TR> +</TABLE> +% +% } +% +%unless ( $custnum ) { +% # pry the wrong place for this logic. also pretty expensive +% #use FS::part_pkg; +% +% #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 @part_pkg = grep { $_->svcpart('svc_acct') +% && ( $pkgpart->{ $_->pkgpart } +% || $agentnum eq 'all' +% || ( $agentnum ne 'all' +% && $agentnum +% && $_->agentnum +% && $_->agentnum == $agentnum +% ) +% ) +% } +% qsearch( 'part_pkg', { 'disabled' => '' }, '', 'ORDER BY pkg' ); # case? +% +% if ( @part_pkg ) { +% +% # print "<BR><BR>First package", &itable("#cccccc", "0 ALIGN=LEFT"), +% #apiabuse & undesirable wrapping +% +% + + <BR>First package + <% ntable("#cccccc") %> + + <TR> + <TD COLSPAN=2> + <% include('cust_main/select-domain.html', + 'pkgparts' => \@part_pkg, + 'saved_pkgpart' => $saved_pkgpart, + 'saved_domsvc' => $saved_domsvc, + ) + %> + </TD> + </TR> +% +% #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; +% + + + <TR> + <TD ALIGN="right">Username</TD> + <TD> + <INPUT TYPE="text" NAME="username" VALUE="<% $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="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $passwordmax %>> + (blank to generate) + </TD> + </TR> + + <TR> + <TD ALIGN="right">Access number</TD> + <TD><% FS::svc_acct_pop::popselector($popnum) %></TD> + </TR> + </TABLE> +% } +% } + + +<INPUT TYPE="hidden" NAME="otaker" VALUE="<% $cust_main->otaker %>"> +<BR> +<INPUT TYPE="button" NAME="submitButton" ID="submitButton" VALUE="<% $custnum ? "Apply Changes" : "Add Customer" %>" onClick="document.bottomform.submitButton.disabled=true; bottomfixup(this.form);"> +<BR> +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Edit customer'); + +#for misplaced logic below +#use FS::part_pkg; + +#for false laziness below (now more properly lazy) +#use FS::svc_acct_pop; + +#for (other) false laziness below +#use FS::agent; +#use FS::type_pkgs; + +my $conf = new FS::Conf; + +my $taxpre = $conf->exists('tax-ship_address') ? 'ship_' : ''; +#get record + +my($custnum, $username, $password, $popnum, $cust_main, $saved_pkgpart, $saved_domsvc); +my(@invoicing_list); +my ($ss,$stateid,$payinfo); +my $same = ''; +if ( $cgi->param('error') ) { + $cust_main = new FS::cust_main ( { + map { $_, scalar($cgi->param($_)) } fields('cust_main') + } ); + $custnum = $cust_main->custnum; + $saved_domsvc = $cgi->param('domsvc') || ''; + if ( $saved_domsvc =~ /^(\d+)$/ ) { + $saved_domsvc = $1; + } else { + $saved_domsvc = ''; + } + $saved_pkgpart = $cgi->param('pkgpart_svcpart') || ''; + if ( $saved_pkgpart =~ /^(\d+)_/ ) { + $saved_pkgpart = $1; + } else { + $saved_pkgpart = ''; + } + $username = $cgi->param('username'); + $password = $cgi->param('_password'); + $popnum = $cgi->param('popnum'); + @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 +} elsif ( $cgi->keywords ) { #editing + 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); + } + $saved_pkgpart = 0; + $saved_domsvc = 0; + $username = ''; + $password = ''; + $popnum = 0; + @invoicing_list = $cust_main->invoicing_list; + $ss = $cust_main->masked('ss'); + $stateid = $cust_main->masked('stateid'); + $payinfo = $cust_main->paymask; +} else { + $custnum=''; + $cust_main = new FS::cust_main ( {} ); + $cust_main->otaker( &getotaker ); + $cust_main->referral_custnum( $cgi->param('referral_custnum') ); + $saved_pkgpart = 0; + $saved_domsvc = 0; + $username = ''; + $password = ''; + $popnum = 0; + @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 $action = $custnum ? 'Edit' : 'Add'; +$action .= ": ". $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..8724db9dc --- /dev/null +++ b/httemplate/edit/cust_main/billing.html @@ -0,0 +1,484 @@ +%if ( $payby_default eq 'HIDE' ) { +% +% $cust_main->payby('BILL') unless $cust_main->payby; + + <INPUT TYPE="hidden" NAME="select" VALUE="<% $cust_main->payby %>"> + + </FORM> + + <FORM NAME="<% $cust_main->payby %>" STYLE="margin-top: 0; margin-bottom: 0"> + + <INPUT TYPE="hidden" NAME="payinfo" VALUE="<% $cust_main->paymask %>"> + +% foreach my $field (qw( payname paycvv paystart_month paystart_year payissue payip paytype paystate )) { + + <INPUT TYPE="hidden" NAME="<% $field %>" VALUE="<% $cust_main->getfield($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="exp_month" VALUE="<% $mon %>"> + <INPUT TYPE="hidden" NAME="exp_year" VALUE="<% $year %>"> + + </FORM> + + <FORM NAME="billing_bottomform" STYLE="margin-top: 0; margin-bottom: 0"> + + <INPUT TYPE="hidden" NAME="tax" VALUE="<% $cust_main->tax %>"> + + <INPUT TYPE="hidden" NAME="invoicing_list" VALUE="<% join(', ', @invoicing_list) %>"> + + </FORM> + +% } else { +% +% my $r = qq!<font color="#ff0000">*</font> !; + + <BR>Billing information + <% &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="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' => '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="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' => '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="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="payname" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!. +% +% qq!<TR><TD COLSPAN=2 WIDTH="608"><INPUT TYPE="checkbox" NAME="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="payinfo1" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $account : '' ). '"></TD>'. +% qq!<TD ALIGN="right">Type</TD><TD><SELECT NAME="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="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="exp_month" VALUE="12">!. +% qq!<INPUT TYPE="hidden" NAME="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="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' => 'pay', +% ). "</TD></TR>" +% : '<INPUT TYPE="hidden" NAME="paystate" VALUE="'. +% $cust_main->paystate. '">' +% ). +% +% +% qq!<TR><TD COLSPAN=4 WIDTH="608"><INPUT TYPE="checkbox" NAME="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="payinfo" VALUE="!. ( $payby eq 'LECB' ? $cust_main->payinfo : '' ). qq!" MAXLENGTH=15 SIZE=16></TD></TR>!. +% +% qq!<INPUT TYPE="hidden" NAME="exp_month" VALUE="12">!. +% qq!<INPUT TYPE="hidden" NAME="exp_year" VALUE="2037">!. +% qq!<INPUT TYPE="hidden" NAME="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="payinfo" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payinfo : '' ). qq!"></TD></TR>!. +% +% qq!<INPUT TYPE="hidden" NAME="exp_month" VALUE="12">!. +% qq!<INPUT TYPE="hidden" NAME="exp_year" VALUE="2037">!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">Attention </TD>!. +% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="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="payinfo" VALUE=""></TD></TR>!. +% +% qq!<TR><TD ALIGN="right" WIDTH="200">${r}Expiration </TD>!. +% '<TD WIDTH="408">'. +% +% include('/elements/select-month_year.html', +% 'prefix' => '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="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="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="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 = ( +% 'CARD' => 'Credit card', +% 'CHEK' => 'Electronic check', +% 'LECB' => 'Phone bill billing', +% 'BILL' => 'Billing', +% 'CASH' => 'Cash', # initial payment, then billing', +% 'WEST' => 'Western Union', # initial payment, then billing', +% 'MCRD' => 'Manual credit card', # initial payment, then billing', +% 'COMP' => 'Complimentary', +% ); +% if ( $cust_main->custnum ) { #don't offer CASH/WEST/MCRD initial payment types +% # when editing customer +% delete $allopt{$_} for qw(CASH WEST MCRD); +% } +% +% tie my %options, 'Tie::IxHash', +% map { $_ => $allopt{$_} } +% grep { exists $allopt{$_} } +% @payby; +% +% my %payby2option = ( +% ( map { $_ => $_ } keys %options ), +% 'DCRD' => 'CARD', +% 'DCHK' => 'CHEK', +% ); +% +% my $widget = new HTML::Widgets::SelectLayers( +% 'options' => \%options, +% #'form_name' => 'dummy', +% #'form_action' => 'nothingyet', +% #chops bottom of page in IE# 'under_position' => 'absolute', +% 'html_between' => '</TD></TR></TABLE>', +% 'selected_layer' => $payby2option{$payby || $payby_default || $payby[0] }, +% 'layer_callback' => sub { my $layer = shift; $payby{$layer}; }, +% ); +% +% + + + <TD WIDTH="408"><% $widget->html %> + + <FORM NAME="billing_bottomform" STYLE="margin-top: 0; margin-bottom: 0"> + + <% &ntable("#cccccc") %> + + <TR><TD> </TD></TR> + + <TR> + <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="tax" VALUE="Y" <% $cust_main->tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt</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"> + <SELECT NAME="invoice_terms"> + <OPTION VALUE="">Default (<% $conf->config('invoice_default_terms') || 'Payable upon receipt' %>) +% foreach my $term ( 'Payable upon receipt', +% ( map "Net $_", 0, 10, 15, 30, 45, 60 ), +% ) { + <OPTION VALUE="<% $term %>" <% $cust_main->invoice_terms eq $term ? ' SELECTED' : '' %>><% $term %> +% } + </SELECT> + </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 %>"> +% } + + </TABLE> + + </FORM> + + <% $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; + +</%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..bd8b95cb6 --- /dev/null +++ b/httemplate/edit/cust_main/choose_tax_location.html @@ -0,0 +1,85 @@ +<FORM NAME="choosegeocodeform"> +<CENTER><BR><B>Choose tax location</B><BR><BR> +<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 (!$have_selected && lc($location->city) eq lc($location{city})) { +% $selected = 'SELECTED'; +% } + <OPTION VALUE="<% $value %>" STYLE="<% $style %>" <% $selected %>><% $content %> +% } +</SELECT><BR><BR> + +<TABLE><TR> + <TD> <BUTTON TYPE="button" onClick="set_geocode(document.getElementById('geocodes')); document.bottomform.submit();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Set location </BUTTON></TD> + <TD><BUTTON TYPE="button" onClick="document.bottomform.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 $have_selected = 0; + +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($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..27dd38516 --- /dev/null +++ b/httemplate/edit/cust_main/contact.html @@ -0,0 +1,137 @@ +<% &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%>> , + <INPUT TYPE="text" NAME="<%$pre%>first" VALUE="<% $cust_main->get($pre.'first') %>" onChange="<% $onchange %>" <%$disabled%>> + </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%>> + </TD> +</TR> + +<% include('/elements/location.html', + 'prefix' => $pre, + 'object' => $cust_main, + 'onchange' => $onchange, + 'disabled' => $disabled, + 'same_checked' => $opt{'same_checked'}, + 'geocode' => $opt{'geocode'}, + ) +%> + +<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%>> + </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%>> + </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%>> + </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%>></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, + ) + %> + </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 $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'); + +#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/select-domain.html b/httemplate/edit/cust_main/select-domain.html new file mode 100644 index 000000000..bec1e834c --- /dev/null +++ b/httemplate/edit/cust_main/select-domain.html @@ -0,0 +1,67 @@ + +<% include('/elements/xmlhttp.html', + 'url' => $p.'misc/svc_acct-domains.cgi', + 'subs' => [ $opt{'prefix'}. 'get_domains' ], + ) +%> + +<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; + } + + function <% $opt{'prefix'} %>pkgpart_svcpart_changed(what,selected) { + + pkgpart_svcpart = what.options[what.selectedIndex].value; + + 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); + } + + } + + // go get the new domains + <% $opt{'prefix'} %>get_domains( pkgpart_svcpart, <% $opt{'prefix'} %>update_domains ); + + } + +</SCRIPT> + +<SELECT NAME="<% $opt{'prefix'} %>pkgpart_svcpart" onchange="<% $opt{'prefix'} %>pkgpart_svcpart_changed(this,0);" > + <OPTION VALUE="">(none) + +% foreach my $part_pkg ( @part_pkg ) { + + <OPTION VALUE="<% $part_pkg->pkgpart. "_". $part_pkg->svcpart('svc_acct') %>"<% ( $opt{saved_pkgpart} && $part_pkg->pkgpart == $opt{saved_pkgpart} ) ? ' SELECTED' : '' %>><% $part_pkg->pkg. " - ". $part_pkg->comment %> + +% } + +</SELECT> +<SCRIPT> + pkgpart_svcpart_changed(document.bottomform.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 @part_pkg = @{$opt{'pkgparts'}}; + +</%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..d5297ab0a --- /dev/null +++ b/httemplate/edit/cust_main_county-expand.cgi @@ -0,0 +1,50 @@ +<% 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->county; + +if ( $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..5d7f9e62e --- /dev/null +++ b/httemplate/edit/cust_main_county.html @@ -0,0 +1,62 @@ +<% 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', + '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', }, +); + +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..6c6a1a9a0 --- /dev/null +++ b/httemplate/edit/cust_main_note.cgi @@ -0,0 +1,45 @@ +<% 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 %>"> + + +<BR><BR> +<TEXTAREA NAME="comment" ROWS="12" COLS="60"> +<% $comment %> +</TEXTAREA> + +<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; +} + +$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..3c2877498 --- /dev/null +++ b/httemplate/edit/cust_pay.cgi @@ -0,0 +1,138 @@ +% 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("%m/%d/%Y %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: "%m/%d/%Y", + 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> + +</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') || '$'; + +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') ? str2time($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' ) { + $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..f927e1042 --- /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') } = $_->getfield('comment'); + next if $_->disabled; + $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); + $comment{ $_ -> getfield('pkgpart') } = $_->getfield('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_refund.cgi b/httemplate/edit/cust_refund.cgi new file mode 100755 index 000000000..94c0993d7 --- /dev/null +++ b/httemplate/edit/cust_refund.cgi @@ -0,0 +1,165 @@ +% 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("%D",$cust_pay->_date) %></TD> + </TR> + + <TR> + <TD ALIGN="right">Method</TD><TD BGCOLOR="#ffffff"><% $payby %> # <% $paymask %></TD> + </TR> + +% unless ( $paydate ) { # 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("%D",$_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 $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/elements/ApplicationCommon.html b/httemplate/edit/elements/ApplicationCommon.html new file mode 100644 index 000000000..a485d37de --- /dev/null +++ b/httemplate/edit/elements/ApplicationCommon.html @@ -0,0 +1,177 @@ +<%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" ) %> + +<% include('/elements/error.html') %> + +<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("%D", $src->_date) %></B></TD> +</TR> + +<TR> + <TD ALIGN="right">Amount: </TD> + <TD><B><% $money_char %><% $src->amount %></B></TD> +</TR> + +<TR> + <TD ALIGN="right">Unapplied amount: </TD> + <TD><B><% $money_char %><% $unapplied %></B></TD> +</TR> + +% if ( $src_table eq 'cust_credit' ) { + <TR> + <TD ALIGN="right">Reason: </TD> + <TD><B><% $src->reason %></B></TD> + </TR> +% } + +</TABLE> +<BR> + +<SCRIPT TYPE="text/javascript"> +function changed(what) { + dst = what.options[what.selectedIndex].value; + + if ( dst == '' ) { + what.form.submit.disabled=true; + return true; + } + + what.form.submit.disabled=false; + +% foreach my $dst ( @dst ) { + + if ( dst == <% $dst->$dst_pkey %> ) { + what.form.amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>"; + } + +% } + +} +</SCRIPT> + +Apply to: + +<TABLE 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("%D", $dst->_date) %> - $<% $dst->$dst_unapplied %> +% } + +</SELECT> + </TD> +</TR> + +<TR> + <TD ALIGN="right">Amount: </TD> + <TD><% $money_char %><INPUT TYPE="text" NAME="amount" VALUE="<% $amount %>" SIZE=8 MAXLENGTH=8></TD> +</TR> + +</TABLE> + +<BR> +<CENTER><INPUT TYPE="submit" VALUE="Apply" NAME="submit" ID="submit" DISABLED></CENTER> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +my %opt = @_; + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; + +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'; + +my $to = $dst_table eq 'cust_refund' ? ' to Refund' : ''; + +my($src_pkeyvalue, $amount, $dst_pkeyvalue); +if ( $cgi->param('error') ) { + $src_pkeyvalue = $cgi->param($src_pkey); + $amount = $cgi->param('amount'); + $dst_pkeyvalue = $cgi->param($dst_pkey); +} 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; + +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 } ); + +</%init> diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html new file mode 100644 index 000000000..d18a37d5a --- /dev/null +++ b/httemplate/edit/elements/edit.html @@ -0,0 +1,700 @@ +<%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', + + #m2 stuff only tested w/selectlayers so far + #might work w/select too, dunno others + '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 + + '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_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"; + }, + + #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 ) = @_; }, + + #always run, after the mode-specific object creation/search + 'end_callback' = sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + ###mode-specific callbacks + + #run when re-displaying with an error + 'error_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + #run when editing + 'edit_callback' => sub { my( $cgi, $object, $fields_listref ) = @_; }, + + # returns a hashref for the new object + 'new_hashref_callback' + + # returns the new object iself (otherwise, ->new is called) + 'new_object_callback' + + #run when adding + 'new_callback' => sub { my( $cgi, $object, $fields_listref ) = @_; }, + + #run when cloning + 'clone_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + + ###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 ACTION="<% $url %>" METHOD=POST NAME="edit_topform"> + +<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->{'m2m_method'}; +% +% warn "layer values: ". Dumper($layer_values) +% if $opt{'debug'}; +% +% my %include_common = ( +% +% #text and derivitives +% 'size' => $f->{'size'}, +% 'maxlength' => $f->{'maxlength'}, +% +% #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, 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_right ); +% +% if ( $type eq 'tablebreak-tr-title' ) { +% $include_common{'table_id'} = 'TableNumber'. $tablenum++ +% } +% +% 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; +% }; +% +% 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->{'m2m_method'} ) { #XXX test this for all +% #types of fields +% my($table, $col); +% if ( $f->{'m2name_table'} ) { +% $table = $f->{'m2name_table'}; +% $col = $f->{'m2name_namecol'}; +% } 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' : '' ), +% ); + + <% include( @existing ) %> + +% $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') : () ), +% ); + + <% include( @include ) %> + +% if ( $f->{'m2name_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+)$/; + var match = field_regex.exec(what.name); + if ( !match ) { + alert(what.name + " didn't match?!"); + 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 %>; + +% if ( $type eq 'selectlayers' ) { #until the rest have html/js_only + 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.innerHTML = newrow; + + row.appendChild(widget_cell); + +% 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'}" %>"> + +</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 } }); + + &{$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) + 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) + 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}; + +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/svc_Common.html b/httemplate/edit/elements/svc_Common.html new file mode 100644 index 000000000..0b64120fb --- /dev/null +++ b/httemplate/edit/elements/svc_Common.html @@ -0,0 +1,122 @@ +<% include( 'edit.html', + + 'menubar' => [], + + 'error_callback' => sub { + my( $cgi, $svc_x ) = @_; + #$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; + + $svc_x->setfield('svcpart', $svcpart); + }, + + 'edit_callback' => sub { + my( $cgi, $svc_x ) = @_; + #$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; + }, + + 'new_hashref_callback' => sub { + #my( $cgi, $svc_x ) = @_; + + { svcpart => $svcpart }; + + }, + + 'new_callback' => sub { + my( $cgi, $svc_x ) = @_;; + + $part_svc = qsearchs( 'part_svc', { svcpart=>$svcpart }); + die "No part_svc entry!" unless $part_svc; + + #$svcnum=''; + + $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; + } + }, + + '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 + ) +%> +<%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/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..3b5114122 --- /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' => 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' => 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_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..d57979751 --- /dev/null +++ b/httemplate/edit/part_export.cgi @@ -0,0 +1,123 @@ +<% 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' ) { + $html .= qq!<SELECT NAME="$option">!; + foreach my $select_option ( @{$optinfo->{options}} ) { + #if ( ref($select_option) ) { + #} else { + my $selected = $select_option eq $value ? ' SELECTED' : ''; + $html .= qq!<OPTION VALUE="$select_option"$selected>!. + qq!$select_option</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..f40469937 --- /dev/null +++ b/httemplate/edit/part_pkg.cgi @@ -0,0 +1,637 @@ +<% 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', + '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', + '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', + }, + + 'fields' => [ + { field=>'clone', type=>'hidden', + curr_value_callback => + sub { shift->param('clone') }, + }, + { field=>'pkgnum', type=>'hidden', + curr_value_callback => + sub { shift->param('pkgnum') }, + }, + + { 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' }, + {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 => 'Line-item revenue recogition', #better name? + }, + { field=>'pay_weight', type=>'text', size=>6 }, + { field=>'credit_weight', type=>'text', size=>6 }, + + { 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 => 'columnend' }, + + { 'type' => 'tablebreak-tr-title', + 'value' => 'Pricing add-ons', + }, + { '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'), + }, + + { 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'; + +my $agent_clone_extra_sql = + ' ( '. FS::part_pkg->curuser_pkgs_sql. + #kludge to clone custom customer packages you otherwise couldn't see + " OR ( part_pkg.disabled = 'Y' AND part_pkg.comment LIKE '(CUSTOM)%' ) ". + ' ) '; + +my $conf = new FS::Conf; +my $taxproducts = $conf->exists('enable_taxproducts'); + +#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'=>$1}); + + foreach ($object->options) { + /^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1); + } + foreach ($object->part_pkg_taxoverride) { + $taxproductnums{$_->usage_class} = 1 + if $_->usage_class; + } + + %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 did anyway + $object->comment( '(CUSTOM) '. $object->comment ) + unless $object->comment =~ /^\(CUSTOM\) /; + + $object->disabled('Y'); + + } + + %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 { + new FS::part_pkg_link { + 'link_type' => $link_type, + 'src_pkgpart' => $object->pkgpart, + 'dst_pkgpart' => $_, + }; + } + grep $_, + map $cgi->param($_), + grep /^${link_type}_dst_pkgpart(\d+)$/, $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 ) { + + $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">!; + + 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_taxclass.html b/httemplate/edit/part_pkg_taxclass.html new file mode 100644 index 000000000..e76705722 --- /dev/null +++ b/httemplate/edit/part_pkg_taxclass.html @@ -0,0 +1,32 @@ +<% include('/elements/header.html', "$action taxclass") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/part_pkg_taxclass.html" METHOD=POST> + +<INPUT TYPE="hidden" NAME="taxclassnum" VALUE=""> + +Tax class <INPUT TYPE="text" NAME="taxclass" VALUE="<% $taxclass |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 = ''; +if ( $cgi->param('error') ) { + $taxclass = $cgi->param('taxclass'); +} + +my $action = 'Add'; + +my $p1 = popurl(1); + +</%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..e0fb615b1 --- /dev/null +++ b/httemplate/edit/part_svc.cgi @@ -0,0 +1,361 @@ +<% 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_www - Virtual domain website + <LI>svc_broadband - Broadband/High-speed Internet service (always-on) + <LI>svc_phone - Customer phone numbers + <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} }, +% }, +%# need to template-ize httemplate/edit/svc_* first +%# '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" COLSPAN=2>Modifier</TH>'. +% '</TR>'; +% +% my $bgcolor1 = '#eeeeee'; +% my $bgcolor2 = '#ffffff'; +% my $bgcolor; +% +% #yucky kludge +% my @fields = defined( dbdef->table($layer) ) +% ? grep { $_ ne 'svcnum' } fields($layer) +% : (); +% push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge +% $part_svc->svcpart($clone) if $clone; #haha, undone below +% +% +% foreach my $field (@fields) { +% +% #my $def = $defs{$layer}{$field}; +% my $def = FS::part_svc->svc_table_fields($layer)->{$field}; +% my $label = $def->{'def_label'} || $def->{'label'}; +% my $formatter = $def->{'format'} || sub { shift }; +% my $part_svc_column = $part_svc->part_svc_column($field); +% 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 CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!. +% ( $label || $field ). +% "</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 ) { +% +% #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 '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 '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 'disabled' ) { +% +% $html .= +% qq!<INPUT TYPE="hidden" NAME="${layer}__${field}" VALUE="">!; +% +% } else { +% +% $html .= '<font color="#ff0000">unknown type'. $def->{type}; +% +% } +% +% $html .= "</TD></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_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..e3893cf49 --- /dev/null +++ b/httemplate/edit/payment_gateway.html @@ -0,0 +1,132 @@ +<% include("/elements/header.html","$action Payment gateway", menubar( + 'View all payment gateways' => $p. 'browse/payment_gateway.html', +)) %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%popurl(1)%>process/payment_gateway.html" METHOD=POST> +<INPUT TYPE="hidden" NAME="gatewaynum" VALUE="<% $payment_gateway->gatewaynum %>"> +Gateway #<% $payment_gateway->gatewaynum || "(NEW)" %> + +<% ntable('#cccccc', 2, '') %> + +<TR> + <TH ALIGN="right">Gateway: </TH> + <TD> +% if ( $payment_gateway->gatewaynum ) { + + + <% $payment_gateway->gateway_module %> + <INPUT TYPE="hidden" NAME="gateway_module" VALUE="<% $payment_gateway->gateway_module %>"> +% } else { + + + <SELECT NAME="gateway_module" SIZE=1> +% foreach my $module ( qw( +% 2CheckOut +% AuthorizeNet +% BankOfAmerica +% Beanstream +% Capstone +% Cardstream +% CashCow +% CyberSource +% eSec +% eSelectPlus +% Exact +% iAuthorizer +% IPaymentTPG +% Jettis +% LinkPoint +% MerchantCommerce +% Network1Financial +% OCV +% OpenECHO +% PayConnect +% PayflowPro +% PaymentsGateway +% PXPost +% SecureHostingUPG +% Skipjack +% StGeorge +% SurePay +% TCLink +% TransactionCentral +% TransFirsteLink +% VirtualNet +% ) ) { +% + + <OPTION VALUE="<% $module %>"><% $module %> +% } + + </SELECT> +% } + + + </TD> +</TR> + +<TR> + <TH ALIGN="right">Username: </TH> + <TD><INPUT TYPE="text" NAME="gateway_username" VALUE="<% $payment_gateway->gateway_username %>"></TD> +</TR> + +<TR> + <TH ALIGN="right">Password: </TH> + <TD><INPUT TYPE="text" NAME="gateway_password" VALUE="<% $payment_gateway->gateway_password %>"></TD> +</TR> + +<TR> + <TH ALIGN="right">Action: </TH> + <TD> + <SELECT NAME="gateway_action" SIZE=1> +% foreach my $action ( +% 'Normal Authorization', +% 'Authorization Only', +% 'Authorization Only, Post Authorization', +% ) { +% + + <OPTION VALUE="<% $action %>"<% $action eq $payment_gateway->gateway_action ? ' SELECTED' : '' %>><% $action %> +% } + + </SELECT> + </TD> +</TR> + +<TR> + <TH ALIGN="right">Options: (Name/Value pairs, one element per line)</TH> + <TD> + <TEXTAREA ROWS="5" NAME="gateway_options"><% join("\r", $payment_gateway->options ) %></TEXTAREA> + </TD> +</TR> + +</TABLE> + +<BR><INPUT TYPE="submit" VALUE="<% $payment_gateway->gatewaynum ? "Apply changes" : "Add gateway" %>"> + </FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $payment_gateway; +if ( $cgi->param('error') ) { + $payment_gateway = new FS::payment_gateway ( { + map { $_, scalar($cgi->param($_)) } fields('payment_gateway') + } ); +} elsif ( $cgi->keywords ) { + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/; + $payment_gateway = qsearchs( 'payment_gateway', { 'gatewaynum' => $1 } ); +} else { #adding + $payment_gateway = new FS::payment_gateway {}; +} +my $action = $payment_gateway->gatewaynum ? 'Edit' : 'Add'; +#my $hashref = $payment_gateway->hashref; + +</%init> diff --git a/httemplate/edit/pkg_category.html b/httemplate/edit/pkg_category.html new file mode 100644 index 000000000..fdc8da638 --- /dev/null +++ b/httemplate/edit/pkg_category.html @@ -0,0 +1,22 @@ +<% include( 'elements/edit.html', + 'name' => 'Package Category', + 'table' => 'pkg_category', + 'fields' => [ + 'categoryname', + { field=>'disabled', type=>'checkbox', value=>'Y', }, + ], + 'labels' => { + 'categorynum' => 'Category number', + 'categoryname' => 'Category name', + 'disabled' => 'Disable category', + }, + 'viewall_dir' => 'browse', + ) + +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/pkg_class.html b/httemplate/edit/pkg_class.html new file mode 100644 index 000000000..26bc8baa5 --- /dev/null +++ b/httemplate/edit/pkg_class.html @@ -0,0 +1,28 @@ +<% include( 'elements/edit.html', + 'name' => 'Package Class', + 'table' => 'pkg_class', + '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', + ) + +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my @category = qsearch('pkg_category', { 'disabled' => '' }); +</%init> diff --git a/httemplate/edit/prepay_credit.cgi b/httemplate/edit/prepay_credit.cgi new file mode 100644 index 000000000..9e1c30ba6 --- /dev/null +++ b/httemplate/edit/prepay_credit.cgi @@ -0,0 +1,110 @@ +<% 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 = ''; }"> + +<SELECT NAME="type"> +% foreach (qw(alpha alphanumeric numeric)) { + <OPTION<% $cgi->param('type') eq $_ ? ' SELECTED' : '' %>><% $_ %> +% } +</SELECT> + +prepaid cards + +<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', + 1000 => 'Kbytes', + 1000000 => 'Mbytes', + 1000000000 => 'Gbytes', +; + +$cgi->param('multiplier', '60') unless $cgi->param('multiplier'); +$cgi->param('upmultiplier', '1000000') unless $cgi->param('upmultiplier'); +$cgi->param('downmultiplier', '1000000') unless $cgi->param('downmultiplier'); +$cgi->param('totalmultiplier','1000000') 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..ebcb7e4ba --- /dev/null +++ b/httemplate/edit/process/REAL_cust_pkg.cgi @@ -0,0 +1,36 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "REAL_cust_pkg.cgi?". $cgi->query_string ) %> +%} else { +% my $custnum = $new->custnum; +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum#cust_pkg$pkgnum" ) %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->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{'setup'} = $cgi->param('setup') ? str2time($cgi->param('setup')) : ''; +$hash{'bill'} = $cgi->param('bill') ? str2time($cgi->param('bill')) : ''; +$hash{'last_bill'} = + $cgi->param('last_bill') ? str2time($cgi->param('last_bill')) : ''; +$hash{'adjourn'} = $cgi->param('adjourn') ? str2time($cgi->param('adjourn')) : ''; +$hash{'expire'} = $cgi->param('expire') ? str2time($cgi->param('expire')) : ''; + +my $new; +my $error; +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 + ) +{ + $error = '_bill_areyousure'; +} 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..ab25cb3a2 --- /dev/null +++ b/httemplate/edit/process/access_group.html @@ -0,0 +1,28 @@ +% if ( $conf->exists('disable_acl_changes') ) { + ACL changes disabled in public demo. +% } else { +<% 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'); + +my $conf = new FS::Conf; + +</%init> diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html new file mode 100644 index 000000000..ca6bb603f --- /dev/null +++ b/httemplate/edit/process/access_user.html @@ -0,0 +1,21 @@ +% 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'); + +</%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..3cdf40c9b --- /dev/null +++ b/httemplate/edit/process/agent.cgi @@ -0,0 +1,16 @@ +<% 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'); + +</%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..e05192e96 --- /dev/null +++ b/httemplate/edit/process/bulk-cust_main_county.html @@ -0,0 +1,63 @@ +% if ( $error ) { #better to redirect back to +%# <% $cgi->redirect("$url?". $cgi->query_string ) %> + <% include('/elements/header-popup.html', 'Error adding taxes' ) %> + + <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT> + <BR><BR> + + </BODY> + </HTML> + +% } else { + <% include('/elements/header-popup.html', 'Taxes added') %> + + <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); + +my $error = ''; +foreach my $taxnum ( @taxnum ) { + + my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } ) + or die "unknown taxnum: $taxnum"; + + if ( $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/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_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..c0f34ae5b --- /dev/null +++ b/httemplate/edit/process/cust_credit_bill.cgi @@ -0,0 +1,13 @@ +<% 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'); + +</%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..097d38204 --- /dev/null +++ b/httemplate/edit/process/cust_main.cgi @@ -0,0 +1,210 @@ +% 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 $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 $payby = $cgi->param('select'); # XXX key + +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 + ); +} + +if ( $cgi->param('birthdate') && $cgi->param('birthdate') =~ /^([ 0-9\-\/]{0,10})$/) { + my $conf = new FS::Conf; + 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($1); + if ($dt) { + $new->setfield('birthdate', $dt->epoch); + $cgi->param('birthdate', $dt->epoch); + } else { +# $error ||= $cgi->param('birthdate') . " is an invalid birthdate:" . $parser->errmsg; + $error ||= "Invalid birthdate: " . $cgi->param('birthdate') . "."; + $cgi->param('birthdate', ''); + } +} + +$new->setfield('paid', $cgi->param('paid') ) + if $cgi->param('paid'); + +#perhaps this stuff should go to cust_main.pm +my $cust_pkg = ''; +my $svc_acct = ''; +if ( $new->custnum eq '' ) { + + 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); + #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 }); + #my($type_pkgs); + #foreach $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) { + # my($pkgpart)=$type_pkgs->pkgpart; + # $part_pkg{$pkgpart}++; + #} + # $pkgpart_href->{PKGPART} is true iff $custnum may purchase $pkgpart + my $pkgpart_href = $agent->pkgpart_hashref; + #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 }; + + $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 %svc_acct = ( + 'svcpart' => $svcpart, + 'username' => $cgi->param('username'), + '_password' => $cgi->param('_password'), + 'popnum' => $cgi->param('popnum'), + ); + $svc_acct{'domsvc'} = $cgi->param('domsvc') + if $cgi->param('domsvc'); + + $svc_acct = new FS::svc_acct \%svc_acct; + + #and just in case you were silly + $svc_acct->svcpart($svcpart); + $svc_acct->username($cgi->param('username')); + $svc_acct->_password($cgi->param('_password')); + $svc_acct->popnum($cgi->param('popnum')); + + #$error ||= $svc_acct->check; + + } elsif ( $cgi->param('username') ) { #good thing to catch + $error = "Can't assign username without a package!"; + } + + use Tie::RefHash; + tie my %hash, 'Tie::RefHash'; + %hash = ( $cust_pkg => [ $svc_acct ] ) if $cust_pkg; + $error ||= $new->insert( \%hash, \@invoicing_list ); + + 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); + + warn "$me returned from replace" if $DEBUG; + +} + +</%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..a917825ce --- /dev/null +++ b/httemplate/edit/process/cust_main_county-collapse.cgi @@ -0,0 +1,44 @@ +% +% +%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 +% +%foreach my $delete ( qsearch('cust_main_county', { +% 'country' => $cust_main_county->country, +% 'state' => $cust_main_county->state +% } ) ) { +%# 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(''); +%$cust_main_county->county(''); +%my $error = $cust_main_county->insert; +%die $error if $error; +% +%print $cgi->redirect(popurl(3). "browse/cust_main_county.cgi"); +% +% +<%init> + +#this isn't actually linked from anywhere just now, but it will be again soon + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + + +</%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..04533a539 --- /dev/null +++ b/httemplate/edit/process/cust_main_county-expand.cgi @@ -0,0 +1,78 @@ +<% 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',$_); + } else { + $new->setfield('county',$_); + } + my $error = $new->insert; + die $error if $error; +} + +unless ( qsearch( 'cust_main', { + 'state' => $cust_main_county->state, + 'county' => $cust_main_county->county, + '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?". + 'state='. uri_escape($cust_main_county->state ).';'. + 'county='. uri_escape($cust_main_county->county ).';'. + '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..5127c72d1 --- /dev/null +++ b/httemplate/edit/process/cust_main_note.cgi @@ -0,0 +1,54 @@ +%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 $otaker = $FS::CurrentUser::CurrentUser->name; +$otaker = $FS::CurrentUser::CurrentUser->username + if ($otaker eq "User, Legacy"); + +my $new = new FS::cust_main_note ( { + notenum => $notenum, + custnum => $custnum, + _date => time, + otaker => $otaker, + comments => $cgi->param('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..647f6fc6c --- /dev/null +++ b/httemplate/edit/process/cust_pay.cgi @@ -0,0 +1,55 @@ +%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; +% } +% 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 = str2time($cgi->param('_date')); + +my $new = new FS::cust_pay ( { + $field => $linknum, + _date => $_date, + map { + $_, scalar($cgi->param($_)); + } qw(paid payby payinfo paybatch) + #} 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_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/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/elements/ApplicationCommon.html b/httemplate/edit/process/elements/ApplicationCommon.html new file mode 100644 index 000000000..2782dc231 --- /dev/null +++ b/httemplate/edit/process/elements/ApplicationCommon.html @@ -0,0 +1,77 @@ +<%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 $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 $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 { + + my $class = 'FS::'. $opt{link_table}; + + $new = $class->new( { + map { + $_ => scalar($cgi->param($_)); + } fields($opt{link_table}) + } ); + +#} + +my $error = $new->insert; + +</%init> diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html new file mode 100644 index 000000000..5befdd337 --- /dev/null +++ b/httemplate/edit/process/elements/process.html @@ -0,0 +1,268 @@ +<%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', + + + }, + + #checks CGI params and whatever else before much else runs + #return an error string or empty for no error + 'precheck_callback' => sub { my( $cgi ) = @_; }, + + #supplies arguments to insert() and replace() + # for use with tables that are FS::option_Common + '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'} ) { + die "illegal agentnum" + unless $curuser->agentnums_href->{$new->agentnum} + 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 ) { + $cgi->param('error', $error); + if ( $opt{'clear_on_error'} && scalar(@{$opt{'clear_on_error'}}) ) { + foreach my $field (@{$opt{'clear_on_error'}}) { + $cgi->param($field, '') + } + } +} + +</%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..8e8c99a42 --- /dev/null +++ b/httemplate/edit/process/elements/svc_Common.html @@ -0,0 +1,15 @@ +% +% +% my %opt = @_; +% my $table = $opt{'table'}; +% $opt{'fields'} ||= [ fields($table) ]; +% push @{ $opt{'fields'} }, qw( pkgnum svcpart ); +% +% +<% include( 'process.html', + 'edit_ext' => 'cgi', + 'redirect' => popurl(3)."view/$table.cgi?", + %opt, + ) +%> + 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/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_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..b5f82e892 --- /dev/null +++ b/httemplate/edit/process/part_export.cgi @@ -0,0 +1,41 @@ +%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 $value = $cgi->param($_); + $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..96c5b36b7 --- /dev/null +++ b/httemplate/edit/process/part_pkg.cgi @@ -0,0 +1,198 @@ +<% 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 ); + } + @options; + + foreach ( split(',', $cgi->param('taxproductnums') ) ) { + my $value = $cgi->param("taxproductnum_$_"); + $error ||= "Illegal taxproductnum_$_: $value" + unless ( $value =~ /^\d*$/ ); + $options{"usage_taxproductnum_$_"} = $value; + } + + $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; + popurl(3). "view/cust_main.cgi?keywords=$custnum;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' => 'bill' }, + 'params' => [ map $cgi->param($_), grep /^bill_dst_pkgpart/, $cgi->param ], + }, + { 'link_table' => 'part_pkg_link', + 'target_table' => 'part_pkg', + 'base_field' => 'src_pkgpart', + 'target_field' => 'dst_pkgpart', + 'hashref' => { 'link_type' => 'svc' }, + 'params' => [ map $cgi->param($_), grep /^svc_dst_pkgpart/, $cgi->param ], + }, +); + +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_taxclass.html b/httemplate/edit/process/part_pkg_taxclass.html new file mode 100644 index 000000000..8f149bb94 --- /dev/null +++ b/httemplate/edit/process/part_pkg_taxclass.html @@ -0,0 +1,53 @@ +% if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "part_pkg_taxclass.html?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/cust_main_county.cgi?taxclass=". uri_escape($part_pkg_taxclass->taxclass) ) %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $part_pkg_taxclass = new FS::part_pkg_taxclass { + 'taxclass' => $cgi->param('taxclass'), +}; + +#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 part_pkg_taxclass... + +my $error = $part_pkg_taxclass->insert; + +unless ( $error ) { + #auto-add the new taxclass to any regions that have taxclasses already + + my $sth = dbh->prepare(" + SELECT country, state, county FROM cust_main_county + WHERE taxclass IS NOT NULL AND taxclass != '' + GROUP BY country, state, county + ") 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::cust_main_county { + 'country' => $row->{country}, + 'state' => $row->{state}, + 'county' => $row->{county}, + 'tax' => 0, + 'taxclass' => $part_pkg_taxclass->taxclass, + #exempt_amount + #taxname + #setuptax + #recurtax + }; + $error = $cust_main_county->insert; + #last if $error; + die $error if $error; + } + + +} + +</%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/payment_gateway.html b/httemplate/edit/process/payment_gateway.html new file mode 100644 index 000000000..b16bc3d27 --- /dev/null +++ b/httemplate/edit/process/payment_gateway.html @@ -0,0 +1,35 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "payment_gateway.html?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/payment_gateway.html") %> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $gatewaynum = $cgi->param('gatewaynum'); + +my $old = qsearchs('payment_gateway',{'gatewaynum'=>$gatewaynum}) if $gatewaynum; + +my $new = new FS::payment_gateway ( { + map { + $_, scalar($cgi->param($_)); + } fields('payment_gateway') +} ); + +my @options = split(/\r?\n/, $cgi->param('gateway_options') ); +pop @options + if scalar(@options) % 2 && $options[-1] =~ /^\s*$/; +my %options = @options; + +my $error; +if ( $gatewaynum ) { + $error=$new->replace($old, \%options); +} else { + $error=$new->insert(\%options); + $gatewaynum=$new->getfield('gatewaynum'); +} + +</%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..8f2eb2b25 --- /dev/null +++ b/httemplate/edit/process/prepay_credit.cgi @@ -0,0 +1,62 @@ +%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')), + $hashref + ); + +</%init> diff --git a/httemplate/edit/process/quick-charge.cgi b/httemplate/edit/process/quick-charge.cgi new file mode 100644 index 000000000..8fa57ddea --- /dev/null +++ b/httemplate/edit/process/quick-charge.cgi @@ -0,0 +1,68 @@ +% 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, + '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..9c2474330 --- /dev/null +++ b/httemplate/edit/process/quick-cust_pkg.cgi @@ -0,0 +1,63 @@ +%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; +<% 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?keywords=$custnum;fragment=$frag#$frag" %>'; + + </SCRIPT> + + </BODY></HTML> +%} +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->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; + +my $cust_pkg = new FS::cust_pkg { + 'custnum' => $custnum, + 'pkgpart' => $pkgpart, + 'refnum' => $refnum, + 'locationnum' => $locationnum, +}; + +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..882991e9d --- /dev/null +++ b/httemplate/edit/process/rate_region.cgi @@ -0,0 +1,57 @@ +%if ( $error ) { +% $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "rate_region.cgi?". $cgi->query_string ) %> +%} 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 $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; + +my @dest_detail = map { + my $ratenum = $_->ratenum; + new FS::rate_detail { + 'ratenum' => $ratenum, + map { $_ => $cgi->param("$_$ratenum") } + qw( min_included min_charge sec_granularity classnum ) + }; +} qsearch('rate', {} ); + + +my $error; +if ( $regionnum ) { + $error = $new->replace($old, 'rate_prefix' => \@rate_prefix, + 'dest_detail' => \@dest_detail, ); +} else { + $error = $new->insert( 'rate_prefix' => \@rate_prefix, + 'dest_detail' => \@dest_detail, ); + $regionnum = $new->getfield('regionnum'); +} + +</%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..0a89e253c --- /dev/null +++ b/httemplate/edit/process/svc_acct.cgi @@ -0,0 +1,64 @@ +%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> + +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 passwd +if ( $cgi->param('_password') eq '*HIDDEN*' ) { + die "fatal: no previous account to recall hidden password from!" unless $old; + $cgi->param('_password',$old->getfield('_password')); +} + +#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($_)) ); +} + +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; +if ( $svcnum ) { + foreach (grep { $old->$_ != $new->$_ } qw( seconds upbytes downbytes totalbytes )) { + my %hash = map { $_ => $new->$_ } + grep { $new->$_ } + qw( seconds upbytes downbytes totalbytes ); + + $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..9993a879e --- /dev/null +++ b/httemplate/edit/process/svc_domain.cgi @@ -0,0 +1,33 @@ +%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; + +my $new = new FS::svc_domain ( { + map { + $_, scalar($cgi->param($_)); + #} qw(svcnum pkgnum svcpart domain action purpose) + } ( fields('svc_domain'), qw( pkgnum svcpart action purpose ) ) +} ); + +my $error = ''; +if ($cgi->param('svcnum')) { + $error="Can't modify a domain!"; +} 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_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_phone.html b/httemplate/edit/process/svc_phone.html new file mode 100644 index 000000000..27a703cdf --- /dev/null +++ b/httemplate/edit/process/svc_phone.html @@ -0,0 +1,10 @@ +<% include( 'elements/svc_Common.html', + 'table' => 'svc_phone', + ) +%> +<%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_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/quick-charge.html b/httemplate/edit/quick-charge.html new file mode 100644 index 000000000..c18b2bc64 --- /dev/null +++ b/httemplate/edit/quick-charge.html @@ -0,0 +1,197 @@ +<% include("/elements/header-popup.html", 'One-time charge', '', + ( $cgi->param('error') ? '' : 'onload="addRow()"' ), + ) +%> + +<% 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; +} + +</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">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; + +$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; +} + +my $quantity = 1; +if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) { + $quantity = $1; +} + +$cgi->param('pkg') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ + or die 'illegal description'; +my $pkg = $1; + +</%init> diff --git a/httemplate/edit/rate.cgi b/httemplate/edit/rate.cgi new file mode 100644 index 000000000..4c0abfe01 --- /dev/null +++ b/httemplate/edit/rate.cgi @@ -0,0 +1,43 @@ +<% include("/elements/header.html","$action Rate plan", menubar( + 'View all rate plans' => "${p}browse/rate.cgi", + )) +%> + +<% include('/elements/progress-init.html', + 'OneTrueForm', + [ '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 NAME="submit" TYPE="button" VALUE="<% + $rate->ratenum ? "Apply changes" : "Add rate plan" +%>" onClick="document.OneTrueForm.submit.disabled=true; process();"> + +</FORM> + +<% 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..dd8c3f6b3 --- /dev/null +++ b/httemplate/edit/rate_detail.html @@ -0,0 +1,63 @@ +<% include('elements/edit.html', + 'popup' => 1, + 'name' => $name, + 'table' => 'rate_detail', + 'labels' => { 'ratedetailnum' => 'Rate', #should hide... + 'dest_regionname' => 'Region', + 'dest_prefixes_short' => 'Prefix(es)', + 'min_included' => 'Included minutes/calls', + '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=>'dest_regionname', type=>'fixed', }, + { field=>'dest_prefixes_short', type=>'fixed', }, + { field=>'min_included', type=>'text', size=>5 }, + { 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 => '' }, + }, + + ], + ) +%> +<%once> + +tie my %granularity, 'Tie::IxHash', FS::rate_detail::granularities(); + +</%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..9ca3a3569 --- /dev/null +++ b/httemplate/edit/rate_region.cgi @@ -0,0 +1,163 @@ +<% 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> + +%# rate plan info + +<BR> + +<% include('/elements/table-grid.html') %> +% my $bgcolor1 = '#eeeeee'; +% my $bgcolor2 = '#ffffff'; +% my $bgcolor = ''; + + <TR> + <TH CLASS="grid" BGCOLOR="#cccccc"> + Rate plan + </TH> + <TH CLASS="grid" BGCOLOR="#cccccc"> + <FONT SIZE=-1>Included<BR>minutes/calls</FONT> + </TH> + <TH CLASS="grid" BGCOLOR="#cccccc"> + <FONT SIZE=-1>Charge per<BR>minute/call</FONT> + </TH> + <TH CLASS="grid" BGCOLOR="#cccccc"> + <FONT SIZE=-1>Granularity</FONT> + </TH> + <TH CLASS="grid" BGCOLOR="#cccccc"> + <FONT SIZE=-1>Usage class</FONT> + </TH> + </TR> + +% foreach my $rate ( qsearch('rate', {}) ) { +% +% my $n = $rate->ratenum; +% my $rate_detail = $rate->dest_detail($rate_region) +% || new FS::rate_region { 'min_included' => 0, +% 'min_charge' => 0, +% 'sec_granularity' => '60' +% }; +% +% if ( $bgcolor eq $bgcolor1 ) { +% $bgcolor = $bgcolor2; +% } else { +% $bgcolor = $bgcolor1; +% } + + <TR> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> + <A HREF="<%$p%>edit/rate.cgi?<% $rate->ratenum %>"><% $rate->ratename %></A> + </TD> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> + <INPUT TYPE="text" SIZE=9 NAME="min_included<%$n%>" VALUE="<% $cgi->param("min_included$n") || $rate_detail->min_included |h %>"> + </TD> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> + $<INPUT TYPE="text" SIZE=6 NAME="min_charge<%$n%>" VALUE="<% $cgi->param("min_charge$n") || $rate_detail->min_charge |h %>"> + </TD> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> + <SELECT NAME="sec_granularity<%$n%>"> +% foreach my $granularity ( keys %granularity ) { + <OPTION VALUE="<%$granularity%>"<% $granularity == ( $cgi->param("sec_granularity$n") || $rate_detail->sec_granularity ) ? ' SELECTED' : '' %>><%$granularity{$granularity}%> +% } + </SELECT> + </TD> + + <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"> + <% include( '/elements/select-table.html', + 'element_name' => "classnum$n", + 'table' => 'usage_class', + 'name_col' => 'classname', + 'empty_label' => '(default)', + 'hashref' => { disabled => '' }, + 'curr_value' => ( $cgi->param("classnum$n") || + $rate_detail->classnum ), + ) + %> + </TD> + + </TR> + +% } + +</TABLE> + + +<BR><BR> +<INPUT TYPE="submit" VALUE="<% $rate_region->regionnum ? "Apply changes" : "Add region" %>"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +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/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..e57ac09bf --- /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 %> - <% $part_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..19e63b3e8 --- /dev/null +++ b/httemplate/edit/router.cgi @@ -0,0 +1,44 @@ +<% 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' }, + ], + 'error_callback' => $callback, + 'edit_callback' => $callback, + 'new_callback' => $callback, + ) +%> +<%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' => '' }, + }; + } +}; + +</%init> diff --git a/httemplate/edit/svc_Common.html b/httemplate/edit/svc_Common.html new file mode 100644 index 000000000..6666d9720 --- /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..58283ef54 --- /dev/null +++ b/httemplate/edit/svc_acct.cgi @@ -0,0 +1,452 @@ +<% 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> +% } + + +<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="_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>> + (blank to generate) + </TD> +</TR> +%}else{ + <INPUT TYPE="hidden" NAME="_password" VALUE="<% $password %>"> +%} +% +%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> +% } +% +%#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">GECOS</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 ( $part_svc->part_svc_column('quota')->columnflag eq 'F' ) { + + + <INPUT TYPE="hidden" NAME="quota" VALUE="<% $svc_acct->quota %>"> +% } else { + + + <TR> + <TD ALIGN="right">Quota:</TD> + <TD><INPUT TYPE="text" NAME="quota" VALUE="<% $svc_acct->quota %>"></TD> + </TR> +% } +% 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=''; + +} 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( $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 ( $svc_acct->_password ) { + if ( $conf->exists('showpasswords') || ! $svcnum ) { + $password = $svc_acct->_password; + } else { + $password = "*HIDDEN*"; + } +} else { + $password = ''; +} + +my $ulen = + $conf->exists('usernamemax') + ? $conf->config('usernamemax') + : dbdef->table('svc_acct')->column('username')->length; +my $ulen2 = $ulen+2; + +my $pmax = $conf->config('passwordmax') || 8; +my $pmax2 = $pmax+2; + +my $p1 = popurl(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..e60c76c90 --- /dev/null +++ b/httemplate/edit/svc_broadband.cgi @@ -0,0 +1,105 @@ +<% 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', + '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 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'; + + } else { + + if ($fieldref->{field} eq 'block_label') { + if ($fixedblock) { + $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 } @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..56ba604bf --- /dev/null +++ b/httemplate/edit/svc_domain.cgi @@ -0,0 +1,91 @@ +<% 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 %>"> + +<INPUT TYPE="radio" NAME="action" VALUE="N"<% $kludge_action eq 'N' ? ' CHECKED' : '' %>>New +<BR> + +<INPUT TYPE="radio" NAME="action" VALUE="M"<% $kludge_action eq 'M' ? ' CHECKED' : '' %>>Transfer + +<P>Domain <INPUT TYPE="text" NAME="domain" VALUE="<% $domain %>" SIZE=28 MAXLENGTH=63> + +<BR>Purpose/Description: <INPUT TYPE="text" NAME="purpose" VALUE="<% $purpose %>" SIZE=64> + +<P><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($svcnum, $pkgnum, $svcpart, $kludge_action, $purpose, $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'); + $purpose = $cgi->param('purpose'); + $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 = ''; + $purpose = ''; + 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 $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..0df842b21 --- /dev/null +++ b/httemplate/edit/svc_external.cgi @@ -0,0 +1,102 @@ +<% include('/elements/header.html', "External service $action") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/svc_external.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 $id = $svc_external->id; +% my $title = $svc_external->title; +% +<% &ntable("#cccccc",2) %> + <TR> + <TD ALIGN="right">External ID</TD> + <TD><INPUT TYPE="text" NAME="id" VALUE="<% $id %>"></TD> + </TR> + <TR> + <TD ALIGN="right">Title</TD> + <TD><INPUT TYPE="text" NAME="title" VALUE="<% $title %>"></TD> + </TR> + +% foreach my $field ($svc_external->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_external->pvf($field)->widget( 'HTML', + 'edit', + $svc_external->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( $svcnum, $pkgnum, $svcpart, $part_svc, $svc_external ); +if ( $cgi->param('error') ) { + + $svc_external = new FS::svc_external ( { + map { $_, scalar($cgi->param($_)) } fields('svc_external') + } ); + $svcnum = $svc_external->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_external = new FS::svc_external { svcpart => $svcpart }; + + $svcnum=''; + + $svc_external->set_default_and_fixed; + +} else { #adding + + my($query) = $cgi->keywords; + $query =~ /^(\d+)$/ or die "unparsable svcnum"; + $svcnum=$1; + $svc_external=qsearchs('svc_external',{'svcnum'=>$svcnum}) + or die "Unknown (svc_external) 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_external->svcnum ? 'Edit' : 'Add'; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/svc_forward.cgi b/httemplate/edit/svc_forward.cgi new file mode 100755 index 000000000..96a00a5aa --- /dev/null +++ b/httemplate/edit/svc_forward.cgi @@ -0,0 +1,175 @@ +<% 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><SELECT NAME="srcsvc" SIZE=1 onChange="srcchanged(this)"> +% foreach $_ (keys %email) { + + <OPTION<% $_ eq $srcsvc ? " SELECTED" : "" %> VALUE="<% $_ %>"><% $email{$_} %></OPTION> +% } +% if ( $svc_forward->dbdef_table->column('src') ) { + + <OPTION <% $src ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION> +% } + +</SELECT> +% if ( $svc_forward->dbdef_table->column('src') ) { + +<INPUT TYPE="text" NAME="src" VALUE="<% $src %>" <% ( $src || !scalar(%email) ) ? '' : '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_phone.cgi b/httemplate/edit/svc_phone.cgi new file mode 100644 index 000000000..d7629ab6f --- /dev/null +++ b/httemplate/edit/svc_phone.cgi @@ -0,0 +1,27 @@ +<% include( 'elements/svc_Common.html', + 'name' => 'Phone number', + 'table' => 'svc_phone', + 'fields' => [ 'countrycode', + { field => 'phonenum', + type => 'select-did', + label => 'Phone number', + }, + 'sip_password', + 'pin', + 'phone_name', + ], + 'labels' => { + 'countrycode' => 'Country code', + 'phonenum' => 'Phone number', + 'sip_password' => 'SIP password', + 'pin' => 'Voicemail PIN', + 'phone_name' => 'Name', + }, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +</%init> diff --git a/httemplate/edit/svc_www.cgi b/httemplate/edit/svc_www.cgi new file mode 100644 index 000000000..eeb6f678c --- /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..ef4b1fff4 --- /dev/null +++ b/httemplate/edit/usage_class.html @@ -0,0 +1,25 @@ +<% include( 'elements/edit.html', + 'name_singular' => 'Usage Class', + 'table' => 'usage_class', + 'fields' => [ + 'classname', + { field=>'disabled', + type=>'checkbox', + value=>'Y', + }, + ], + 'labels' => { + 'classnum' => 'Class number', + 'classname' => 'Class name', + 'disabled' => 'Disable class', + }, + 'viewall_dir' => 'browse', + ) + +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> |