diff options
Diffstat (limited to 'httemplate/edit')
162 files changed, 14661 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..c31213805 --- /dev/null +++ b/httemplate/edit/REAL_cust_pkg.cgi @@ -0,0 +1,221 @@ +<% include("/elements/header.html",'Customer package - Edit dates') %> + +%#, menubar( +%#  "View this customer (#$custnum)" => popurl(2). "view/cust_main.cgi?$custnum", +%#)); + +<LINK REL="stylesheet" TYPE="text/css" HREF="../elements/calendar-win2k-2.css" TITLE="win2k-2"> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar_stripped.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-en.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="../elements/calendar-setup.js"></SCRIPT> + +<FORM NAME="formname" ACTION="process/REAL_cust_pkg.cgi" METHOD="POST"> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> + +% # raw error from below +% if ( $error ) {  +  <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error %></FONT> +% }  +% #or, regular error handler +<% include('/elements/error.html') %> + +<% ntable("#cccccc",2) %> + +  <TR> +    <TD ALIGN="right">Package number</TD> +    <TD BGCOLOR="#ffffff"><% $cust_pkg->pkgnum %></TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Package</TD> +    <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Custom</TD> +    <TD BGCOLOR="#ffffff"><% $part_pkg->custom %></TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Comment</TD> +    <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Order taker</TD> +    <TD BGCOLOR="#ffffff"><% $cust_pkg->otaker %></TD> +  </TR> + +% if ( $cust_pkg->setup && ! $cust_pkg->start_date ) { +  <& .row_display, cust_pkg=>$cust_pkg, column=>'start_date',   label=>'Start' &> +% } else { +  <& .row_edit, cust_pkg=>$cust_pkg, column=>'start_date', label=>'Start' &> +% } + +  <& .row_edit, cust_pkg=>$cust_pkg, column=>'setup',     label=>'Setup' &> +  <& .row_edit, cust_pkg=>$cust_pkg, column=>'last_bill', label=>$last_bill_or_renewed &> +  <& .row_edit, cust_pkg=>$cust_pkg, column=>'bill',      label=>$next_bill_or_prepaid_until &> +  <& .row_display, cust_pkg=>$cust_pkg, column=>'adjourn',   label=>'Adjournment', note=>'(will <b>suspend</b> this package when the date is reached)' &> +  <& .row_display, cust_pkg=>$cust_pkg, column=>'susp',   label=>'Suspension' &> + +  <& .row_display, cust_pkg=>$cust_pkg, column=>'expire',   label=>'Expiration', note=>'(will <b>cancel</b> this package when the date is reached)' &> +  <& .row_display, cust_pkg=>$cust_pkg, column=>'cancel',   label=>'Cancellation' &> + +<%def .row_edit> +<%args> +  $cust_pkg +  $column +  $label +  $note => '' +</%args> +% my $value = $cust_pkg->get($column); +% $value = $value ? time2str($format, $value) : ""; + +  <TR> +    <TD ALIGN="right"><% $label %> date</TD> +    <TD> +      <INPUT TYPE  = "text" +             NAME  = "<% $column %>" +             SIZE  = 32 +             ID    = "<% $column %>_text" +             VALUE = "<% $value %>" +      > +      <IMG SRC   = "../images/calendar.png" +           ID    = "<% $column %>_button" +           STYLE = "cursor: pointer" +           TITLE = "Select date" +      > +%     if ( $note ) { +        <BR><FONT SIZE=-1><% $note %></FONT> +%     } +    </TD> +  </TR> + +  <SCRIPT TYPE="text/javascript"> +    Calendar.setup({ +      inputField: "<% $column %>_text", +      ifFormat:   "%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') =~ /^_/ ) { + +    my @errors = (); +    my %errors = map { $_=>1 } split(',', $cgi->param('error')); +    $cgi->param('error', ''); + +    if ( $errors{'_bill_areyousure'} ) { +      if ( $cgi->param('bill') =~ /^([\s\d\/\:\-\(\w\)]*)$/ ) { +        my $bill = $1; +        push @errors, +          "You are attempting to set the next bill date to $bill, which is +           in the past.  This will charge the customer for the interval +           from $bill until now.  Are you sure you want to do this? ". +          '<INPUT TYPE="checkbox" NAME="bill_areyousure" VALUE="1">'; +      } +    } + +    if ( $errors{'_setup_areyousure'} ) { +      push @errors, +        "You are attempting to remove the setup date.  This will re-charge the +         customer for the setup fee. Are you sure you want to do this? ". +        '<INPUT TYPE="checkbox" NAME="setup_areyousure" VALUE="1">'; +    } + +    if ( $errors{'_start'} ) { +      push @errors, +        "You are attempting to add a start date to a package that has already +         started billing."; +    } + +    $error = join('<BR><BR>', @errors ); + +  } + +  #get package record +  $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); +  die "No package!" unless $cust_pkg; + +  foreach my $col (qw( 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..a0af9fa44 --- /dev/null +++ b/httemplate/edit/agent.cgi @@ -0,0 +1,131 @@ +<% 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> + +% if ( $conf->exists('agent-invoice_template') ) { + +  <% include('/elements/tr-select-invoice_template.html', +               'label'      => 'Invoice template', +               'field'      => 'invoice_template', +               'curr_value' => $agent->invoice_template, +            ) +  %> + +% } else { + +  <INPUT TYPE="hidden" NAME="invoice_template" VALUE="<% $agent->invoice_template %>"> + +% } + +% if ( $conf->config('ticket_system') ) { +%    my $default_queueid = $conf->config('ticket_system-default_queueid'); +%    my $default_queue = FS::TicketSystem->queue($default_queueid); +%    $default_queue = "(default) $default_queueid: $default_queue" +%      if $default_queueid; +%    my %queues = FS::TicketSystem->queues(); +%    my @queueids = sort { $a <=> $b } keys %queues; +%   + +    <TR> +      <TD ALIGN="right">Ticketing queue</TD> +      <TD> +        <SELECT NAME="ticketing_queueid"> +          <OPTION VALUE=""><% $default_queue %> +% foreach my $queueid ( @queueids ) {  + +            <OPTION VALUE="<% $queueid %>" <% $agent->ticketing_queueid == $queueid ? ' SELECTED' : '' %>><% $queueid %>: <% $queues{$queueid} %> +% }  + +        </SELECT> +      </TD> +    </TR> +% }  + +  <TR> +    <TD ALIGN="right">Access Groups</TD> +    <TD><% include('/elements/checkboxes-table.html', +                     'source_obj'   => $agent, +                     'link_table'   => 'access_groupagent', +                     'target_table' => 'access_group', +                     'name_col'     => 'groupname', +                     'target_link'  => $p. 'edit/access_group.html?', +                  ) +        %> +    </TD> +  </TR> + +</TABLE> + +<BR> +<INPUT TYPE="submit" VALUE="<% $agent->agentnum ? "Apply changes" : "Add agent" %>"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $agent; +if ( $cgi->param('error') ) { +  $agent = new FS::agent ( { +    map { $_, scalar($cgi->param($_)) } fields('agent') +  } ); +} elsif ( $cgi->keywords ) { +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/; +  $agent = qsearchs( 'agent', { 'agentnum' => $1 } ); +} else { #adding +  $agent = new FS::agent {}; +} +my $action = $agent->agentnum ? 'Edit' : 'Add'; + +my $conf = new FS::Conf; + +</%init> diff --git a/httemplate/edit/agent_payment_gateway.html b/httemplate/edit/agent_payment_gateway.html new file mode 100644 index 000000000..4a7cedf79 --- /dev/null +++ b/httemplate/edit/agent_payment_gateway.html @@ -0,0 +1,68 @@ +<% include("/elements/header.html","$action payment gateway override for ". $agent->agent,  menubar( +  #'View all payment gateways' => $p. 'browse/payment_gateway.html', +  'View all agents' => $p. 'browse/agent.html', +)) %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%popurl(1)%>process/agent_payment_gateway.html" METHOD=POST> +<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>"> + +Use gateway <SELECT NAME="gatewaynum"> +% foreach my $payment_gateway ( +%      qsearch('payment_gateway', { 'disabled' => '' } ) +%    ) { +% + +  <OPTION VALUE="<% $payment_gateway->gatewaynum %>"><% $payment_gateway->gateway_module %> (<% $payment_gateway->gateway_username %>) +% }  + +</SELECT> +<BR><BR> + +for <SELECT NAME="cardtype" MULTIPLE> +% foreach my $cardtype ( +%  "", +%  "VISA card", +%  "MasterCard", +%  "Discover card", +%  "American Express card", +%  "Diner's Club/Carte Blanche", +%  "enRoute", +%  "JCB", +%  "BankCard", +%  "Switch", +%  "Solo", +%  'ACH', +%) {  + +  <OPTION VALUE="<% $cardtype %>"><% $cardtype || '(Default fallback)' %> +% }  + +</SELECT> +<BR><BR> + +(optional) when invoice contains only items of taxclass <INPUT TYPE="text" NAME="taxclass"> +<BR><BR> + +<INPUT TYPE="submit" VALUE="Add gateway override"> +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +$cgi->param('agentnum') =~ /(\d+)$/ or die "illegal agentnum"; +my $agent = qsearchs('agent', { 'agentnum' => $1 } ); +die "agentnum $1 not found" unless $agent; + +#my @agent_payment_gateway; +if ( $cgi->param('error') ) { +} + +my $action = 'Add'; + +</%init> diff --git a/httemplate/edit/agent_type.cgi b/httemplate/edit/agent_type.cgi new file mode 100755 index 000000000..8a6fbc255 --- /dev/null +++ b/httemplate/edit/agent_type.cgi @@ -0,0 +1,57 @@ +<% include("/elements/header.html","$action Agent Type", menubar( +  'View all agent types' => "${p}browse/agent_type.cgi", +)) +%> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% popurl(1) %>process/agent_type.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="typenum" VALUE="<% $agent_type->typenum %>"> +Agent Type #<% $agent_type->typenum || "(NEW)" %> +<BR> + +Agent Type +<INPUT TYPE="text" NAME="atype" SIZE=32 VALUE="<% $agent_type->atype %>"> +<BR><BR> + +Select which packages agents of this type may sell to customers<BR> +<% ntable("#cccccc", 2) %><TR><TD> +<% include('/elements/checkboxes-table.html', +              'source_obj'    => $agent_type, +              'link_table'    => 'type_pkgs', +              'target_table'  => 'part_pkg', +              'name_callback' => sub { $_[0]->pkg_comment(nopkgpart => 1); }, +              'target_link'   => $p.'edit/part_pkg.cgi?', +              'disable-able'  => 1, + +           ) +%> +</TD></TR></TABLE> +<BR> + +<INPUT TYPE="submit" VALUE="<% $agent_type->typenum ? "Apply changes" : "Add agent type" %>"> + +    </FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my($agent_type); +if ( $cgi->param('error') ) { +  $agent_type = new FS::agent_type ( { +    map { $_, scalar($cgi->param($_)) } fields('agent') +  } ); +} elsif ( $cgi->keywords ) { #editing +  my( $query ) = $cgi->keywords; +  $query =~ /^(\d+)$/; +  $agent_type=qsearchs('agent_type',{'typenum'=>$1}); +} else { #adding +  $agent_type = new FS::agent_type {}; +} +my $action = $agent_type->typenum ? 'Edit' : 'Add'; + +</%init> diff --git a/httemplate/edit/allocate.html b/httemplate/edit/allocate.html new file mode 100644 index 000000000..8d1347df2 --- /dev/null +++ b/httemplate/edit/allocate.html @@ -0,0 +1,33 @@ +<% include('elements/edit.html', +                'name'        => 'Allocation', +                'table'       => 'addr_block', +                'labels'      => { 'NetAddr'   => 'Block', +                                   'routernum' => 'Router', +                                 }, +                'fields'      => [ { 'field'         => 'NetAddr', +                                     'type'          => 'fixed', +                                   }, +                                   { 'field'         => 'routernum', +                                     'type'          => 'select-table', +                                     'table'         => 'router', +                                     'name_col'      => 'routername', +                                     'disable_empty' => 1, +                                     'agent_virt'    => 1, +                                     'agent_null_right' => +                                       'Broadband global configuration', +                                   }, +                                 ], +                'post_url'    => "process/addr_block/allocate.cgi", +                'popup'       => 1, +                'agent_virt'  => 1, +                'agent_null_right' => 'Broadband global configuration', +          ) +%> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" +  unless $curuser->access_right('Broadband configuration') +      || $curuser->access_right('Broadband global configuration'); + +</%init> diff --git a/httemplate/edit/bulk-cust_main_county.html b/httemplate/edit/bulk-cust_main_county.html new file mode 100644 index 000000000..8e447e54f --- /dev/null +++ b/httemplate/edit/bulk-cust_main_county.html @@ -0,0 +1,128 @@ +<% include('/elements/header-popup.html', $title ) %> + +<FORM ACTION="<% popurl(1)."process/bulk-cust_main_county.html" %>" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="action" VALUE="<% $action %>"> +<INPUT TYPE="hidden" NAME="taxnum" VALUE="<% join(',', @taxnum) %>"> + +<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + +<% include('/elements/tr-td-label.html', 'label' => 'Country' ) %> +  <TD BGCOLOR="#dddddd"><% $countries %> +  </TD> +</TR> + +<% include('/elements/tr-td-label.html', 'label' => 'State' ) %> +  <TD BGCOLOR="#dddddd"><% $states %> +  </TD> +</TR> + +% if ( $counties ) { +  <% include('/elements/tr-td-label.html', 'label' => 'County' ) %> +    <TD BGCOLOR="#dddddd"><% $counties %> +    </TD> +  </TR> +% } + +% if ( $conf->exists('enable_taxclasses') && $taxclasses ) { +  <% include('/elements/tr-td-label.html', 'label' => 'Tax Class' ) %> +    <TD BGCOLOR="#dddddd"><% $taxclasses %> +    </TD> +  </TR> +% } + +<% include('/elements/tr-input-text.html', +             'field' => 'taxname', +             'label' => 'Tax name' +          ) +%> + +<% include('/elements/tr-input-percentage.html', +             'field' => 'tax', +             'label' => 'Tax rate', +          ) +%> + +<% include('/elements/tablebreak-tr-title.html', value=>'Exemptions' ) %> + +<% include('/elements/tr-checkbox.html', +             'field' => 'setuptax', +             'value' => 'Y', +             'label' => 'This tax not applicable to setup fees', +          ) +%> + +<% include('/elements/tr-checkbox.html', +             'field' => 'recurtax', +             'value' => 'Y', +             'label' => 'This tax not applicable to recurring fees', +          ) +%> + +<% include('/elements/tr-input-money.html', +             'field' => 'exempt_amount', +             'label' => 'Monthly exemption per customer ($25 "Texas tax")', +          ) +%> + +</TABLE> + +<BR> + +<INPUT TYPE="submit" VALUE="Bulk <% $action %> tax"> + +<%init> + +my $conf = new FS::Conf; + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my @taxnum; +$cgi->param('taxnum') =~ /^([\d,]+)$/ +   or $m->comp('/elements/errorpage-popup.html', $cgi->param('error') || 'Nothing selected'); +my @taxnum = split(',', $1); + +$cgi->param('action') =~ /^(add|edit)$/ or die "unknown action"; +my $action = $1; +my $title = "Bulk $action tax rate"; + +my @cust_main_county = +  map { +        qsearchs('cust_main_county', { 'taxnum' => $_ }) +          or die "unknown taxnum $1"; +      } +      @taxnum; + +my %seen_country = {}; +my @countries = map  code2country($_)." ($_)", +                grep !$seen_country{$_}++, +                map  $_->country, +                     @cust_main_county; +my $countries = join(', ', @countries); + +my %seen_state = {}; +my @states = map state_label($_->[0], $_->[1]), +            grep !$seen_state{$_->[0]}++, +             map [ $_->state, $_->country ], +                 @cust_main_county; +my $states = join(', ', @states); + +my %seen_county = {}; +my @counties = grep !$seen_county{$_}++, map $_->county, @cust_main_county; +my $counties = join(', ', @counties); + +my %seen_taxclass = {}; +my @taxclasses = grep !$seen_taxclass{$_}++, map $_->taxclass, @cust_main_county; +my $taxclasses = join(', ', @taxclasses); + +#my @fields = ( +#  { field=>'country',  type=>'fixed-country', }, +#  { field=>'state',    type=>'fixed-state', }, +#  { field=>'county',   type=>'fixed', }, +#); + +#push @fields, { field=>'taxclass', type=>'fixed', } +#  if $conf->exists('enable_taxclasses'); + +</%init> diff --git a/httemplate/edit/bulk-cust_svc.html b/httemplate/edit/bulk-cust_svc.html new file mode 100644 index 000000000..a3c21b112 --- /dev/null +++ b/httemplate/edit/bulk-cust_svc.html @@ -0,0 +1,95 @@ +<% include('/elements/header.html', 'Bulk customer service change') %> + +<% include('/elements/init_overlib.html') %> + +<% include('/elements/progress-init.html', +              'OneTrueForm', +              [qw( old_svcpart new_svcpart pkgpart )], +              'process/bulk-cust_svc.cgi', +              $p.'browse/part_svc.cgi', +           ) +%> + +<FORM NAME="OneTrueForm"> +% +%  $cgi->param('svcpart') =~ /^(\d+)$/ +%    or die "illegal svcpart: ". $cgi->param('svcpart'); +% +%  my $old_svcpart = $1; +%  my $src_part_svc = qsearchs('part_svc', { 'svcpart' => $old_svcpart } ) +%    or die "unknown svcpart: $old_svcpart"; +% + + +<INPUT NAME="old_svcpart" TYPE="hidden" VALUE="<% $old_svcpart %>"> +Change <!-- customer +<B><% $src_part_svc->svcpart %>: <% $src_part_svc->svc %></B> services +<BR> +--> + +<SELECT NAME="pkgpart"> +% my $num_cust_svc = $src_part_svc->num_cust_svc;  +% if ( $num_cust_svc > 1 ) {  + +  <OPTION VALUE="">all <% $num_cust_svc %> <% $src_part_svc->svc %> services +% } else {  + +  <OPTION VALUE="">the <% $num_cust_svc %> <% $src_part_svc->svc %> service +% }  +% +%  my $num_unlinked = $src_part_svc->num_cust_svc(0); +%  if ( $num_unlinked ) { +% + +  <OPTION VALUE="0">the <% $num_unlinked %> unlinked <% $src_part_svc->svc %> services +% }  +% foreach my $schwartz ( +%     grep { $_->[1] } +%     map  { [ $_, $src_part_svc->num_cust_svc($_->pkgpart) ] } +%          qsearch('part_pkg', {} ) +%   ) { +%     my( $part_pkg, $num_cust_svc ) = @$schwartz; +% + +  <OPTION VALUE="<% $part_pkg->pkgpart %>">the <% $num_cust_svc %> +    <% $src_part_svc->svc %> service<% $num_cust_svc > 1 ? 's in' : ' in a' %> +    <% $part_pkg->pkg %> package<% $num_cust_svc > 1 ? 's' : '' %> +% }  + +</SELECT> +<BR> + +to new service definition +<SELECT NAME="new_svcpart"> +% foreach my $dest_part_svc ( +%     grep {    $_->svcpart != $old_svcpart +%            && $_->svcdb   eq $src_part_svc->svcdb +%          } +%          qsearch('part_svc', { 'disabled' => '' } ) +%   ) { +% + +  <OPTION VALUE="<% $dest_part_svc->svcpart %>"><% $dest_part_svc->svcpart %>: <% $dest_part_svc->svc %> +% }  + +</SELECT> +<BR> + +<BR> + +<SCRIPT TYPE="text/javascript"> +var confirm_change = '<P ALIGN="center"><B>Bulk customer service change - Are you sure?</B><BR><P ALIGN="CENTER" <INPUT TYPE="button" VALUE="Yes, make changes" onClick="process();">        <INPUT TYPE="BUTTON" VALUE="Cancel" onClick="cClick()">'; +</SCRIPT> + +<INPUT TYPE="button" VALUE="Bulk change customer services" onClick="overlib(confirm_change, CAPTION, 'Confirm bulk customer service change', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 128, TEXTSIZE, 3, BGCOLOR, '#ff0000', CGCOLOR, '#ff0000' );"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/cust_bill_pay.cgi b/httemplate/edit/cust_bill_pay.cgi new file mode 100755 index 000000000..532db6a6e --- /dev/null +++ b/httemplate/edit/cust_bill_pay.cgi @@ -0,0 +1,14 @@ +<% include('elements/ApplicationCommon.html', +     'form_action' => 'process/cust_bill_pay.cgi', +     'src_table'   => 'cust_pay', +     'src_thing'   => 'payment', +     'dst_table'   => 'cust_bill', +     'dst_thing'   => 'invoice', +   ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/cust_category.html b/httemplate/edit/cust_category.html new file mode 100644 index 000000000..18a6189bc --- /dev/null +++ b/httemplate/edit/cust_category.html @@ -0,0 +1,5 @@ +<% include( 'elements/category_Common.html', +              'name'   => 'Customer Category', +              'table'  => 'cust_category', +          ) +%> diff --git a/httemplate/edit/cust_class.html b/httemplate/edit/cust_class.html new file mode 100644 index 000000000..fdb58e687 --- /dev/null +++ b/httemplate/edit/cust_class.html @@ -0,0 +1,5 @@ +<% include( 'elements/class_Common.html', +              'name'   => 'Customer Class', +              'table'  => 'cust_class', +          ) +%> diff --git a/httemplate/edit/cust_credit.cgi b/httemplate/edit/cust_credit.cgi new file mode 100755 index 000000000..5785a05c0 --- /dev/null +++ b/httemplate/edit/cust_credit.cgi @@ -0,0 +1,78 @@ +<% 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> + +% if ( $conf->exists('pkg-balances') ) { +  <% include('/elements/tr-select-cust_pkg-balances.html', +               'custnum' => $custnum, +               'cgi'     => $cgi +            ) +  %> +% } else { +  <INPUT TYPE="hidden" NAME="pkgnum" VALUE=""> +% } + +</TABLE> + +<BR> + +<CENTER><INPUT TYPE="submit" ID="confirm_credit_button" VALUE="Enter credit" DISABLED></CENTER> + +</FORM> +</BODY> +</HTML> +<%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..fac7ef27c --- /dev/null +++ b/httemplate/edit/cust_main.cgi @@ -0,0 +1,321 @@ +<% include('/elements/header.html', +      "Customer $action", +      '', +      ' onUnload="myclose()"' #hmm, in billing.html +) %> + +<% include('/elements/error.html') %> + +<FORM NAME   = "CustomerForm" +      METHOD = "POST" +      ACTION = "<% popurl(1) %>process/cust_main.cgi" +%#      STYLE = "margin-bottom: 0" +%#      STYLE="margin-top: 0; margin-bottom: 0"> +> + +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> + +% if ( $custnum ) {  +  Customer #<B><% $cust_main->display_custnum %></B> -  +  <B><FONT COLOR="#<% $cust_main->statuscolor %>"> +    <% ucfirst($cust_main->status) %> +  </FONT></B> +  <BR><BR> +% }  + +%# agent, agent_custid, refnum (advertising source), referral_custnum +<% include('cust_main/top_misc.html', $cust_main, 'custnum' => $custnum ) %> + +%# birthdate +% if ( $conf->exists('cust_main-enable_birthdate') ) { +  <BR> +  <% include('cust_main/birthdate.html', $cust_main) %> +% } + +%# latitude and longitude +% if ( $conf->exists('cust_main-require_censustract') ) { +%   my ($latitude, $longitude) = $cust_main->service_coordinates; +%   $latitude ||= $conf->config('company_latitude') || ''; +%   $longitude ||= $conf->config('company_longitude') || ''; +  <INPUT NAME="latitude" TYPE="hidden" VALUE="<% $latitude |h %>"> +  <INPUT NAME="longitude" TYPE="hidden" VALUE="<% $longitude |h %>"> +% } + +%# contact info + +%  my $same_checked = ''; +%  my $ship_disabled = ''; +%  my @ship_style = (); +%  unless ( $cust_main->ship_last && $same ne 'Y' ) { +%    $same_checked = 'CHECKED'; +%    $ship_disabled = 'DISABLED'; +%    push @ship_style, 'background-color:#dddddd'; +%    foreach ( +%      qw( last first company address1 address2 city county state zip country +%          daytime night fax ) +%    ) { +%      $cust_main->set("ship_$_", $cust_main->get($_) ); +%    } +%  } + +<BR> +<FONT SIZE="+1"><B>Billing address</B></FONT> + +<% include('cust_main/contact.html', +             'cust_main'    => $cust_main, +             'pre'          => '', +             'onchange'     => 'bill_changed(this)', +             'disabled'     => '', +             'ss'           => $ss, +             'stateid'      => $stateid, +             'same_checked' => $same_checked, #for address2 "Unit #" labeling +          ) +%> + +<SCRIPT> +function bill_changed(what) { +  if ( what.form.same.checked ) { +% for (qw( last first company address1 address2 city zip daytime night fax )) {  +    what.form.ship_<%$_%>.value = what.form.<%$_%>.value; +% }  + +    what.form.ship_country.selectedIndex = what.form.country.selectedIndex; + +    function fix_ship_city() { +      what.form.ship_city_select.selectedIndex = what.form.city_select.selectedIndex; +      what.form.ship_city.style.display = what.form.city.style.display; +      what.form.ship_city_select.style.display = what.form.city_select.style.display; +    } + +    function fix_ship_county() { +      what.form.ship_county.selectedIndex = what.form.county.selectedIndex; +      ship_county_changed(what.form.ship_county, fix_ship_city ); +    } + +    function fix_ship_state() { +      what.form.ship_state.selectedIndex = what.form.state.selectedIndex; +      ship_state_changed(what.form.ship_state, fix_ship_county ); +    } + +    ship_country_changed(what.form.ship_country, fix_ship_state ); + +  } +} +function samechanged(what) { +  if ( what.checked ) { +    bill_changed(what); + +%   my @fields = qw( last first company address1 address2 city city_select county state zip country daytime night fax ); +%   for (@fields) {  +      what.form.ship_<%$_%>.disabled = true; +      what.form.ship_<%$_%>.style.backgroundColor = '#dddddd'; +%   }  + +%   if ( $conf->exists('cust_main-require_address2') ) { +      document.getElementById('address2_required').style.visibility = ''; +      document.getElementById('address2_label').style.visibility = ''; +      document.getElementById('ship_address2_required').style.visibility = 'hidden'; +      document.getElementById('ship_address2_label').style.visibility = 'hidden'; +%   } + +  } else { + +%   for (@fields) {  +      what.form.ship_<%$_%>.disabled = false; +      what.form.ship_<%$_%>.style.backgroundColor = '#ffffff'; +%   }  + +%   if ( $conf->exists('cust_main-require_address2') ) { +      document.getElementById('address2_required').style.visibility = 'hidden'; +      document.getElementById('address2_label').style.visibility = 'hidden'; +      document.getElementById('ship_address2_required').style.visibility = ''; +      document.getElementById('ship_address2_label').style.visibility = ''; +%   } + +  } +} +</SCRIPT> + +<BR> +<FONT SIZE="+1"><B>Service address</B></FONT> + +(<INPUT TYPE="checkbox" NAME="same" VALUE="Y" onClick="samechanged(this)" <%$same_checked%>>same as billing address) +<% include('cust_main/contact.html', +             'cust_main' => $cust_main, +             'pre'       => 'ship_', +             'onchange'  => '', +             'disabled'  => $ship_disabled, +             'style'     => \@ship_style +          ) +%> + +%# billing info +<% include( 'cust_main/billing.html', $cust_main, +               'payinfo'        => $payinfo, +               'invoicing_list' => \@invoicing_list, +           ) +%> + +% my $ro_comments = $conf->exists('cust_main-use_comments')?'':'readonly'; +% if (!$ro_comments || $cust_main->comments) { + +    <BR>Comments +    <% &ntable("#cccccc") %> +      <TR> +        <TD> +          <TEXTAREA NAME = "comments" +                    COLS = 80 +                    ROWS = 5 +                    WRAP = "HARD" +                    <% $ro_comments %> +          ><% $cust_main->comments %></TEXTAREA> +        </TD> +      </TR> +    </TABLE> + +% } + +% unless ( $custnum ) { + +    <% include('cust_main/first_pkg.html', $cust_main, +                 'pkgpart_svcpart' => $pkgpart_svcpart, +                 #svc_acct +                 'username'        => $username, +                 'password'        => $password, +                 'popnum'          => $popnum, +                 'saved_domsvc'    => $saved_domsvc, +                 %svc_phone, +              ) +    %> + +% } + +<INPUT TYPE="hidden" NAME="otaker" VALUE="<% $cust_main->otaker %>"> + +%# cust_main/bottomfixup.js +% foreach my $hidden ( +%    'payauto', +%    'payinfo', 'payinfo1', 'payinfo2', 'paytype', +%    'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv', +%    'paystart_month', 'paystart_year', 'payissue', +%    'payip', +%    'paid', +% ) { +    <INPUT TYPE="hidden" NAME="<% $hidden %>" VALUE=""> +% }  + +<% include('cust_main/bottomfixup.html') %> + +<BR> +<INPUT TYPE    = "button" +       NAME    = "submitButton" +       ID      = "submitButton" +       VALUE   = "<% $custnum ?  "Apply Changes" : "Add Customer" %>" +       onClick = "this.disabled=true; bottomfixup(this.form);" +> +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +#probably redundant given the checks below... +die "access denied" +  unless $curuser->access_right('New customer') +     ||  $curuser->access_right('Edit customer'); + +my $conf = new FS::Conf; + +#get record + +my($custnum, $cust_main, $ss, $stateid, $payinfo, @invoicing_list); +my $same = ''; +my $pkgpart_svcpart = ''; #first_pkg +my($username, $password, $popnum, $saved_domsvc) = ( '', '', 0, 0 ); #svc_acct +my %svc_phone = (); + +if ( $cgi->param('error') ) { + +  $cust_main = new FS::cust_main ( { +    map { $_, scalar($cgi->param($_)) } fields('cust_main') +  } ); + +  $custnum = $cust_main->custnum; + +  die "access denied" +    unless $curuser->access_right($custnum ? 'Edit customer' : 'New customer'); + +  @invoicing_list = split( /\s*,\s*/, $cgi->param('invoicing_list') ); +  $same = $cgi->param('same'); +  $cust_main->setfield('paid' => $cgi->param('paid')) if $cgi->param('paid'); +  $ss = $cust_main->ss;           # don't mask an entered value on errors +  $stateid = $cust_main->stateid; # don't mask an entered value on errors +  $payinfo = $cust_main->payinfo; # don't mask an entered value on errors + +  $pkgpart_svcpart = $cgi->param('pkgpart_svcpart') || ''; + +  #svc_acct +  $username = $cgi->param('username'); +  $password = $cgi->param('_password'); +  $popnum = $cgi->param('popnum'); +  $saved_domsvc = $cgi->param('domsvc') || ''; +  if ( $saved_domsvc =~ /^(\d+)$/ ) { +    $saved_domsvc = $1; +  } else { +    $saved_domsvc = ''; +  } + +  #svc_phone +  $svc_phone{$_} = $cgi->param($_) +    foreach qw( countrycode phonenum sip_password pin phone_name ); + +} elsif ( $cgi->keywords ) { #editing + +  die "access denied" +    unless $curuser->access_right('Edit customer'); + +  my( $query ) = $cgi->keywords; +  $query =~ /^(\d+)$/; +  $custnum=$1; +  $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ); +  if ( $cust_main->dbdef_table->column('paycvv') +       && length($cust_main->paycvv)             ) { +    my $paycvv = $cust_main->paycvv; +    $paycvv =~ s/./*/g; +    $cust_main->paycvv($paycvv); +  } +  @invoicing_list = $cust_main->invoicing_list; +  $ss = $cust_main->masked('ss'); +  $stateid = $cust_main->masked('stateid'); +  $payinfo = $cust_main->paymask; + +} else { #new customer + +  die "access denied" +    unless $curuser->access_right('New customer'); + +  $custnum=''; +  $cust_main = new FS::cust_main ( {} ); +  $cust_main->otaker( &getotaker ); +  $cust_main->referral_custnum( $cgi->param('referral_custnum') ); +  @invoicing_list = (); +  push @invoicing_list, 'POST' +    unless $conf->exists('disablepostalinvoicedefault'); +  $ss = ''; +  $stateid = ''; +  $payinfo = ''; + +} + +my $error = $cgi->param('error'); +$cgi->delete_all(); +$cgi->param('error', $error); + +my $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..ad83778ca --- /dev/null +++ b/httemplate/edit/cust_main/billing.html @@ -0,0 +1,492 @@ +%if ( $payby_default eq 'HIDE' ) { +% +%  $cust_main->payby('BILL') unless $cust_main->payby; +%  my $payby = $cust_main->payby; + +  <INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>"> + +  <INPUT TYPE="hidden" NAME="<%$payby%>_payinfo" VALUE="<% $cust_main->paymask %>"> + +% foreach my $field (qw( payname paycvv paystart_month paystart_year payissue payip paytype paystate )) {  + +    <INPUT TYPE="hidden" NAME="<% $payby.'_'.$field %>" VALUE="<% $cust_main->get($field) %>"> + +% }  + +%  #false laziness w/elements/select-month_year.html & view/cust_main/billing.html +%  my( $mon, $year ); +%  my $date = $cust_main->paydate || '12-2037'; +%  if ( $date  =~ /^(\d{4})-(\d{1,2})-\d{1,2}$/ ) { #PostgreSQL date format +%    ( $mon, $year ) = ( $2, $1 ); +%  } elsif ( $date =~ /^(\d{1,2})-(\d{1,2}-)?(\d{4}$)/ ) { +%    ( $mon, $year ) = ( $1, $3 ); +%  } else { +%    die "unrecognized expiration date format: $date"; +%  } + +  <INPUT TYPE="hidden" NAME="<%$payby%>_exp_month" VALUE="<% $mon %>"> +  <INPUT TYPE="hidden" NAME="<%$payby%>_exp_year"  VALUE="<% $year %>"> + +  <INPUT TYPE="hidden" NAME="tax" VALUE="<% $cust_main->tax %>"> + +  <INPUT TYPE="hidden" NAME="invoicing_list" VALUE="<% join(', ', @invoicing_list) %>"> + +% } else { +% +%  my $r = qq!<font color="#ff0000">*</font> !; + +  <BR><FONT SIZE="+1"><B>Billing information</B></FONT> +  <% &ntable("#cccccc") %> + +    <TR> +      <TD ALIGN="right" WIDTH="200"><%$r%>Billing type</TD> + +  <SCRIPT> + +    var mywindow = -1; +    function myopen(filename,windowname,properties) { +      myclose(); +      mywindow = window.open(filename,windowname,properties); +    } +    function myclose() { +      if ( mywindow != -1 ) +        mywindow.close(); +      mywindow = -1; +    } + +    var achwindow = -1; +    function achopen(filename,windowname,properties) { +      achclose(); +      achwindow = window.open(filename,windowname,properties); +    } +    function achclose() { +      if ( achwindow != -1 ) +        achwindow.close(); +      achwindow = -1; +    } + +    function card_changed(what) { +      if ( +             what.form.payinfo.value.substring(0, 4) == '4093'  +          || what.form.payinfo.value.substring(0, 4) == '4911'  +          || what.form.payinfo.value.substring(0, 4) == '4936'  +          || what.form.payinfo.value.substring(0, 6) == '564132'  +          || what.form.payinfo.value.substring(0, 2) == '63'  +          || what.form.payinfo.value.substring(0, 2) == '67'  +         ) +      { +        what.form.paystart_month.disabled = false; +        what.form.paystart_year.disabled = false; +        what.form.payissue.disabled = false; +        what.form.paystart_month.style.backgroundColor = '#ffffff'; +        what.form.paystart_year.style.backgroundColor = '#ffffff'; +        what.form.payissue.style.backgroundColor = '#ffffff'; +        document.getElementById('paystart_label').style.color = '#000000'; +        document.getElementById('payissue_label').style.color = '#000000'; +      } else { +        what.form.paystart_month.disabled = true; +        what.form.paystart_year.disabled = true; +        what.form.payissue.disabled = true; +        what.form.paystart_month.style.backgroundColor = '#dddddd'; +        what.form.paystart_year.style.backgroundColor = '#dddddd'; +        what.form.payissue.style.backgroundColor = '#dddddd'; +        document.getElementById('paystart_label').style.color = '#999999'; +        document.getElementById('payissue_label').style.color = '#999999'; +      } +      return true; +    } + +  </SCRIPT> + +  <% include('/elements/init_overlib.html') %> + +%  my $payby = $cust_main->payby; +%  my $paytype = $cust_main->paytype; +%  my( $account, $aba ) = split('@', $payinfo); +% +%  my $disabled = 'DISABLED style="background-color: #dddddd"'; +%  my $text_disabled = 'style="color: #999999"'; +% +%  if ( $payby =~ /^(CARD|DCRD)$/ && cardtype($payinfo) =~ /^(Switch|Solo)$/ ) { +%    $disabled = 'style="background-color: #ffffff"'; +%    $text_disabled = 'style="color: #000000";' +%  } +% +%  my %payby = ( +% +%    'CARD' => +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Card number </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_payinfo" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $payinfo : '' ). qq!" MAXLENGTH=19 onChange="card_changed(this)" onKeyUp="card_changed(this)"></TD></TR>!. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Expiration </TD>!. +%          '<TD WIDTH="408">'. +% +%          include('/elements/select-month_year.html', +%                    'prefix' => 'CARD_exp', +%                    'selected_date' => +%                      ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->paydate : '' ), +%                 ). +% +%          '</TD></TR>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">CVV2 !. +% +%          qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/cvv2.html', 480, 352, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)!. +%          qq!</TD>!. +%          '<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_paycvv" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ && !$cust_main->is_encrypted($cust_main->paycvv) ? $cust_main->paycvv : '' ). '" SIZE=4 MAXLENGTH=4>'. +% +% +%        qq!<TR><TD ALIGN="right" WIDTH="200"><SPAN ID="paystart_label" $text_disabled>Start date </SPAN></TD>!. +%          '<TD WIDTH="408">'. +% +%          include('/elements/select-month_year.html', +%                    'prefix' => 'CARD_paystart', +%                    'disabled' => $disabled, +%                    'empty_option' => 1, +%                    'start_year' => 2000, +%                    'end_year'   => (localtime())[5] + 1900, +%                    'selected_date' => ( +%                      ( $payby =~ /^(CARD|DCRD)$/ +%                        && cardtype($payinfo) =~ /^(Switch|Solo)$/ ) +%                          ? $cust_main->paystart_month. '-'. +%                            $cust_main->paystart_year  +%                          : '' +%                    ) +%                 ). +% +%        qq!<SPAN ID="payissue_label" $text_disabled> or Issue number </SPAN>!. +%          '<INPUT TYPE="text" NAME="CARD_payissue" VALUE="'. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payissue : '' ). qq!" SIZE=3 MAXLENGTH=2 $disabled></TD></TR>!. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Exact name on card </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CARD_payname" VALUE="!. ( $payby =~ /^(CARD|DCRD)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!. +% +%        qq!<TR><TD COLSPAN=2 WIDTH="608"><INPUT TYPE="checkbox" NAME="CARD_payauto" !. ( $payby eq 'DCRD' ? '' : 'CHECKED' ). '> Charge future payments to this card automatically</TD></TR>'. +% +%      '</TABLE>', +% +%    'CHEK' =>  +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Account number </TD>!. +%          qq!<TD><INPUT TYPE="text" SIZE=12 NAME="CHEK_payinfo1" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $account : '' ). '"></TD>'. +%          qq!<TD ALIGN="right">Type</TD><TD><SELECT NAME="CHEK_paytype">!. +%            join('', map { qq!<OPTION VALUE="$_" !.($paytype eq $_ ? 'SELECTED' : '').">$_</OPTION>" } @FS::cust_main::paytypes). +%          qq!</SELECT></TD></TR>!. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}ABA/Routing number </TD>!. +%          qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" SIZE=10 MAXLENGTH=9 NAME="CHEK_payinfo2" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $aba : '' ). qq!" SIZE=10 MAXLENGTH=9> !. +%          qq!(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('../docs/ach.html', 380, 240, 'ach_popup' ), CAPTION, 'ACH Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;">help</A>)!. +%          qq!</TD></TR>!. +% +%        qq!<INPUT TYPE="hidden" NAME="CHEK_exp_month" VALUE="12">!. +%        qq!<INPUT TYPE="hidden" NAME="CHEK_exp_year" VALUE="2037">!. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Bank name </TD>!. +%          qq!<TD COLSPAN="3" WIDTH="408"><INPUT TYPE="text" NAME="CHEK_payname" VALUE="!. ( $payby =~ /^(CHEK|DCHK)$/ ? $cust_main->payname : '' ). qq!"></TD></TR>!. +%      ( $conf->exists('show_bankstate') ? +%          qq!<TR><TD ALIGN="right" WIDTH="200">$paystate_label</TD>!. +%          qq!<TD COLSPAN="3" WIDTH="408">!. +%          include('/elements/select-state.html', +%                    'empty'   => '(choose)', +%                    'state'   => $cust_main->paystate, +%                    'country' => $cust_main->country, +%                    'prefix'  => 'CHEK_pay', +%                 ). "</TD></TR>" +%         : '<INPUT TYPE="hidden" NAME="CHEK_paystate" VALUE="'. +%            $cust_main->paystate. '">' +%       ). +% +% +%        qq!<TR><TD COLSPAN=4 WIDTH="608"><INPUT TYPE="checkbox" NAME="CHEK_payauto" !. ( $payby eq 'DCHK' ? '' : 'CHECKED' ). '> Charge future payments to this electronic check automatically</TD></TR>'. +% +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +% +%      '</TABLE>', +% +%    'LECB' =>   +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Phone number </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="LECB_payinfo" VALUE="!. ( $payby eq 'LECB' ? $cust_main->payinfo : '' ). qq!" MAXLENGTH=15 SIZE=16></TD></TR>!. +% +%        qq!<INPUT TYPE="hidden" NAME="LECB_exp_month" VALUE="12">!. +%        qq!<INPUT TYPE="hidden" NAME="LECB_exp_year" VALUE="2037">!. +%        qq!<INPUT TYPE="hidden" NAME="LECB_payname" VALUE="">!. +% +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +% +%      '</TABLE>', +% +%    'BILL' =>   +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">P.O. </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payinfo" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payinfo : '' ). qq!"></TD></TR>!. +% +%        qq!<INPUT TYPE="hidden" NAME="BILL_exp_month" VALUE="12">!. +%        qq!<INPUT TYPE="hidden" NAME="BILL_exp_year" VALUE="2037">!. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">Attention </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payname" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payname : '' ). qq!"></TD></TR>!. +% +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +% +%      '</TABLE>', +% +%    'COMP' =>    +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Approved by </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="COMP_payinfo" VALUE=""></TD></TR>!. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Expiration </TD>!. +%          '<TD WIDTH="408">'. +% +%          include('/elements/select-month_year.html', +%                    'prefix' => 'COMP_exp', +%                    'selected_date' => +%                      ( $payby eq 'COMP' ? $cust_main->paydate : '' ), +%                 ). +% +%          '</TD></TR>'. +% +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +% +%      '</TABLE>', +% +%    'CASH' => +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="CASH_paid" VALUE="!. ( $payby eq 'CASH' ? $cust_main->paid : '' ). qq!"></TD></TR>!. +% +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +% +%      '</TABLE>', +% +%    'WEST' => +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="WEST_paid" VALUE="!. ( $payby eq 'WEST' ? $cust_main->paid : '' ). qq!"></TD></TR>!. +% +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +% +%      '</TABLE>', +% +%    'MCRD' => +% +%      '<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 HEIGHT=192>'. +% +%        qq!<TR><TD ALIGN="right" WIDTH="200">${r}Amount </TD>!. +%          qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="MCRD_paid" VALUE="!. ( $payby eq 'MCRD' ? $cust_main->paid : '' ). qq!"></TD></TR>!. +% +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +%        '<TR><TD> </TD></TR>'. +% +%      '</TABLE>', +% +%  ); +% +%  #this should use FS::payby +%  my @allopt = qw( CARD CHEK LECB BILL CASH WEST MCRD COMP ); +% +%  my %allopt = map { $_ => FS::payby->shortname($_) } @allopt; +% +%  if ( $cust_main->custnum ) { +%    #don't offer CASH/WEST/MCRD initial payment types when editing customer +%    delete $allopt{$_} for qw(CASH WEST MCRD); +%  } +%   +%  my @options = grep exists( $allopt{$_} ), @payby; +% +%  my %payby2option = ( +%    ( map { $_ => $_ } @options ), +%    'DCRD' => 'CARD', +%    'DCHK' => 'CHEK', +%  ); + +  <TD WIDTH="408"> +    <% include( '/elements/selectlayers.html', +                  'field'      => 'payby', +                  'curr_value' => $payby2option{$payby || $payby_default || $payby[0] }, +                  'options'    => \@options, +                  'labels'     => \%allopt, +                  'html_between' => '</TD></TR></TABLE>', +                  'layer_callback' => sub { my $layer = shift; $payby{$layer}; }, +              ) +    %> + +  <% &ntable("#cccccc") %> + +    <TR><TD> </TD></TR> + +%   my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); + +    <TR> +      <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="tax" VALUE="Y" <% $cust_main->tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt<% @exempt_groups ? ' (all taxes)' : '' %></TD> +    </TR> + +%   foreach my $exempt_group ( @exempt_groups ) { +%     #escape $exempt_group for NAME +      <TR> +        <TD WIDTH="608" COLSPAN="2">  <INPUT TYPE="checkbox" NAME="tax_<% $exempt_group %>" VALUE="Y" <% $cust_main->tax_exemption($exempt_group) ? 'CHECKED' : '' %>> Tax Exempt (<% $exempt_group %> taxes)<TD> +      </TR> +%   } + +% unless ( $conf->exists('emailinvoiceonly') ) { + +    <TR> +      <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_POST" VALUE="POST" <% + +        ( grep { $_ eq 'POST' } @invoicing_list ) + +          ? 'CHECKED' +          : '' + +        %>> Postal mail invoice + +      </TD> +    </TR> + +    <TR> +      <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoicing_list_FAX" VALUE="FAX" <% + +        ( grep { $_ eq 'FAX' } @invoicing_list ) +          ? 'CHECKED' +          : '' + +        %>> Fax invoice + +      </TD> +    </TR> + +% } + +    <TR> +      <TD ALIGN="right" WIDTH="200"> +        <% $conf->exists('cust_main-require_invoicing_list_email') ? $r : '' %>Email address(es) +      </TD> +      <TD WIDTH="408"><INPUT TYPE="text" NAME="invoicing_list" VALUE="<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) %>"></TD> +    </TR> + +    <TR> +      <TD ALIGN="right" WIDTH="200">Invoice terms </TD> +      <TD WIDTH="408"> +        <% include('/elements/select-terms.html', +                     'curr_value' => $cust_main->invoice_terms, +                  ) +        %> +      </TD> +    </TR> + +% if ( $conf->exists('voip-cust_cdr_spools') ) {  +      <TR> +       <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="spool_cdr" VALUE="Y" <% $cust_main->spool_cdr eq "Y" ? 'CHECKED' : '' %>> Spool CDRs</TD> +      </TR> +% } else {  +      <INPUT TYPE="hidden" NAME="spool_cdr" VALUE="<% $cust_main->spool_cdr %>"> +% } + +% if ( $conf->exists('voip-cust_cdr_squelch') ) {  +      <TR> +       <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="squelch_cdr" VALUE="Y" <% $cust_main->squelch_cdr eq "Y" ? 'CHECKED' : '' %>> Omit CDRs from invoices</TD> +      </TR> +% } else {  +      <INPUT TYPE="hidden" NAME="squelch_cdr" VALUE="<% $cust_main->squelch_cdr %>"> +% } + +% if ( $conf->exists('voip-cust_email_csv_cdr') ) {  +      <TR> +       <TD COLSPAN="2"><INPUT TYPE="checkbox" NAME="email_csv_cdr" VALUE="Y" <% $cust_main->email_csv_cdr eq "Y" ? 'CHECKED' : '' %>> Attach CDRs as CSV to emailed invoices</TD> +      </TR> +% } else {  +      <INPUT TYPE="hidden" NAME="email_csv_cdr" VALUE="<% $cust_main->email_csv_cdr %>"> +% } + +% if ( $show_term || $cust_main->cdr_termination_percentage ) { +      <TR> +       <TD ALIGN="right">CDR termination settlement</TD> +       <TD><INPUT TYPE  = "text" +                  NAME  = "cdr_termination_percentage" +                  SIZE  = 6 +                  VALUE = "<% $cust_main->cdr_termination_percentage %>" +                  STYLE = "text-align:right;" +        ><B>%</B></TD> +      </TR> +% } else { +      <INPUT TYPE="hidden" NAME="cdr_termination_percentage" VALUE="<% $cust_main->cdr_termination_percentage %>"> +% } + +  </TABLE> + +  <% $r %> required fields +% }  + +<%once> + +my $paystate_label = FS::Msgcat::_gettext('paystate'); +$paystate_label = 'Bank state' if $paystate_label =~/^paystate$/; + +</%once> +<%init> + +my( $cust_main, %options ) = @_; +my @invoicing_list = @{ $options{'invoicing_list'} }; +my $payinfo = $options{'payinfo'}; +my $conf = new FS::Conf; +my $payby_default = $conf->config('payby-default'); + +my @payby = grep /\w/, $conf->config('payby'); +#@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP )) +@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP )) +  unless @payby; + +my $show_term = ''; +if ( $cust_main->custnum ) { +  #false laziness w/view/cust_main/billing.html +  my $term_sql = "SELECT COUNT(*) FROM cust_pkg LEFT JOIN part_pkg USING ( pkgpart ) WHERE custnum = ? AND plan = 'cdr_termination' LIMIT 1"; +  my $term_sth = dbh->prepare($term_sql)  or die dbh->errstr; +  $term_sth->execute($cust_main->custnum) or die $term_sth->errstr; +  $show_term = $term_sth->fetchrow_arrayref->[0]; +} + +</%init> diff --git a/httemplate/edit/cust_main/birthdate.html b/httemplate/edit/cust_main/birthdate.html new file mode 100644 index 000000000..415aba3c4 --- /dev/null +++ b/httemplate/edit/cust_main/birthdate.html @@ -0,0 +1,15 @@ +<% ntable("#cccccc", 2) %> +  <% include ('/elements/tr-input-date-field.html', +              'birthdate', +              $cust_main->birthdate, +              'Date of Birth', +              $conf->config('date_format') || "%m/%d/%Y", +              1) +  %> +</TABLE> +<%init> + +my( $cust_main, %opt ) = @_; +my $conf = new FS::Conf; + +</%init> diff --git a/httemplate/edit/cust_main/bottomfixup.html b/httemplate/edit/cust_main/bottomfixup.html new file mode 100644 index 000000000..1b29c671a --- /dev/null +++ b/httemplate/edit/cust_main/bottomfixup.html @@ -0,0 +1,19 @@ +<% include('/elements/init_overlib.html') %> + +<% include( '/elements/xmlhttp.html', +              'url'  => $p.'misc/xmlhttp-cust_main-address_standardize.html', +              'subs' => [ 'address_standardize' ], +              #'method' => 'POST', #could get too long? +          ) +%> + +<% include( '/elements/xmlhttp.html', +              'url'  => $p.'misc/xmlhttp-cust_main-censustract.html', +              'subs' => [ 'censustract' ], +              #'method' => 'POST', #could get too long? +          ) +%> + +<SCRIPT TYPE="text/javascript"> +  <% include('bottomfixup.js') %> +</SCRIPT> diff --git a/httemplate/edit/cust_main/bottomfixup.js b/httemplate/edit/cust_main/bottomfixup.js new file mode 100644 index 000000000..1a06d9497 --- /dev/null +++ b/httemplate/edit/cust_main/bottomfixup.js @@ -0,0 +1,398 @@ +function bottomfixup(what) { + +%# ../cust_main.cgi +  var layervars = new Array( +    'payauto', +    'payinfo', 'payinfo1', 'payinfo2', 'paytype', +    'payname', 'paystate', 'exp_month', 'exp_year', 'paycvv', +    'paystart_month', 'paystart_year', 'payissue', +    'payip', +    'paid' +  ); + +  var cf = document.CustomerForm; +  var payby = cf.payby.options[cf.payby.selectedIndex].value; +  for ( f=0; f < layervars.length; f++ ) { +    var field = layervars[f]; +    copyelement( cf.elements[payby + '_' + field], +                 cf.elements[field] +               ); +  } + +  //this part does USPS address correction + +  // XXX should this be first and should we update the form fields that are +  // displayed??? + +  var cf = document.CustomerForm; + +  var state_el      = cf.elements['state']; +  var ship_state_el = cf.elements['ship_state']; + +  //address_standardize( +  var cust_main = new Array( +    'company',  cf.elements['company'].value, +    'address1', cf.elements['address1'].value, +    'address2', cf.elements['address2'].value, +    'city',     cf.elements['city'].value, +    'state',    state_el.options[ state_el.selectedIndex ].value, +    'zip',      cf.elements['zip'].value, + +    'ship_company',  cf.elements['ship_company'].value, +    'ship_address1', cf.elements['ship_address1'].value, +    'ship_address2', cf.elements['ship_address2'].value, +    'ship_city',     cf.elements['ship_city'].value, +    'ship_state',    ship_state_el.options[ ship_state_el.selectedIndex ].value, +    'ship_zip',      cf.elements['ship_zip'].value +  ); + +  address_standardize( cust_main, update_address ); + +} + +var standardize_address; + +function update_address(arg) { + +  var argsHash = eval('(' + arg + ')'); + +  var changed  = argsHash['address_standardized']; +  var ship_changed = argsHash['ship_address_standardized']; +  var error = argsHash['error']; +  var ship_error = argsHash['ship_error']; +   + +  //yay closures +  standardize_address = function () { + +    var cf = document.CustomerForm; +    var state_el      = cf.elements['state']; +    var ship_state_el = cf.elements['ship_state']; + +    if ( changed ) { +      cf.elements['company'].value  = argsHash['new_company']; +      cf.elements['address1'].value = argsHash['new_address1']; +      cf.elements['address2'].value = argsHash['new_address2']; +      cf.elements['city'].value     = argsHash['new_city']; +      setselect(cf.elements['state'], argsHash['new_state']); +      cf.elements['zip'].value      = argsHash['new_zip']; +    } + +    if ( ship_changed ) { +      cf.elements['ship_company'].value  = argsHash['new_ship_company']; +      cf.elements['ship_address1'].value = argsHash['new_ship_address1']; +      cf.elements['ship_address2'].value = argsHash['new_ship_address2']; +      cf.elements['ship_city'].value     = argsHash['new_ship_city']; +      setselect(cf.elements['ship_state'], argsHash['new_ship_state']); +      cf.elements['ship_zip'].value      = argsHash['new_ship_zip']; +    } + +    post_standardization(); + +  } + + + +  if ( changed || ship_changed ) { + +%   if ( $conf->exists('cust_main-auto_standardize_address') ) { + +    standardize_address(); + +%   } else { + +    // popup a confirmation popup + +    var confirm_change = +      '<CENTER><BR><B>Confirm address standardization</B><BR><BR>' + +      '<TABLE>'; +     +    if ( changed ) { + +      confirm_change = confirm_change +  +        '<TR><TH>Entered billing address</TH>' + +          '<TH>Standardized billing address</TH></TR>'; +        // + '<TR><TD> </TD><TD> </TD></TR>'; +       +      if ( argsHash['company'] || argsHash['new_company'] ) { +        confirm_change = confirm_change + +        '<TR><TD>' + argsHash['company'] + +          '</TD><TD>' + argsHash['new_company'] + '</TD></TR>'; +      } +       +      confirm_change = confirm_change + +        '<TR><TD>' + argsHash['address1'] + +          '</TD><TD>' + argsHash['new_address1'] + '</TD></TR>' + +        '<TR><TD>' + argsHash['address2'] + +          '</TD><TD>' + argsHash['new_address2'] + '</TD></TR>' + +        '<TR><TD>' + argsHash['city'] + ', ' + argsHash['state'] + '  ' + argsHash['zip'] + +          '</TD><TD>' + argsHash['new_city'] + ', ' + argsHash['new_state'] + '  ' + argsHash['new_zip'] + '</TD></TR>' + +          '<TR><TD> </TD><TD> </TD></TR>'; + +    } + +    if ( ship_changed ) { + +      confirm_change = confirm_change +  +        '<TR><TH>Entered service address</TH>' + +          '<TH>Standardized service address</TH></TR>'; +        // + '<TR><TD> </TD><TD> </TD></TR>'; +       +      if ( argsHash['ship_company'] || argsHash['new_ship_company'] ) { +        confirm_change = confirm_change + +        '<TR><TD>' + argsHash['ship_company'] + +          '</TD><TD>' + argsHash['new_ship_company'] + '</TD></TR>'; +      } +       +      confirm_change = confirm_change + +        '<TR><TD>' + argsHash['ship_address1'] + +          '</TD><TD>' + argsHash['new_ship_address1'] + '</TD></TR>' + +        '<TR><TD>' + argsHash['ship_address2'] + +          '</TD><TD>' + argsHash['new_ship_address2'] + '</TD></TR>' + +        '<TR><TD>' + argsHash['ship_city'] + ', ' + argsHash['ship_state'] + '  ' + argsHash['ship_zip'] + +          '</TD><TD>' + argsHash['new_ship_city'] + ', ' + argsHash['new_ship_state'] + '  ' + argsHash['new_ship_zip'] + '</TD></TR>' + +        '<TR><TD> </TD><TD> </TD></TR>'; + +    } + +    var addresses = 'address'; +    var height = 268; +    if ( changed && ship_changed ) { +      addresses = 'addresses'; +      height = 396; // #what +    } + +    confirm_change = confirm_change + +      '<TR><TD>' + +        '<BUTTON TYPE="button" onClick="post_standardization();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' +  +      '</TD><TD>' + +        '<BUTTON TYPE="button" onClick="standardize_address();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' +  +      '</TD></TR>' + +      '<TR><TD COLSPAN=2 ALIGN="center">' + +        '<BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' + +         +      '</TABLE></CENTER>'; + +    overlib( confirm_change, CAPTION, 'Confirm address standardization', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, height, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 ); + +%   } + +  } else { + +    post_standardization(); + +  } + + +} + +function post_standardization() { + +  var cf = document.CustomerForm; + +% if ( $conf->exists('enable_taxproducts') ) { + +  if ( new String(cf.elements['<% $taxpre %>zip'].value).length < 10 ) +  { + +    var country_el = cf.elements['<% $taxpre %>country']; +    var country = country_el.options[ country_el.selectedIndex ].value; +    var geocode = cf.elements['geocode'].value; + +    if ( country == 'CA' || country == 'US' ) { + +      var state_el = cf.elements['<% $taxpre %>state']; +      var state = state_el.options[ state_el.selectedIndex ].value; + +      var url = "cust_main/choose_tax_location.html" + +                  "?data_vendor=cch-zip" +  +                  ";city="    + cf.elements['<% $taxpre %>city'].value + +                  ";state="   + state +  +                  ";zip="     + cf.elements['<% $taxpre %>zip'].value + +                  ";country=" + country + +                  ";geocode=" + geocode + +                  ";"; + +      // popup a chooser +      OLgetAJAX( url, update_geocode, 300 ); + +    } else { + +      cf.elements['geocode'].value = 'DEFAULT'; +      post_geocode(); + +    } + +  } else { + +    post_geocode(); + +  } + +% } else { + +  post_geocode(); + +% } + +} + +function post_geocode() { + +% if ( $conf->exists('cust_main-require_censustract') ) { + +  //alert('fetch census tract data'); +  var cf = document.CustomerForm; +  var state_el = cf.elements['ship_state']; +  var census_data = new Array( +    'year',   <% $conf->config('census_year') || '2009' %>, +    'address', cf.elements['ship_address1'].value, +    'city',    cf.elements['ship_city'].value, +    'state',   state_el.options[ state_el.selectedIndex ].value, +    'zip',     cf.elements['ship_zip'].value +  ); + +  censustract( census_data, update_censustract ); + +% }else{ + +  document.CustomerForm.submit(); + +% } + +} + +function update_geocode() { + +  //yay closures +  set_geocode = function (what) { + +    var cf = document.CustomerForm; + +    //alert(what.options[what.selectedIndex].value); +    var argsHash = eval('(' + what.options[what.selectedIndex].value + ')'); +    cf.elements['<% $taxpre %>city'].value     = argsHash['city']; +    setselect(cf.elements['<% $taxpre %>state'], argsHash['state']); +    cf.elements['<% $taxpre %>zip'].value      = argsHash['zip']; +    cf.elements['geocode'].value  = argsHash['geocode']; +    post_geocode(); + +  } + +  // popup a chooser + +  overlib( OLresponseAJAX, CAPTION, 'Select tax location', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 ); + +} + +var set_censustract; + +function update_censustract(arg) { + +  var argsHash = eval('(' + arg + ')'); + +  var cf = document.CustomerForm; + +  var msacode    = argsHash['msacode']; +  var statecode  = argsHash['statecode']; +  var countycode = argsHash['countycode']; +  var tractcode  = argsHash['tractcode']; +  var error      = argsHash['error']; +   +  var newcensus =  +    new String(statecode)  + +    new String(countycode) + +    new String(tractcode).replace(/\s$/, '');  // JSON 1 workaround + +  set_censustract = function () { + +    cf.elements['censustract'].value = newcensus +    cf.submit(); + +  } + +  if (error || cf.elements['censustract'].value != newcensus) { +    // popup an entry dialog + +    if (error) { newcensus = error; } +    newcensus.replace(/.*ndefined.*/, 'Not found'); + +    var choose_censustract = +      '<CENTER><BR><B>Confirm censustract</B><BR>' + +      '<A href="http://maps.ffiec.gov/FFIECMapper/TGMapSrv.aspx?' + +      'census_year=<% $conf->config('census_year') || '2008' %>' + +      '&latitude=' + cf.elements['latitude'].value + +      '&longitude=' + cf.elements['longitude'].value + +      '" target="_blank">Map service module location</A><BR>' + +      '<A href="http://maps.ffiec.gov/FFIECMapper/TGMapSrv.aspx?' + +      'census_year=<% $conf->config('census_year') || '2008' %>' + +      '&zip_code=' + cf.elements['ship_zip'].value + +      '" target="_blank">Map zip code center</A><BR><BR>' + +      '<TABLE>'; +     +    choose_censustract = choose_censustract +  +      '<TR><TH style="width:50%">Entered census tract</TH>' + +        '<TH style="width:50%">Calculated census tract</TH></TR>' + +      '<TR><TD>' + cf.elements['censustract'].value + +        '</TD><TD>' + newcensus + '</TD></TR>' + +        '<TR><TD> </TD><TD> </TD></TR>'; + +    choose_censustract = choose_censustract + +      '<TR><TD ALIGN="center">' + +        '<BUTTON TYPE="button" onClick="document.CustomerForm.submit();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered census tract </BUTTON>' +  +      '</TD><TD ALIGN="center">' + +        '<BUTTON TYPE="button" onClick="set_censustract();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use calculated census tract </BUTTON>' +  +      '</TD></TR>' + +      '<TR><TD COLSPAN=2 ALIGN="center">' + +        '<BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' + +         +      '</TABLE></CENTER>'; + +    overlib( choose_censustract, CAPTION, 'Confirm censustract', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 ); + +  } else { + +    cf.submit(); + +  } + +} + +function copyelement(from, to) { +  if ( from == undefined ) { +    to.value = ''; +  } else if ( from.type == 'select-one' ) { +    to.value = from.options[from.selectedIndex].value; +    //alert(from + " (" + from.type + "): " + to.name + " => (" + from.selectedIndex + ") " + to.value); +  } else if ( from.type == 'checkbox' ) { +    if ( from.checked ) { +      to.value = from.value; +    } else { +      to.value = ''; +    } +  } else { +    if ( from.value == undefined ) { +      to.value = ''; +    } else { +      to.value = from.value; +    } +  } +  //alert(from + " (" + from.type + "): " + to.name + " => " + to.value); +} + +function setselect(el, value) { + +  for ( var s = 0; s < el.options.length; s++ ) { +     if ( el.options[s].value == value ) { +       el.selectedIndex = s; +     } +  } + +} +<%init> + +my $conf = new FS::Conf; + +my $taxpre = $conf->exists('tax-ship_address') ? 'ship_' : ''; + +</%init> diff --git a/httemplate/edit/cust_main/choose_tax_location.html b/httemplate/edit/cust_main/choose_tax_location.html new file mode 100644 index 000000000..ac475c54b --- /dev/null +++ b/httemplate/edit/cust_main/choose_tax_location.html @@ -0,0 +1,87 @@ +<FORM NAME="choosegeocodeform"> +<CENTER><BR><B>Choose tax location</B><BR><BR> +<P>the geocode is:<% $header %></P> +<P STYLE="<% $style %>"><% $header %></P> + +<SELECT NAME='geocodes' ID='geocodes' STYLE="<% $style %>"> +% foreach my $location (@cust_tax_location) { +%   my %value = ( zip => $zip5, +%                 map { $_ => $location->$_ } +%                   qw ( city state geocode ) +%               ); +%   map { $value{$_} = $location{$_} } qw ( city state ) +%     if $location{country} eq 'CA'; +% +%   my $value = encode_entities(objToJson({ %value }) +%                              ); +%   my $content = ''; +%   $content .= $location->$_. ' ' x ( $max{$_} - length($location->$_) ) +%     foreach qw( city county state ); +%   $content .=   $location->cityflag eq 'I' ? 'Y' : 'N' ; +%   my $selected = '' ; +%   if ($geocode && $location->geocode eq $geocode) { +%     $selected = 'SELECTED'; +%   } +  <OPTION VALUE="<% $value %>" STYLE="<% $style %>" <% $selected %>><% $content %> +% } +</SELECT><BR><BR> + +<TABLE><TR> +  <TD> <BUTTON TYPE="button" onClick="set_geocode(document.getElementById('geocodes'));"><IMG SRC="<%$p%>images/tick.png" ALT=""> Set location </BUTTON></TD> +  <TD><BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission </BUTTON></TD> +</TR> +</TABLE> + +</CENTER> +</FORM> +<%init> + +my $conf = new FS::Conf; + +my %location = (); + +($location{data_vendor}) = $cgi->param('data_vendor') =~ /^([-\w]+)$/; +($location{city})        = $cgi->param('city')        =~ /^([\w ]+)$/; +($location{state})       = $cgi->param('state')       =~ /^(\w+)$/; +($location{zip})         = $cgi->param('zip')         =~ /^([-\w ]+)$/; +($location{country})     = $cgi->param('country')     =~ /^([\w ]+)$/; + +my($geocode)             = $cgi->param('geocode')     =~ /^([\w]+)$/; + +my($zip5, $zip4) = split('-', $location{zip}); + +#only support US & CA +my $hashref = { 'data_vendor' => $location{data_vendor} }; +$hashref->{zip} = $location{country} eq 'CA' ? substr($zip5,0,1) : $zip5, + +my @keys = keys(%$hashref); +my @cust_tax_location = (); +until ( @cust_tax_location ) { +  @cust_tax_location = qsearch({ table    => 'cust_tax_location', +                                 hashref  =>  $hashref, +                                 order_by =>  'LIMIT 50', +                              }); +  last unless scalar(@keys); +  delete $hashref->{ shift @keys }; +}  + +my %max = ( city => 4, county => 6, state => 5); +foreach my $location (@cust_tax_location) { +  foreach ( qw( city county state ) ) { +    my $length = length($location->$_); +    $max{$_} = ($length > $max{$_}) ? $length : $max{$_}; +  } +} +foreach ( qw( city county state ) ) { +  $max{$_} = $location{$_} if $location{$_} > $max{$_}; +  $max{$_}++; +} + +my $header = '  '; +$header .= $_. ' ' x ( $max{lc($_)} - length($_) ) +  foreach qw( City County State ); +$header .=   "In city?"; + +my $style = "font-family:monospace;"; + +</%init> diff --git a/httemplate/edit/cust_main/contact.html b/httemplate/edit/cust_main/contact.html new file mode 100644 index 000000000..feb61db8d --- /dev/null +++ b/httemplate/edit/cust_main/contact.html @@ -0,0 +1,150 @@ +<% &ntable("#cccccc") %> + +<TR> +  <TH ALIGN="right"><%$r%>Contact name<BR>(last, first)</TH> +  <TD COLSPAN=5> +    <INPUT TYPE="text" NAME="<%$pre%>last" VALUE="<% $cust_main->get($pre.'last') %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>> ,  +    <INPUT TYPE="text" NAME="<%$pre%>first" VALUE="<% $cust_main->get($pre.'first') %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>> +  </TD> +% if ( $conf->exists('show_ss') && !$pre ) {  + +  <TD ALIGN="right">SS#</TD> +  <TD><INPUT TYPE="text" NAME="ss" VALUE="<% $opt{ss} %>" SIZE=11></TD> +% } elsif ( !$pre ) {  + +  <TD><INPUT TYPE="hidden" NAME="ss" VALUE="<% $opt{ss} %>"></TD> +% }  + + +</TR> + +<TR> +  <TD ALIGN="right">Company</TD> +  <TD COLSPAN=7> +    <INPUT TYPE="text" NAME="<%$pre%>company" VALUE="<% $cust_main->get($pre.'company') %>" SIZE=70 onChange="<% $onchange %>" <%$disabled%> <%$style%>> +  </TD> +</TR> + +<% include('/elements/location.html', +             'prefix'       => $pre, +             'object'       => $cust_main, +             'onchange'     => $onchange, +             'disabled'     => $disabled, +             'style'        => \@style, +             'same_checked' => $opt{'same_checked'}, +             'geocode'      => $opt{'geocode'}, +             'censustract'  => $opt{'censustract'}, +          ) +%> + +<TR> +  <TD ALIGN="right"><% $daytime_label %></TD> +  <TD COLSPAN=5> +    <INPUT TYPE="text" NAME="<%$pre%>daytime" VALUE="<% $cust_main->get($pre.'daytime') %>" SIZE=18 onChange="<% $onchange %>" <%$disabled%> <%$style%>> +  </TD> +</TR> + +<TR> +  <TD ALIGN="right"><% $night_label %></TD> +  <TD COLSPAN=5> +    <INPUT TYPE="text" NAME="<%$pre%>night" VALUE="<% $cust_main->get($pre.'night') %>" SIZE=18 onChange="<% $onchange %>" <%$disabled%> <%$style%>> +  </TD> +</TR> + +<TR> +  <TD ALIGN="right">Fax</TD> +  <TD COLSPAN=5> +    <INPUT TYPE="text" NAME="<%$pre%>fax" VALUE="<% $cust_main->get($pre.'fax') %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%> <%$style%>> +  </TD> +</TR> + +% if ( $conf->exists('show_stateid') && !$pre ) {  + +<TR> +  <TD ALIGN="right"><% $stateid_label %></TD> +  <TD><INPUT TYPE="text" NAME="stateid" VALUE="<% $opt{stateid} %>" SIZE=12 onChange="<% $onchange %>" <%$disabled%> <%$style%>></TD> +  <TD ALIGN="right"><% $stateid_state_label %></TD> +  <TD><% include('/elements/select-state.html', +                   'state'    => $cust_main->stateid_state, +                   'country'  => $cust_main->country, +                   'prefix'   => 'stateid_', +                   'onchange' => $onchange, +                   'disabled' => $disabled, +                   'style'    => \@style, +                ) +      %> +  </TD> +</TR> +% } elsif ( !$pre ) {  + +  <TD><INPUT TYPE="hidden" NAME="stateid" VALUE="<% $opt{stateid} %>"></TD> +  <TD><INPUT TYPE="hidden" NAME="stateid_state" VALUE="<% $cust_main->stateid_state %>"></TD> +% }  + +</TABLE> +<%$r%>required fields<BR> + +<%init> + +#my( $cust_main, $pre, $onchange, $disabled, %opt ) = @_; +my %opt = @_; +my $cust_main = $opt{'cust_main'}; +my $pre       = $opt{'pre'}; +my $onchange  = $opt{'onchange'}; +my $disabled  = $opt{'disabled'}; +my @style     = ( $opt{'style'} ? @{ $opt{'style'} } : () ); + +#push @style, 'background-color: #dddddd' if $disabled; +my $style = scalar(@style) ? 'STYLE="'. join(';', @style). '"' : ''; + +my $conf = new FS::Conf; + +foreach (qw(ss stateid)) { +  $opt{$_} = $cust_main->masked($_) unless exists $opt{$_}; +} + +#false laziness with ship state +my $countrydefault = $conf->config('countrydefault') || 'US'; +$cust_main->set($pre.'country', $countrydefault ) +  unless $cust_main->get($pre.'country'); + +my $statedefault = $conf->config('statedefault') +                   || ($countrydefault eq 'US' ? 'CA' : ''); +$cust_main->set($pre.'state', $statedefault ) +  unless $cust_main->get($pre.'state') +         || $cust_main->get($pre.'country') ne $countrydefault; + +$cust_main->set('stateid_state', $cust_main->state ) +  unless $pre || $cust_main->get('stateid_state'); + +$opt{geocode} ||= $cust_main->get('geocode'); + +if ( $conf->exists('cust_main-require_censustract') ) { +  $opt{censustract} ||= $cust_main->censustract; +} + +#my($county_html, $state_html, $country_html) = +#  FS::cust_main_county::regionselector( $cust_main->get($pre.'county'), +#                                        $cust_main->get($pre.'state'), +#                                        $cust_main->get($pre.'country'), +#                                        $pre, +#                                        $onchange, +#                                        $disabled, +#                                      ); + +my $daytime_label = FS::Msgcat::_gettext('daytime') =~ /^(daytime)?$/ +                  ? 'Day Phone' +                  : FS::Msgcat::_gettext('daytime'); +my $night_label = FS::Msgcat::_gettext('night') =~/^(night)?$/ +                ? 'Night Phone' +                : FS::Msgcat::_gettext('night') || 'Night Phone'; +my $stateid_label = FS::Msgcat::_gettext('stateid') =~ /^(stateid)?$/ +                  ? 'Driver’s License' +                  : FS::Msgcat::_gettext('stateid') || 'Driver’s License'; +my $stateid_state_label = FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_state)?$/ +                        ? 'Driver’s License State' +                        : FS::Msgcat::_gettext('stateid_state') || 'Driver’s License State'; + +my $r = qq!<font color="#ff0000">*</font> !; + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg.html b/httemplate/edit/cust_main/first_pkg.html new file mode 100644 index 000000000..0de33c025 --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg.html @@ -0,0 +1,55 @@ +% if ( @part_pkg ) { + +    <BR><BR> +    <FONT SIZE="+1"><B>First package</B></FONT> +    <% ntable("#cccccc") %> + +      <TR> +        <TD COLSPAN=2> +          <% include('first_pkg/select-part_pkg.html', +                       'part_pkg' => \@part_pkg, +                       %opt, +                       # map { $_ => $opt{$_} } qw( pkgpart_svcpart saved_domsvc ) +                    ) +          %> + +% }  +<%init> + +my( $cust_main, %opt ) = @_; + +# pry the wrong place for this logic.  also pretty expensive + +#false laziness, copied from FS::cust_pkg::order +my $pkgpart; +my $agentnum = ''; +my @agents = $FS::CurrentUser::CurrentUser->agents; +if ( scalar(@agents) == 1 ) { +  # $pkgpart->{PKGPART} is true iff $custnum may purchase PKGPART +  $pkgpart = $agents[0]->pkgpart_hashref; +  $agentnum = $agents[0]->agentnum; +} else { +  #can't know (agent not chosen), so, allow all +  $agentnum = 'all'; +  my %typenum; +  foreach my $agent ( @agents ) { +    next if $typenum{$agent->typenum}++; +    $pkgpart->{$_}++ foreach keys %{ $agent->pkgpart_hashref } +  } +} +#eslaf + +my @first_svc = ( 'svc_acct', 'svc_phone' ); + +my @part_pkg = +  grep { $_->svcpart(\@first_svc) +         && ( $pkgpart->{ $_->pkgpart }  +              || $agentnum eq 'all' +              || ( $agentnum ne 'all' && $agentnum && $_->agentnum +                   && $_->agentnum == $agentnum +                 ) +            ) +       } +  qsearch( 'part_pkg', { 'disabled' => '' }, '', 'ORDER BY pkg' ); # case? + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg/select-part_pkg.html b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html new file mode 100644 index 000000000..871e1cdee --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html @@ -0,0 +1,170 @@ +<% include('/elements/xmlhttp.html', +              'url'  => $url_prefix.'misc/svc_acct-domains.cgi', +              'subs' => [ $opt{'prefix'}. 'get_domains' ], +           ) +%> + +<% include('/elements/xmlhttp.html', +              'url'  => $url_prefix.'misc/part_svc-columns.cgi', +              'subs' => [ $opt{'prefix'}. 'get_part_svc' ], +           ) +%> + +<INPUT TYPE="hidden" NAME="svcdb" VALUE=""> + +<SCRIPT TYPE="text/javascript"> + +  function selopt(what,value,text,selected) { +    var optionName = new Option(text, value, false, selected); +    var length = what.length; +    what.options[length] = optionName; +  } + +  var pkgpart_svcpart2svcdb = { +%   foreach my $pkgpart ( map $_->pkgpart, @part_pkg ) { +      "<% $pkgpart_svcpart{$pkgpart} %>":"<% $svcdb{$pkgpart} %>", +%   } +    '':'' +  }; + +  function <% $opt{'prefix'} %>pkgpart_svcpart_changed_too(what,selected) { + +    <% $opt{'onchange'} %>; + +    pkgpart_svcpart = what.options[what.selectedIndex].value; + +    var svcdb = pkgpart_svcpart2svcdb[pkgpart_svcpart]; + +    what.form.svcdb.value = svcdb; + +    if ( svcdb == 'svc_acct' ) { + +      // go get the new domains +      function <% $opt{'prefix'} %>update_domains(domains) { + +        // blank the current domain list +        for ( var i = what.form.<% $opt{'prefix'} %>domsvc.length; i >= 0; i-- ) +            what.form.<% $opt{'prefix'} %>domsvc.options[i] = null; + +        // add the new domains +        var domainArray = eval('(' + domains + ')' ); +        for ( var s = 0; s < domainArray.length; s=s+2 ) { +            var domainLabel = domainArray[s+1]; +            if ( domainLabel == "" ) +                domainLabel = '(n/a)'; +            selopt( what.form.<% $opt{'prefix'} %>domsvc, +                    domainArray[s], +                    domainLabel, +                    (domainArray[s] == selected) ? true : false +                  ); +        } + +      } + +      <% $opt{'prefix'} %>get_domains( pkgpart_svcpart, +                                       <% $opt{'prefix'} %>update_domains +                                     ); + +    } else if ( svcdb == 'svc_phone' ) { + +      function <% $opt{'prefix'} %>update_svc_phone(part_svc_column) { +        var colArray = eval('(' + part_svc_column + ')' ); +        for ( var s = 0; s < colArray.length; s=s+3 ) { +          var name  = colArray[s]; +          var flag  = colArray[s+1]; +          var value = colArray[s+2]; +          var td_label = document.getElementById(name+'_label_td'); +          var td       = document.getElementById(name+'_td'); +          var input    = document.getElementById(name); +          if ( flag == 'D' ) { +            if ( ! input.value ) { input.value = value; } +            td_label.style.display = '' +            td.style.display = '' +          } else if ( flag == 'F' ) { +            input.value = value; +            td_label.style.display = 'none' +            td.style.display = 'none' +          } else { +            td_label.style.display = '' +            td.style.display = '' +          } +        } +      } + +      <% $opt{'prefix'} %>get_part_svc( pkgpart_svcpart, +                                        <% $opt{'prefix'} %>update_svc_phone +                                      ); + +    } + +  } + +</SCRIPT> + +<% include( '/elements/selectlayers.html', +              'field'        => $opt{'prefix'}. 'pkgpart_svcpart', +              'curr_value'   => $opt{pkgpart_svcpart}, +              'options'      => \@options, +              'labels'       => \%labels, +              'html_between' => '</TD></TR></TABLE>', +              #'onchange'     => $opt{'prefix'}. 'pkgpart_svcpart_changed(this,0);', +              'onchange'     => $opt{'prefix'}. 'pkgpart_svcpart_changed_too(what,0)', + +              'layer_callback' => $layer_callback, +              'layermap'       => \%layermap, +          ) +%> + +<SCRIPT TYPE="text/javascript"> +  pkgpart_svcpart_changed_too( document.CustomerForm.pkgpart_svcpart, +                               <% $opt{saved_domsvc} %> +                             ); +</SCRIPT> + +<%init> + +my %opt = @_; + +foreach my $opt (qw( svc_part pkgparts saved_pkgpart saved_domsvc prefix)) { +  $opt{$_} = '' unless exists($opt{$_}) && defined($opt{$_}); +} +$opt{saved_domsvc} = 0 unless $opt{saved_domsvc}; + +my $url_prefix = $opt{'relurls'} ? '' : $p; + +my @part_pkg = @{$opt{'part_pkg'}}; + +my @first_svc = ( 'svc_acct', 'svc_phone' ); + +my %pkgpart_svcpart = (); +my %svcdb = (); +my %layermap = (); +foreach my $part_pkg ( @part_pkg ) { +  my $pkgpart = $part_pkg->pkgpart; +  my $pkgpart_svcpart         = $pkgpart. "_". $part_pkg->svcpart(\@first_svc); +  $pkgpart_svcpart{$pkgpart}  = $pkgpart_svcpart; +  $svcdb{$pkgpart}            = $part_pkg->part_svc(\@first_svc)->svcdb; +  $layermap{$pkgpart_svcpart} = $svcdb{$pkgpart}; +} + +my @options = ( '', map $pkgpart_svcpart{ $_->pkgpart }, @part_pkg ); +my %labels = ( '' => ( $opt{'empty_label'} || '(none)' ), +               map { $pkgpart_svcpart{ $_->pkgpart } => $_->pkg_comment } +                   @part_pkg +             ); + +my $layer_callback = sub { +  my $layer = shift; +  #$layer_fields, $layer_values, $layer_prefix + +#  my( $pkgpart, $svcpart ) = split('_', $layer); +#  my $svcdb = $svcdb{$pkgpart}; +  my $svcdb = $layer; + +  return '' unless $svcdb; #'<BR><BR><BR><BR><BR>' + +  #full path cause we're being slung around as a coderef (mason closures?) +  include("/edit/cust_main/first_pkg/$svcdb.html", %opt, ); +}; + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg/svc_acct.html b/httemplate/edit/cust_main/first_pkg/svc_acct.html new file mode 100644 index 000000000..150d4c043 --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg/svc_acct.html @@ -0,0 +1,88 @@ +<% ntable("#cccccc") %> + +  <TR> +    <TD ALIGN="right">Username</TD> +    <TD> +      <INPUT TYPE      = "text" +             NAME      = "username" +             VALUE     = "<% $opt{'username'} %>" +             SIZE      = <% $ulen2 %> +             MAXLENGTH = <% $ulen %> +      > +    </TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Domain</TD> +    <TD> +      <SELECT NAME="domsvc"> +        <OPTION>(none)</OPTION> +      </SELECT> +    </TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Password</TD> +    <TD> +      <INPUT TYPE      = "text" +             NAME      = "_password" +             VALUE     = "<% $opt{'password'} %>" +             SIZE      = <% $pmax2 %> +             MAXLENGTH = <% $passwordmax %>> +%     unless ( $opt{'password_verify'} ) { +        (blank to generate) +%     } +    </TD> +  </TR> + +% if ( $opt{'password_verify'} ) { +    <TR> +      <TD ALIGN="right">Re-enter Password</TD> +      <TD> +        <INPUT TYPE      = "text" +               NAME      = "_password2" +               VALUE     = "<% $opt{'password2'} %>" +               SIZE      = <% $pmax2 %> +               MAXLENGTH = <% $passwordmax %>> +      </TD> +    </TR> +% } + +% if ( $conf->exists('security_phrase') ) { +    <TR> +      <TD ALIGN="right">Security Phrase</TD> +      <TD><INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $opt{'sec_phrase'} %>"> +      </TD> +    </TR> +% } else { +    <INPUT TYPE="hidden" NAME="sec_phrase" VALUE=""> +% } + +% if ( $conf->exists('svc_acct-disable_access_number') ) { +    <INPUT TYPE="hidden" NAME="popnum" VALUE=""> +% } else { +    <TR> +      <TD ALIGN="right">Access number</TD> +%#           XXX should gain "area code" selection and labels on the dropdowns +      <TD><% FS::svc_acct_pop::popselector($opt{'popnum'}) %></TD> +    </TR> +% } + +</TABLE> + +<%init> + +#use FS::svc_acct_pop; + +my( %opt ) = @_; + +my $conf = new FS::Conf; + +#false laziness: (mostly) copied from edit/svc_acct.cgi +#$ulen = $svc_acct->dbdef_table->column('username')->length; +my $ulen = dbdef->table('svc_acct')->column('username')->length; +my $ulen2 = $ulen+2; +my $passwordmax = $conf->config('passwordmax') || 8; +my $pmax2 = $passwordmax + 2; + +</%init> diff --git a/httemplate/edit/cust_main/first_pkg/svc_phone.html b/httemplate/edit/cust_main/first_pkg/svc_phone.html new file mode 100644 index 000000000..70e013ece --- /dev/null +++ b/httemplate/edit/cust_main/first_pkg/svc_phone.html @@ -0,0 +1,82 @@ +<% ntable("#cccccc") %> + +%#XXX this should be hidden or something in most/all cases +  <TR> +    <TD ALIGN="right" ID="countrycode_label_td">Country code</TD> +    <TD ID="countrycode_td"> +      <INPUT TYPE      = "text" +             NAME      = "countrycode" +             ID        = "countrycode" +             VALUE     = "<% $opt{'countrycode'} %>" +             SIZE      = 4 +             MAXLENGTH = 3 +      > +    </TD> +  </TR> + +%#we don't know the svcpart until the dropdown is changed :/ +%#<% include('/elements/tr-select-did.html', +%#             'label'      => 'Phone number', +%#             'curr_value' => $opt{'phonenum'}, +%#          ) +%#%> +  <TR> +    <TD ALIGN="right" ID="phonenum_label_td">Phone Number</TD> +    <TD ID="phonenum_td"> +      <INPUT TYPE      = "text" +             NAME      = "phonenum" +             ID        = "phonenum" +             VALUE     = "<% $opt{'phonenum'} %>" +             SIZE      = 21 +             MAXLENGTH = 20 +      > +    </TD> +  </TR> + +  <TR> +    <TD ALIGN="right" ID="sip_password_label_td">SIP password</TD> +    <TD ID="sip_password_td"> +      <INPUT TYPE      = "text" +             NAME      = "sip_password" +             ID        = "sip_password" +             VALUE     = "<% $opt{'sip_password'} %>" +             MAXLENGTH = 80 +      > +    </TD> +  </TR> + +  <TR> +    <TD ALIGN="right" ID="pin_label_td">Voicemail PIN</TD> +    <TD ID="pin_td"> +      <INPUT TYPE      = "text" +             NAME      = "pin" +             ID        = "pin" +             VALUE     = "<% $opt{'pin'} %>" +             SIZE      = 5 +             MAXLENGTH = 4 +      > +    </TD> +  </TR> + +%#XXX this should be hidden or something in most/all cases +  <TR> +    <TD ALIGN="right" ID="phone_name_label_td">Name</TD> +    <TD ID="phone_name_td"> +      <INPUT TYPE      = "text" +             NAME      = "phone_name" +             ID        = "phone_name" +             VALUE     = "<% $opt{'phone_name'} %>" +             MAXLENGTH = 80 +      > +    </TD> +  </TR> + +</TABLE> + +<%init> + +my( %opt ) = @_; + +#my $conf = new FS::Conf; + +</%init> diff --git a/httemplate/edit/cust_main/top_misc.html b/httemplate/edit/cust_main/top_misc.html new file mode 100644 index 000000000..7c9e0395c --- /dev/null +++ b/httemplate/edit/cust_main/top_misc.html @@ -0,0 +1,106 @@ +<% &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 %>"> + +% } + +%# class +<% include('/elements/tr-select-cust_class.html', +             'curr_value'  => $cust_main->classnum, +             'label'       => "Class", +             #'empty_label' => '(none)', +             #'disable_empty' =>  +          ) +%> + +%# referral (advertising source) +%my $refnum = $cust_main->refnum || $conf->config('referraldefault') || 0; +%if ( $custnum && ! $conf->exists('editreferrals') ) { + +  <INPUT TYPE="hidden" NAME="refnum" VALUE="<% $refnum %>"> + +% } else {  + +   <% include('/elements/tr-select-part_referral.html', +                'curr_value' => $refnum +             ) +   %> +% }  + + +%# referring customer +%my $referring_cust_main = ''; +%if ( $cust_main->referral_custnum +%     and $referring_cust_main = +%           qsearchs('cust_main', { custnum => $cust_main->referral_custnum } ) +%) { + +  <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=""> +% }  + +%# signup date +% if ( $conf->exists('cust_main-edit_signupdate') ) { +    <% include('/elements/tr-input-date-field.html', { +                  'name'        => 'signupdate', +                  'value'       => $cust_main->signupdate, +                  'label'       => 'Signup date', +                  'format'      => $conf->config('date_format') || "%m/%d/%Y", +              }) +    %> +% } + +</TABLE> + +<%init> + +my( $cust_main, %opt ) = @_; + +my $custnum = $opt{'custnum'}; + +my $conf = new FS::Conf; + +my $r = qq!<font color="#ff0000">*</font> !; + +</%init> diff --git a/httemplate/edit/cust_main_attach.cgi b/httemplate/edit/cust_main_attach.cgi new file mode 100755 index 000000000..ebbaf3cf3 --- /dev/null +++ b/httemplate/edit/cust_main_attach.cgi @@ -0,0 +1,70 @@ +<% include('/elements/header-popup.html', "$action File Attachment") %> + +<% include('/elements/error.html') %> + +<FORM NAME="attach_edit" ACTION="<% popurl(1) %>process/cust_main_attach.cgi" METHOD=POST ENCTYPE="multipart/form-data"> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> +<INPUT TYPE="hidden" NAME="attachnum" VALUE="<% $attachnum %>"> + +<BR><BR> + +<TABLE BGCOLOR="#cccccc" CELLSPACING=0> +% if(defined $attach) { +%   if($curuser->access_right("Download attachment")) { +<A HREF="<% $p.'view/attachment.html?'.$attachnum %>">Download this file</A><BR> +%   } +<TR><TD> Filename </TD> +<TD><INPUT TYPE="text" NAME="filename" SIZE=40 MAXLENGTH=255 VALUE="<% $attach->filename %>"<% $disabled %>></TD></TR> +<TR><TD> Description </TD> +<TD><INPUT TYPE="text" NAME="title" SIZE=40 MAXLENGTH=80 VALUE="<% $attach->title %>"<% $disabled %></TD></TR> +<TR><TD> MIME type </TD> +<TD><INPUT TYPE="text" NAME="mime_type" VALUE="<% $attach->mime_type %>"<% $disabled %></TD></TR> +<TR><TD> Size </TD><TD><% $attach->size %></TD></TR> +% } +% else { # !defined $attach +<TR><TD> Filename </TD><TD><INPUT TYPE="file" SIZE=24 NAME="file"></TD></TR> +<TR><TD> Description </TD><TD><INPUT TYPE="text" NAME="title" SIZE=40 MAXLENGTH=80></TD></TR> +% } +</TABLE> +<BR> +% if(! $disabled) { +<INPUT TYPE="submit" NAME="submit"  +    VALUE="<% $attachnum ? "Apply Changes" : "Upload File" %>"> +% } +% if(defined $attach and $curuser->access_right('Delete attachment')) { +<BR> +<INPUT TYPE="submit" NAME="delete" value="Delete File"  +onclick="return(confirm('Delete this file?'));"> +% } + +</FORM> +</BODY> +</HTML> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $attachnum = ''; +my $attach; +if ( $cgi->param('error') ) { +  #$comment     = $cgi->param('comment'); +} elsif ( $cgi->param('attachnum') =~ /^(\d+)$/ ) { +  $attachnum = $1; +  die "illegal query ". $cgi->keywords unless $attachnum; +  $attach = qsearchs('cust_attachment', { 'attachnum' => $attachnum }); +  die "no such attachment: ". $attachnum unless $attach; +} + +my $action = $attachnum ? 'Edit' : 'Add'; + +my $disabled=''; +if(! $curuser->access_right("$action attachment")) { +  $disabled = ' disabled="disabled"'; +} + +$cgi->param('custnum') =~ /^(\d+)$/ or die "illegal custnum"; +my $custnum = $1; + +</%init> + diff --git a/httemplate/edit/cust_main_county-expand.cgi b/httemplate/edit/cust_main_county-expand.cgi new file mode 100755 index 000000000..265dd1dab --- /dev/null +++ b/httemplate/edit/cust_main_county-expand.cgi @@ -0,0 +1,52 @@ +<% include('/elements/header-popup.html', "Enter $title") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/cust_main_county-expand.cgi" METHOD=POST> + +<INPUT TYPE="hidden" NAME="taxnum" VALUE="<% $taxnum %>"> + +<TEXTAREA NAME="expansion" COLS="50" ROWS="16"><% $expansion |h %></TEXTAREA> + +<BR> +<INPUT TYPE="submit" VALUE="Add <% $title %>"> + +</FORM> +</BODY> +</HTML> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my($taxnum, $expansion); +my($query) = $cgi->keywords; +if ( $cgi->param('error') ) { +  $taxnum = $cgi->param('taxnum'); +  $expansion = $cgi->param('expansion'); +} else { +  $query =~ /^(\d+)$/ +    or die "Illegal taxnum (query $query)"; +  $taxnum = $1; +  $expansion = ''; +} + +my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum}) +  or die "cust_main_county.taxnum $taxnum not found"; + +my $title; + +die "Can't expand entry!" if $cust_main_county->city; + +if ( $cust_main_county->county ) { +  $title = 'Cities'; +} elsif ( $cust_main_county->state ) { +  $title = 'Counties'; +} else { +  $title = 'States/Provinces'; +} + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/cust_main_county.html b/httemplate/edit/cust_main_county.html new file mode 100644 index 000000000..11b8e43cd --- /dev/null +++ b/httemplate/edit/cust_main_county.html @@ -0,0 +1,64 @@ +<% include('elements/edit.html', +     'popup'  => 1, +     'name'   => 'Tax rate', #Edit tax rate +     'table'  => 'cust_main_county', +     'labels' => { 'taxnum'   => 'Tax', +                   'country'  => 'Country', +                   'state'    => 'State', +                   'county'   => 'County', +                   'city'     => 'City', +                   'taxclass' => 'Tax class', +                   'taxname'  => 'Tax name', +                   'tax'      => 'Tax rate', +                   'setuptax' => 'This tax not applicable to setup fees', +                   'recurtax' => 'This tax not applicable to recurring fees', +                   'exempt_amount' => 'Monthly exemption per customer ($25 "Texas tax")', +                 }, +     'fields' => \@fields, +   ) +%> +<%once> + +my $conf = new FS::Conf; + +</%once> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $taxnum; +if ( $cgi->param('error') ) { +  $cgi->param('taxnum') =~ /^(\d+)$/ +    or die "no taxnum, but error: ". $cgi->param('error'); +  $taxnum = $1; +} else { +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/ or die 'no taxnum'; +  $taxnum = $1; +} + +my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum }) +  or die "unknown taxnum $1"; + +my @fields = ( +  { field=>'country',  type=>'fixed-country', }, +  { field=>'state',    type=>'fixed-state', }, +  { field=>'county',   type=>'fixed', }, +  { field=>'city',     type=>'fixed', }, +); + +push @fields, { field=>'taxclass', type=>'fixed', } +  if $conf->exists('enable_taxclasses'); + +push @fields, +  'taxname', +  { field=>'tax',      type=>'percentage', }, + +  { type=>'tablebreak-tr-title', value=>'Exemptions' }, +  { field=>'setuptax', type=>'checkbox', value=>'Y', }, +  { field=>'recurtax', type=>'checkbox', value=>'Y', }, +  { field=>'exempt_amount', type=>'money', }, +; + +</%init> diff --git a/httemplate/edit/cust_main_note.cgi b/httemplate/edit/cust_main_note.cgi new file mode 100755 index 000000000..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..07e51989e --- /dev/null +++ b/httemplate/edit/cust_pay.cgi @@ -0,0 +1,148 @@ +% 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> + +% if ( $conf->exists('pkg-balances') ) { +  <% include('/elements/tr-select-cust_pkg-balances.html', +               'custnum' => $custnum, +               'cgi'     => $cgi +            ) +  %> +% } else { +  <INPUT TYPE="hidden" NAME="pkgnum" VALUE=""> +% } + +</TABLE> + +<BR> +<INPUT TYPE="submit" VALUE="Post payment"> + +</FORM> + +% if ( $link eq 'popup' ) {  +    </BODY> +    </HTML> +% } else {  +    <% include('/elements/footer.html') %> +% }  + +<%init> + +my $conf = new FS::Conf; + +my $money_char = $conf->config('money_char') || '$'; + +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' || $link eq 'popup' ) { +  $custnum = $linknum; +} + +</%init> diff --git a/httemplate/edit/cust_pay_pending.html b/httemplate/edit/cust_pay_pending.html new file mode 100644 index 000000000..0916a1c0c --- /dev/null +++ b/httemplate/edit/cust_pay_pending.html @@ -0,0 +1,154 @@ +<% include('/elements/header-popup.html', $title ) %> + +% if ( $action eq 'delete' ) { + +    <CENTER><FONT SIZE="+1"><B>Are you sure you want to delete this pending payment?</B></FONT></CENTER> + +% } elsif ( $action eq 'complete' ) { + +    <CENTER><FONT SIZE="+1"><B>No response was received from <% $cust_pay_pending->processor || 'the payment gateway' %> for this transaction.  Check <% $cust_pay_pending->processor || 'the payment gateway' %>'s reporting and determine if this transaction completed successfully.</B></FONT></CENTER> + +% } + +<BR> + +%#false laziness w/view/cust_pay.html +<% include('/elements/small_custview.html', +             $cust_pay_pending->custnum, +             scalar($conf->config('countrydefault')), +             1, #no balance +          ) +%> +<BR> + +<% ntable("#cccccc", 2) %> + +<TR> +  <TD ALIGN="right">Pending payment#</TD> +  <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->paypendingnum %></B></TD> +</TR> + +<TR> +  <TD ALIGN="right">Date</TD> +  <TD BGCOLOR="#FFFFFF"><B><% time2str"%a %b %o, %Y %r", $cust_pay_pending->_date %></B></TD> +</TR> + +<TR> +  <TD ALIGN="right">Amount</TD> +  <TD BGCOLOR="#FFFFFF"><B><% $money_char. $cust_pay_pending->paid %></B></TD> +</TR> + +<TR> +  <TD ALIGN="right">Payment method</TD> +  <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->payby_name %> #<% $cust_pay_pending->paymask %></B></TD> +</TR> + +% #if ( $cust_pay_pending->payby =~ /^(CARD|CHEK|LECB)$/ && $cust_pay_pending->paybatch ) {  + +    <TR> +      <TD ALIGN="right">Processor</TD> +      <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->processor %></B></TD> +    </TR> + +    <TR> +      <TD ALIGN="right">Authorization#</TD> +      <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->authorization %></B></TD> +    </TR> + +%   if ( $cust_pay_pending->order_number ) { +      <TR> +        <TD ALIGN="right">Order#</TD> +        <TD BGCOLOR="#FFFFFF"><B><% $cust_pay_pending->order_number %></B></TD> +      </TR> +%   } + +% #} + +</TABLE> + +<BR> + +<FORM NAME   = "pendingform" +      METHOD = "POST" +      ACTION = "process/cust_pay_pending.html" +> + +<INPUT TYPE="hidden" NAME="paypendingnum" VALUE="<% $paypendingnum %>"> + +<% itable() %> + +% if ( $action eq 'delete' ) { + +    <INPUT TYPE="hidden" NAME="action" VALUE="<% $action %>"> + +    <TR> +      <TD ALIGN="center"> +        <BUTTON TYPE="button" onClick="document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, delete payment</BUTTON> +      </TD> +      <TD>   </TD> +      <TD ALIGN="center"> +        <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, cancel deletion</BUTTON> +      </TD> +    </TR> + +% } elsif ( $action eq 'complete' ) { + +    <INPUT TYPE="hidden" NAME="action" VALUE=""> + +    <TR> +      <TD ALIGN="center"> +        <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'insert_cust_pay'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, transaction completed sucessfully.</BUTTON> +      </TD> +      <TD>   </TD> +      <TD ALIGN="center"> +        <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'decline'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was declined</BUTTON> +      </TD> +      <TD>   </TD> +      <TD ALIGN="center"> +        <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'delete'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was not received</BUTTON> +      </TD> +    </TR> + +    <TR><TD COLSPAN=5></TD></TR> + +    <TR> +      <TD COLSPAN=5 ALIGN="center"> +        <BUTTON TYPE="button" onClick="parent.cClick();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->Cancel payment completion; transaction status not yet known</BUTTON> +      </TD> +    </TR> + +% } + +</TABLE> + +</FORM> +</BODY> +</HTML> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Edit customer pending payments'); + +$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action'; +my $action = $1; +my $title = ucfirst($action). ' pending payment'; + +$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum'; +my $paypendingnum = $1; +my $cust_pay_pending = +  qsearchs({ +    'select'    => 'cust_pay_pending.*', +    'table'     => 'cust_pay_pending', +    'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', +    'hashref'   => { 'paypendingnum' => $paypendingnum }, +    'extra_sql' => ' AND '. $curuser->agentnums_sql, +  }) +  or die 'unknown paypendingnum'; + +my $conf = new FS::Conf; + +my $money_char = $conf->config('money_char') || '$'; + +</%init> diff --git a/httemplate/edit/cust_pay_refund.cgi b/httemplate/edit/cust_pay_refund.cgi new file mode 100755 index 000000000..f82fe36fe --- /dev/null +++ b/httemplate/edit/cust_pay_refund.cgi @@ -0,0 +1,14 @@ +<% include('elements/ApplicationCommon.html', +     'form_action' => 'process/cust_pay_refund.cgi', +     'src_table'   => 'cust_pay', +     'src_thing'   => 'payment', +     'dst_table'   => 'cust_refund', +     'dst_thing'   => 'refund', +   ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/cust_pkg.cgi b/httemplate/edit/cust_pkg.cgi new file mode 100755 index 000000000..dd1ed335f --- /dev/null +++ b/httemplate/edit/cust_pkg.cgi @@ -0,0 +1,150 @@ +<% include('/elements/header.html', "Add/Edit Packages", '') %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/cust_pkg.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="action" VALUE="bulk"> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> + +%#current packages +%my @cust_pkg = qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } ); +%if (@cust_pkg) { + +  Current packages - select to remove (services are moved to a new package below) +  <TABLE> +    <TR STYLE="background-color: #cccccc;"> +      <TH COLSPAN="2">Pkg #</TH> +      <TH>Package description</TH> +    </TR> +  <BR><BR> +% +% +%  foreach ( sort {     $all_pkg{ $a->getfield('pkgpart') } +%                   cmp $all_pkg{ $b->getfield('pkgpart') } +%                 } +%                 @cust_pkg +%          ) +%  { +%    my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') ); +%    my $checked = $remove_pkg{$pkgnum} ? ' CHECKED' : ''; +% +%   + + +    <TR> +      <TD><INPUT TYPE="checkbox" NAME="remove_pkg" VALUE="<% $pkgnum %>"<% $checked %>></TD> +      <TD ALIGN="right"><% $pkgnum %>:</TD> +      <TD><% $all_pkg{$pkgpart} %> - <% $all_comment{$pkgpart} %></TD> +    </TR> +% }  + + +  </TABLE> +  <BR><BR> +% }  + + +Order new packages +<BR><BR> +% +%my $cust_main = qsearchs('cust_main',{'custnum'=>$custnum}); +%my $agent = qsearchs('agent',{'agentnum'=> $cust_main->agentnum }); +% +%my %agent_pkgs = map { ( $_->pkgpart , $all_pkg{$_->pkgpart} ) } +%                     qsearch('type_pkgs',{'typenum'=> $agent->typenum }); +% +%my $count = 0; +%my $pkgparts = 0; +% + + +<TABLE> +  <TR STYLE="background-color: #cccccc;"> +    <TH>Qty.</TH> +    <TH COLSPAN="2">Package Description</TH> +  </TR> +% +%#foreach my $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) { +%foreach my $pkgpart ( sort { $agent_pkgs{$a} cmp $agent_pkgs{$b} } +%                             keys(%agent_pkgs) ) { +%  $pkgparts++; +%  next unless exists $pkg{$pkgpart}; #skip disabled ones +%  #print qq!<TR>! if ( $count == 0 ); +%  my $value = $cgi->param("pkg$pkgpart") || 0; +% + + +  <TR> +    <TD> +      <INPUT TYPE="text" NAME="<% "pkg$pkgpart" %>" VALUE="<% $value %>" SIZE="2" MAXLENGTH="2"> +    </TD> +    <TD ALIGN="right"><% $pkgpart %>:</TD> +    <TD><% $pkg{$pkgpart} %> - <% $comment{$pkgpart}%></TD> +  </TR> +% +%  $count ++ ; +%  #if ( $count == 2 ) { +%  #  print qq!</TR>\n! ; +%  #  $count = 0; +%  #} +%} +% + + +</TABLE> +% unless ( $pkgparts ) { +%     my $p2 = popurl(2); +%     my $typenum = $agent->typenum; +%     my $agent_type = qsearchs( 'agent_type', { 'typenum' => $typenum } ); +%     my $atype = $agent_type->atype; +% + + +     (No <A HREF="<% $p2 %>browse/part_pkg.cgi">package definitions</A>, +     or agent type +     <A HREF="<% $p2 %>edit/agent_type.cgi?<% $typenum %>"><% $atype %></a> +     is not allowed to purchase any packages.) +% }  + + +<P><INPUT TYPE="submit" VALUE="Order"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages'); + +my %pkg = (); +my %comment = (); +my %all_pkg = (); +my %all_comment = (); +#foreach (qsearch('part_pkg', { 'disabled' => '' })) { +#  $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); +#  $comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment'); +#} +foreach (qsearch('part_pkg', {} )) { +  $all_pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); +  $all_comment{ $_ -> getfield('pkgpart') } = $_->custom_comment; +  next if $_->disabled; +  $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); +  $comment{ $_ -> getfield('pkgpart') } = $_->custom_comment; +} + +my($custnum, %remove_pkg); +if ( $cgi->param('error') ) { +  $custnum = $cgi->param('custnum'); +  %remove_pkg = map { $_ => 1 } $cgi->param('remove_pkg'); +} else { +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/; +  $custnum = $1; +  %remove_pkg = (); +} + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/cust_pkg_detail.html new file mode 100644 index 000000000..009ed5c6e --- /dev/null +++ b/httemplate/edit/cust_pkg_detail.html @@ -0,0 +1,142 @@ +<% include("/elements/header-popup.html", $title, '', +            ( $cgi->param('error') ? '' : 'onload="addRow()"' ), +          ) +%> + +%# <% include('/elements/error.html') %> + +<FORM ACTION="process/cust_pkg_detail.html" NAME="DetailForm" ID="DetailForm" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<INPUT TYPE="hidden" NAME="detailtype" VALUE="<% $detailtype %>"> + +<TABLE ID="DetailTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=1 STYLE="background-color: #cccccc"> + +% if ( $curuser->option('show_pkgnum') ) { + +    <TR> +      <TD ALIGN="right">Package #</TD> +      <TD BGCOLOR="#ffffff"><% $pkgnum %></TD> +    </TR> + +% } + +  <TR> +    <TD ALIGN="right">Package</TD> +    <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Comment</TD> +    <TD BGCOLOR="#ffffff"><% $part_pkg->comment %></TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Status</TD> +    <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_pkg->statuscolor %>"><B><% ucfirst($cust_pkg->status) %></B></FONT></TD> +  </TR> + +  <TR> +    <TD COLSPAN=2><% ucfirst($name{$detailtype}) %>: </TD> +  </TR> + +% my $row = 0; +% for ( @details ) {  + +    <TR> +      <TD></TD> +      <TD> +        <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup = "possiblyAddRow;" > +      </TD> +    </TR> + +% }  + +</TABLE> + +<BR> +<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="<% $title %>"> + +</FORM> + +<SCRIPT TYPE="text/javascript"> + +  var rownum = <% $row %>; + +  function possiblyAddRow() { +    if ( ( rownum - this.getAttribute('rownum') ) == 1 ) { +      addRow(); +    } +  } + +  function addRow() { + +    var table = document.getElementById('DetailTable'); +    var tablebody = table.getElementsByTagName('tbody').item(0); + +    var row = document.createElement('TR'); + +    var empty_cell = document.createElement('TD'); +    row.appendChild(empty_cell); + +    var detail_cell = document.createElement('TD'); + +      var detail_input = document.createElement('INPUT'); +      detail_input.setAttribute('name', 'detail'+rownum); +      detail_input.setAttribute('id',   'detail'+rownum); +      detail_input.setAttribute('size', 60); +      detail_input.setAttribute('maxLength', 65); +      detail_input.setAttribute('rownum',   rownum); +      detail_input.onkeyup = possiblyAddRow; +      detail_cell.appendChild(detail_input); + +    row.appendChild(detail_cell); + +    tablebody.appendChild(row); + +    rownum++; + +  } + +</SCRIPT> + +</BODY> +</HTML> +<%init> + +my %access_right = ( +  'I' => 'Edit customer package invoice details',  +  'C' => 'Edit customer package comments', +); + +my %name = ( +  'I' => 'invoice details', +  'C' => 'package comments', +); + +my $curuser = $FS::CurrentUser::CurrentUser; + +$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype'; +my $detailtype = $1; + +my $right = $access_right{$detailtype}; +die "access denied" +  unless $curuser->access_right($right); + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum'; +my $pkgnum = $1; + +my $cust_pkg = qsearchs({ +  'table'     => 'cust_pkg', +  'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', +  'hashref'   => { 'pkgnum' => $pkgnum }, +  'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); + +my $part_pkg = $cust_pkg->part_pkg; + +my @details = $cust_pkg->cust_pkg_detail($detailtype); + +my $title = ( scalar(@details) ? 'Edit ' : 'Add ' ). $name{$detailtype}; + +</%init> diff --git a/httemplate/edit/cust_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/cust_tax_adjustment.html b/httemplate/edit/cust_tax_adjustment.html new file mode 100644 index 000000000..9d4afbc60 --- /dev/null +++ b/httemplate/edit/cust_tax_adjustment.html @@ -0,0 +1,102 @@ +<% include('/elements/header-popup.html', 'Tax adjustment' ) %> + +<% include('/elements/error.html') %> + +<SCRIPT TYPE="text/javascript"> + +function enable_tax_adjustment () { +  if (    document.TaxAdjustmentForm.amount.value +       && document.TaxAdjustmentForm.taxname.selectedIndex > 0  ) { +    document.TaxAdjustmentForm.submit.disabled = false; +  } else { +    document.TaxAdjustmentForm.submit.disabled = true; +  } +} + +function validate_tax_adjustment () { +  var comment = document.TaxAdjustmentForm.comment.value; +  var comment_regex = /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ ; +  var amount = document.TaxAdjustmentForm.amount.value; +  var amount_regex = /^\s*\$?\s*(\d*(\.?\d{1,2}))\s*$/ ; +  var rval = true; + +  if ( ! amount_regex.test(amount) ) { +    alert('Illegal amount - enter the amount of the tax adjustment, for example, "5" or "43" or "21.46".'); +    return false; +  } +  if ( ! comment_regex.test(comment) ) { +    alert('Illegal comment - spaces, letters, numbers, and the following punctuation characters are allowed: . , ! ? @ # $ % & ( ) - + ; : ' + "'" + ' " = [ ]' ); +    return false; +  } + +  return true; +} + +</SCRIPT> + +<FORM ACTION="process/cust_tax_adjustment.html" NAME="TaxAdjustmentForm" ID="TaxAdjustmentForm" METHOD="POST" onsubmit="document.TaxAdjustmentForm.submit.disabled=true;return validate_tax_adjustment();"> + +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> + +<TABLE ID="TaxAdjustmentTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 STYLE="background-color: #cccccc"> + +<TR> +  <TD ALIGN="right">Tax </TD> +  <TD> +    <SELECT NAME="taxname" ID="taxname" onChange="enable_tax_adjustment()" onKeyPress="enable_tax_adjustment()"> +      <OPTION VALUE=""></OPTION> +%     foreach my $taxname (@taxname) { +        <OPTION VALUE="<% $taxname %>"><% $taxname %></OPTION> +%     } +    </SELECT> +  </TD> +</TR> + +<TR> +  <TD ALIGN="right">Amount </TD> +  <TD> +    $<INPUT TYPE="text" NAME="amount" SIZE=6 VALUE="<% $amount %>" onChange="enable_tax_adjustment()" onKeyPress="enable_tax_adjustment()"> +  </TD> +</TR> + +<TR> +  <TD ALIGN="right">Comment </TD> +  <TD> +    <INPUT TYPE="text" NAME="comment" SIZE="50" MAXLENGTH="50" VALUE="<% $comment %>" onChange="enable_tax_adjustment()" onKeyPress="enable_tax_adjustment()"> +  </TD> +</TR> + +</TABLE> + +<BR> +<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="Add tax adjustment" <% $cgi->param('error') ? '' :' DISABLED' %>> + +</FORM> + +</BODY> +</HTML> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Add customer tax adjustment'); + +my $sql = 'SELECT DISTINCT(taxname) FROM cust_main_county'; +my $sth = dbh->prepare($sql) or die dbh->errstr; +$sth->execute() or die $sth->errstr; +my @taxname = map { $_->[0] || 'Tax' } @{ $sth->fetchall_arrayref([]) }; + +my $conf = new FS::Conf; + +$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum'; +my $custnum = $1; + +my $amount = ''; +if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) { +  $amount = $1; +} + +$cgi->param('comment') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/  +  or die 'illegal description'; +my $comment = $1; + +</%init> diff --git a/httemplate/edit/elements/ApplicationCommon.html b/httemplate/edit/elements/ApplicationCommon.html new file mode 100644 index 000000000..08e1d46e6 --- /dev/null +++ b/httemplate/edit/elements/ApplicationCommon.html @@ -0,0 +1,520 @@ +<%doc> + +Examples: + +  #cust_bill_pay +  include('elements/ApplicationCommon.html', +    'form_action' => 'process/cust_bill_pay.cgi',  +    'src_table'   => 'cust_pay', +    'src_thing'   => 'payment', +    'dst_table'   => 'cust_bill', +    'dst_thing'   => 'invoice', +  ) + +  #cust_credit_bill +  include('elements/ApplicationCommon.html', +    'form_action' => 'process/cust_credit_bill.cgi', +    'src_table'   => 'cust_credit', +    'src_thing'   => 'credit', +    'dst_table'   => 'cust_bill', +    'dst_thing'   => 'invoice', +  ) + +  #cust_pay_refund +  include('elements/ApplicationCommon.html', +    'form_action' => 'process/cust_pay_refund.cgi', +    'src_table'   => 'cust_pay', +    'src_thing'   => 'payment', +    'dst_table'   => 'cust_refund', +    'dst_thing'   => 'refund', +  ) + +  #cust_credit_refund +  include('elements/ApplicationCommon.html', +    'form_action' => 'process/cust_credit_refund.cgi', +    'src_table'   => 'cust_credit', +    'src_thing'   => 'credit', +    'dst_table'   => 'cust_refund', +    'dst_thing'   => 'refund', +  ) + +</%doc> + +<% include('/elements/header-popup.html', "Apply $src_thing$to", '', 'onLoad="myOnLoadFunction();"') %> + +<% include('/elements/error.html') %> + +<P ID="ErrorMessage"></P> +<FORM ACTION="<% $p1. $opt{'form_action'} %>" NAME="ApplicationForm" ID="ApplicationForm" METHOD=POST> + +<% $src_thing %> #<B><% $src_pkeyvalue %></B><BR> +<INPUT TYPE="hidden" NAME="<% $src_pkey %>" VALUE="<% $src_pkeyvalue %>"> + +<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + +<TR> +  <TD ALIGN="right">Date: </TD> +  <TD><B><% time2str("%D", $src->_date) %></B></TD> +</TR> + +<TR> +  <TD ALIGN="right">Amount: </TD> +  <TD ID="original_amount"><B><% $money_char %><% $src_amount %></B> +  </TD> +  <TD> +% if ($use_sub_dst_thing && $can_change_credit) { +    <INPUT TYPE="hidden" NAME="src_amount" VALUE="<% $src_amount %>" > +    <BUTTON TYPE="button" NAME="expand_button" ID="expand_button" onClick="do_change_amount(this);">Change</BUTTON> +% } +  </TD> + +</TR> + +<TR> +  <TD ALIGN="right">Unapplied amount: </TD> +  <TD ID="unapplied_amount"><B><% $money_char %><% $unapplied %></B></TD> +</TR> + +% if ( $src_table eq 'cust_credit' ) { +    <TR> +      <TD ALIGN="right">Reason: </TD> +      <TD COLSPAN=2><B><% $src->reason %></B></TD> +    </TR> +% } + +</TABLE> +<BR> + +<SCRIPT TYPE="text/javascript"> +function changed(what) { +  dst = what.options[what.selectedIndex].value; + +  if ( dst == '' ) { +    what.form.submit.disabled=true; +%if ($src_pkey eq 'crednum') { +    what.form.tax_button.disabled=true; +%} +    return true; +  } + +  what.form.submit.disabled=false; +%if ($src_pkey eq 'crednum') { +  what.form.tax_button.disabled=false; +%} + +% foreach my $dst ( @dst ) { + +    if ( dst == <% $dst->$dst_pkey %> ) { +      what.form.amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>"; +%     if ($use_sub_dst_thing) { +        what.form.display_amount.value = "<% min($dst->$dst_unapplied, $unapplied) %>"; + +        var rownum=0 +        var table = document.getElementById('ApplicationTable'); +        while(table.rows[2]) { +          table.deleteRow(2); +        } +%       my $app_class = "FS::$link_table"; +%       my $temp_app = $app_class->new( +%         { $src_pkey => $src_pkeyvalue, +%           $dst_pkey => $dst->$dst_pkey, +%           'amount'  => min($dst->$dst_unapplied, $unapplied), +%         } +%       ); +%       my %apphash = (); +%       my $listref_or_error = $temp_app->calculate_applications; +%       %apphash = map { &{$key_generator}($_), $_ } @$listref_or_error +%         if ref($listref_or_error); +%       foreach my $cbp ( $dst->open_cust_bill_pkg ) { +%         my $desc = $cbp->desc; +%         my $total_owed = $cbp->owed_setup + $cbp->owed_recur; +%         my $key = &{$key_generator}([ $cbp, 0, {} ]); +%         my $amount = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0; +%         unless ( $cbp->pkgnum ) { +%           foreach my $taxX ( $cbp->cust_bill_pkg_tax_Xlocation ) { +%             my $pkey = $taxX->primary_key; +%             my $owed = $taxX->owed; +%             my $key = &{$key_generator}([ $cbp, 0, { $pkey => $taxX->$pkey } ]); +%             my $toapp = exists($apphash{ $key }) ? $apphash{ $key }->[1] : 0; +              <% &{$row_generator}( $key, $cbp, $taxX->desc, $owed, $toapp, $taxX->$pkey ) %> +%             $total_owed -= $owed; +%             $amount -= $toapp; +%           } +%           $desc .= ' (default)'; +%         } +%         if ( $total_owed > 0 ) { +            <% &{$row_generator}($key, $cbp, $desc, $total_owed, $amount, '') %> +%         } +%       } +%     } +    } + +% }  + +} + +function sub_changed(what) { + +  var amount = 0; +  var table = document.getElementById('ApplicationTable'); +  var i = table.rows.length; +  while(i-- > 2) { +    var inputs = table.rows[i].getElementsByTagName('input'); +    if (! inputs.length) { +      continue; +    } +    amount += parseFloat( inputs.item(0).value ) || 0;  +  } +  what.form.amount.value = parseFloat(amount).toFixed(2); +  what.form.display_amount.value = parseFloat(amount).toFixed(2); +  set_amount_color(what); + +} + +function set_amount_color(what) { +  if (what.form.src_amount.value < what.form.amount.value) { +    what.form.display_amount.style.color = '#ff0000'; +  } else { +    what.form.display_amount.style.color = '#00ff00'; +  } +} + +</SCRIPT> + +Apply to: + +% if ($use_sub_dst_thing && $src_pkey eq 'crednum') { +<CENTER><BUTTON TYPE="button" NAME="tax_button" ID="tax_button" onClick="do_calculate_tax(this);" DISABLED>Calculate Tax</BUTTON></CENTER> +<% include( '/elements/xmlhttp.html', +            'url' =>  $p.'misc/xmlhttp-calculate_taxes.html', +            'subs' => [ 'calculate_taxes' ], +          ) + %> +<SCRIPT TYPE="text/javascript"> + +function show_taxes(arg) { +  var argsHash = eval('(' + arg + ')'); + +  var button = document.getElementById('tax_button'); +  button.disabled = false; +  button.innerHTML = 'Calculate Tax'; + +  var error = argsHash['error']; + +  var paragraph = document.getElementById('ErrorMessage'); +  if (error) { +    paragraph.innerHTML = 'Error: ' + error; +    paragraph.style.color = '#ff0000'; +  } else { +    paragraph.innerHTML = ''; +  } +  var taxlines = argsHash['taxlines']; + +  var table = document.getElementById('ApplicationTable'); + +  var aFoundRow = 0; +  for (i = 0; taxlines[i]; i++) { +    var itemdesc = taxlines[i][0]; +    var locnum   = taxlines[i][2]; +    if (taxlines[i][3]) { +      locnum  = taxlines[i][3]; +    } + +    var found = 0; +    for (var row = 2; table.rows[row]; row++) { +      var inputs = table.rows[row].getElementsByTagName('input'); +      if (! inputs.length) { +        while ( table.rows[row] ) { +           table.deleteRow(row); +        } +        break; +      } +      if ( inputs.item(4).value == itemdesc && inputs.item(2).value == locnum ) +      { +        inputs.item(0).value = taxlines[i][1]; +        aFoundRow = found = row; +        break; +      } +    } +    if (! found) { +      var row = table.insertRow(table.rows.length); +      var warning_cell = document.createElement('TD'); +      warning_cell.style.color = '#ff0000'; +      warning_cell.colSpan = 2; +      warning_cell.innerHTML = 'Calculated Tax - ' + itemdesc + ' - ' + +                               taxlines[i][1] + ' will not be applied'; +      row.appendChild(warning_cell); +    } +  } + +  if (aFoundRow) { +    sub_changed(table.rows[aFoundRow].getElementsByTagName('input').item(0)); +  } +     +} + +function do_calculate_tax (what) { +  what.innerHTML = 'Calculating....'; +  what.disabled = true; +  var taxed_items = new Array(); +  var table = document.getElementById('ApplicationTable'); +  for (var row = 2; table.rows[row]; row++) +  { +    var inputs = table.rows[row].getElementsByTagName('input'); +    if ( !inputs.length ) { +      break; +    } +    var taxed_item = new Array( +      inputs.item(1).value, // billpkgnum +      inputs.item(3).value, // s_or_r +      inputs.item(0).value  // amount +    ); +    taxed_items.push(taxed_item); +  } + +  var args = new Array( +    'crednum', '<% $src_pkeyvalue %>', +    'items', taxed_items +  ); +  calculate_taxes ( args, show_taxes ); +} + +function do_change_amount (what) { +  var amount_cell = document.getElementById('original_amount'); +  var inputs = amount_cell.getElementsByTagName('input'); +  if (inputs.length) { +    src_amount_changed(); +    amount_cell.innerHTML = '<B><% $money_char %></B>' + inputs.item(0).value; +  } else { +    amount_cell.innerHTML = '<% $money_char %>'; +    var amount_input = document.createElement('INPUT'); +    amount_input.setAttribute('name', 'entered_amount'); +    amount_input.setAttribute('id',   'entered_amount'); +    amount_input.style.textAlign = 'right'; +    amount_input.setAttribute('size', 8); +    amount_input.setAttribute('maxlength', 8); +    amount_input.setAttribute('value', what.form.src_amount.value); +    amount_input.setAttribute('onChange', "src_amount_changed(this);"); +    amount_cell.appendChild(amount_input); +  } +} + +function src_amount_changed () { +  //alert('src_amount_changed called'); +  var entered_amount = document.getElementById('entered_amount'); +  if ( entered_amount ) { +    entered_amount.form.src_amount.value = entered_amount.value; +    var unapplied_cell = document.getElementById('unapplied_amount'); +    unapplied_cell.innerHTML = '<B><% $money_char %>' + entered_amount.value + '</B>'; +    set_amount_color(entered_amount); +  } +  return true; +} + +</SCRIPT> + +%} + +<TABLE ID="ApplicationTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0> + +<TR> +  <TD ALIGN="right"><% $dst_thing %>: </TD> +  <TD><SELECT NAME="<% $dst_pkey %>" SIZE=1 onChange="changed(this)"> +<OPTION VALUE="">Select <% $dst_thing %> + +% foreach my $dst ( @dst ) {  +  <OPTION<% $dst->$dst_pkey eq $dst_pkeyvalue ? ' SELECTED' : '' %> VALUE="<% $dst->$dst_pkey %>">#<% $dst->$dst_pkey %> - <% time2str("%D", $dst->_date) %> - $<% $dst->$dst_unapplied %> +% }  + +</SELECT> +  </TD> +</TR> + +<TR> +  <TD ALIGN="right">Amount: </TD> +  <TD><% $money_char %><INPUT TYPE="text" NAME="<% $use_sub_dst_thing ? 'display_' : '' %>amount" VALUE="<% $amount %>" SIZE=8 MAXLENGTH=8 <% $use_sub_dst_thing ? 'DISABLED' : '' %> STYLE="text-align:right;"></TD> +% if ($use_sub_dst_thing) { +    <INPUT TYPE="hidden" NAME="amount" VALUE="<% $amount %>" > +% } +</TR> + +</TABLE> + +<BR> +<CENTER><INPUT TYPE="submit" +               VALUE="Apply" +               NAME="submit" +               ID="submit" +% if ($use_sub_dst_thing && $can_change_credit) { +               onClick="src_amount_changed()" +% } +               DISABLED +></CENTER> + +</FORM> + +<SCRIPT TYPE="text/javascript"> + +function myOnLoadFunction () { +  <% $onload %> +} + +</SCRIPT> + +<% include('/elements/footer.html') %> + +<%init> + +my %opt = @_; + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; + +my $src_thing = ucfirst($opt{'src_thing'}); +my $src_table = $opt{'src_table'}; +my $src_pkey = dbdef->table($src_table)->primary_key; + +my $dst_thing = ucfirst($opt{'dst_thing'}); +my $dst_table = $opt{'dst_table'}; +my $dst_pkey = dbdef->table($dst_table)->primary_key; +my $dst_unapplied = $dst_table eq 'cust_bill' ? 'owed' : 'unapplied'; + +$opt{form_action} =~ /^process\/(.*)\./ or die "bad form action"; +my $link_table = $1; + +my $use_sub_dst_thing = 0; +$use_sub_dst_thing = 1 +  if ( $dst_table eq 'cust_bill' && $conf->exists("${link_table}_pkg-manual") ); + +my $can_change_credit = 0; +$can_change_credit = 1 +  if ( $src_table eq 'cust_credit' &&  +       $FS::CurrentUser::CurrentUser->access_right('Post credit') && +       $FS::CurrentUser::CurrentUser->access_right('Delete credit') +     ); + +my $to = $dst_table eq 'cust_refund' ? ' to Refund' : ''; + +my($src_pkeyvalue, $amount, $dst_pkeyvalue, $src_amount); +if ( $cgi->param('error') ) { +  $src_pkeyvalue = $cgi->param($src_pkey); +  $amount    = $cgi->param('amount'); +  $dst_pkeyvalue    = $cgi->param($dst_pkey); +  $src_amount = $cgi->param('src_amount'); +} else { +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/; +  $src_pkeyvalue = $1; +  $amount = ''; +  $dst_pkeyvalue = ''; +} + +my $otaker = getotaker; + +my $p1 = popurl(1); + +my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } ); +die "$src_thing $src_pkeyvalue not found!" unless $src; + +$src_amount = $src->amount unless $cgi->param('error'); + +my $unapplied = $src->unapplied; + +my @dst = sort {    $a->_date     <=> $b->_date +                 or $a->$dst_pkey <=> $b->$dst_pkey +               } +          grep { $_->$dst_unapplied != 0 } +          qsearch($dst_table, { 'custnum' => $src->custnum } ); + +my $row_generator = sub { +  my ($key, $cust_bill_pkg, $desc, $owed, $amount, $taxXnum) = @_; +  my ($num, $s_or_r, $taxlinenum) = split(':', $key); +  my $id = $cust_bill_pkg->pkgnum || 'Tax'; +  my $billpkgnum = $cust_bill_pkg->billpkgnum; +  my $s_or_r = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur'; + +  $amount = sprintf("%.2f", $amount); +  qq! +      var tablebody = document.getElementsByTagName('tbody').item(0); +      var row = table.insertRow(rownum+2); +      var pkg_cell = document.createElement('TD'); +      pkg_cell.style.textAlign = 'right'; +      pkg_cell.innerHTML = "$id - $desc - $owed:"; +      var amount_cell = document.createElement('TD'); +      amount_cell.innerHTML = "$money_char"; +      var amount_input = document.createElement('INPUT'); +      amount_input.setAttribute('name', 'subamount'+rownum); +      amount_input.setAttribute('id',   'subamount'+rownum); +      amount_input.style.textAlign = 'right'; +      amount_input.setAttribute('size', 8); +      amount_input.setAttribute('maxlength', 8); +      amount_input.setAttribute('rownum', rownum); +      amount_input.setAttribute('value', "$amount"); +      amount_input.setAttribute('onChange', "sub_changed(this);"); +      amount_cell.appendChild(amount_input); +      var subnum_input = document.createElement('INPUT'); +      subnum_input.setAttribute('name', 'subnum'+rownum); +      subnum_input.setAttribute('id',   'subnum'+rownum); +      subnum_input.setAttribute('type', 'hidden'); +      subnum_input.setAttribute('rownum', rownum); +      subnum_input.setAttribute('value', "$billpkgnum"); +      amount_cell.appendChild(subnum_input); +      var taxnum_input = document.createElement('INPUT'); +      taxnum_input.setAttribute('name', 'taxXlocationnum'+rownum); +      taxnum_input.setAttribute('id',   'taxXlocationnum'+rownum); +      taxnum_input.setAttribute('type', 'hidden'); +      taxnum_input.setAttribute('rownum', rownum); +      taxnum_input.setAttribute('value', "$taxXnum"); +      amount_cell.appendChild(taxnum_input); +      var s_or_r_input = document.createElement('INPUT'); +      s_or_r_input.setAttribute('name', 's_or_r'+rownum); +      s_or_r_input.setAttribute('id',   's_or_r'+rownum); +      s_or_r_input.setAttribute('type', 'hidden'); +      s_or_r_input.setAttribute('rownum', rownum); +      s_or_r_input.setAttribute('value', "$s_or_r"); +      amount_cell.appendChild(s_or_r_input); +      var itemdesc_input = document.createElement('INPUT'); +      itemdesc_input.setAttribute('name', 'itemdesc'+rownum); +      itemdesc_input.setAttribute('id',   'itemdesc'+rownum); +      itemdesc_input.setAttribute('type', 'hidden'); +      itemdesc_input.setAttribute('rownum', rownum); +      itemdesc_input.setAttribute('value', "$desc"); +      amount_cell.appendChild(itemdesc_input); +      row.appendChild(pkg_cell); +      row.appendChild(amount_cell); +      rownum++; +    !; +}; + +my $key_generator = sub { +  my $listref = shift; +  my ($cust_bill_pkg, $amount, $hashref) = @$listref; +  my $setup_or_recur = $cust_bill_pkg->setup > 0 ? 'setup' : 'recur'; +  my $taxlinenum = $hashref->{billpkgtaxlocationnum} || +                   $hashref->{billpkgtaxratelocationnum} || +                   ''; +                    +  join(':', $cust_bill_pkg->billpkgnum, $setup_or_recur, $taxlinenum); +}; + +my $onload = 'return true;'; + +if ($cgi->param('error')) { + +  my $set_sub_amounts = +    join(';', map { "myform.subamount$_.value = ". $cgi->param("subamount$_") } +               grep { /.+/ } +               map { /^subnum(\d+)$/ ? $1 : '' } +               $cgi->param +  ); +  $set_sub_amounts &&= "$set_sub_amounts;sub_changed(myform.subamount0)"; + +  $onload = qq! +    var myform = document.getElementById('ApplicationForm'); +    changed(myform.elements['$dst_pkey']); +    $set_sub_amounts; +    return true; +  !; +} + +</%init> diff --git a/httemplate/edit/elements/category_Common.html b/httemplate/edit/elements/category_Common.html new file mode 100644 index 000000000..8bbdcd15b --- /dev/null +++ b/httemplate/edit/elements/category_Common.html @@ -0,0 +1,24 @@ +<% include( 'edit.html', +              'fields' => [ +                            'categoryname', +                            'weight', +                            { field=>'disabled', type=>'checkbox', value=>'Y', }, +                          ], +              'labels' => {  +                            'categorynum'  => 'Category number', +                            'categoryname' => 'Category name', +                            'weight'       => 'Weight', +                            'disabled'     => 'Disable category', +                          }, +              'viewall_dir' => 'browse', +              %opt, +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %opt = @_; + +</%init> diff --git a/httemplate/edit/elements/class_Common.html b/httemplate/edit/elements/class_Common.html new file mode 100644 index 000000000..b5f493991 --- /dev/null +++ b/httemplate/edit/elements/class_Common.html @@ -0,0 +1,32 @@ +<% include( 'edit.html', +              'fields' => [ +                            'classname', +                            (scalar(@category) +                              ? { field=>'categorynum', type=>'select-table', 'empty_label'=>'(none)', 'table'=>'pkg_category', 'name_col'=>'categoryname' } +                              : { field=>'categorynum', type=>'hidden' } +                            ), +                            { field=>'disabled', type=>'checkbox', value=>'Y', }, +                          ], +              'labels' => {  +                            'classnum'    => 'Class number', +                            'classname'   => 'Class name', +                            'categorynum' => 'Category', +                            'disabled'    => 'Disable class', +                          }, +              'viewall_dir' => 'browse', +              %opt, +           ) +           +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %opt = @_; + +my $table = $opt{'table'}; +( my $category_table = $table ) =~ s/class/category/ or die; + +my @category = qsearch($category_table, { 'disabled' => '' }); +</%init> diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html new file mode 100644 index 000000000..fd73e031e --- /dev/null +++ b/httemplate/edit/elements/edit.html @@ -0,0 +1,816 @@ +<%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 ) = @_; }, + +    #after the mode-specific object creation/search +    'end_callback' = sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + +    ###mode-specific callbacks.  one (and only one) of these four is called +    +    #run when adding +    'new_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + +    #run when editing +    'edit_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + +    #run when re-displaying with an error +    'error_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, +    +    #run when cloning +    'clone_callback' => sub { my( $cgi, $object, $fields_listref, $opt_hashref ) = @_; }, + +    ###callbacks called in new mode only + +    # returns a hashref for the new object +    'new_hashref_callback' + +    # returns the new object iself (otherwise, ->new is called) +    'new_object_callback' + +    ###display callbacks + +    #run before display to return a different value +    'value_callback' => sub { my( $columname, $value ) = @_; }, + +    #run before display to manipulate element of the 'fields' arrayref +    'field_callback' => sub { my( $cgi, $object, $field_hashref ) = @_; }, + +  ); + +</%doc> + +<% include('/elements/header'. ( $opt{popup} ? '-popup' : '' ). '.html', +              $title, +              include( '/elements/menubar.html', @menubar ), +              $opt{'body_etc'}, +           ) +%> + +<% defined($opt{'html_init'})  +      ? ( ref($opt{'html_init'}) +            ? &{$opt{'html_init'}}() +            : $opt{'html_init'} +        ) +      : '' +%> + +<% include('/elements/error.html') %> + +% my $url = $opt{'post_url'} || popurl(1)."process/$table.html"; + +<FORM 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++; +%     $include_common{'colspan'} = $f->{colspan} if $f->{colspan}; +%   } +% +%   my $layer_prefix_on = ''; +% +%   my $include_sub = sub { +%     my %opt = @_; +% +%     my $fieldnum   = delete $opt{'fieldnum'}; +% +%     my $include = $type; +%     $include = "input-$include" if $include =~ /^(text|money|percentage)$/; +%     $include = "tr-$include" unless $include =~ /^(hidden|tablebreak|column)/; +% +%     $include_common{'layer_prefix'} = "$field$fieldnum." +%       if $layer_prefix_on; +% +%     my @include =  +%     ( "/elements/$include.html", +%         'field'      => "$field$fieldnum", +%         'id'         => "$field$fieldnum", #separate? +%         'label_id'   => $field."_label$fieldnum", #don't want field0_label0... +%         %include_common, +%         %opt, +%     ); +%     @include; +%   }; +% +%   my $column_sub = sub { +%     my %opt = @_; +% +%     my $column   = delete($opt{field}); +%     my $fieldnum = delete($opt{fieldnum}); +%     my $include  = delete($opt{type}) || 'text'; +%     $include = "input-$include" if $include =~ /^(text|money|percentage)$/; +% +%     ( "/elements/$include.html", +%         'field'        => $field.'__'.$column.$fieldnum, +%         'id'           => $field.'__'.$column.$fieldnum, +%         'layer_prefix' => $field.'__'.$column.$fieldnum.".", +%         ( $fieldnum +%             ? ('cell_style' => 'border-top:1px solid black') +%             : () +%         ), +%         'cgi' => $cgi, +%         %opt, +%     ); +%   }; +% +%   unless ( $type =~ /^column/ ) { +%     $g_row = 1 if $type eq 'tablebreak-tr-title'; +%     $g_row++; +%     $g_row++ if $type eq 'title'; +%   } else { +%     if ( $type eq 'columnstart' ) { +%       push @g_row_stack, $g_row; +%       $g_row = 0; +%     #} elsif ( $type eq 'columnnext' ) { +%     } elsif ( $type eq 'columnend' ) { +%       $g_row = pop @g_row_stack;  +%     } +%   +%   } +% +%   my $fieldnum = ''; +%   my $curr_value = ''; +%   if ( $f->{'m2name_table'} || $f->{'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' : '' ), +%       ); +%       $existing[0] =~ s(^/elements/tr-)(/elements/); +%       my @label = @existing; +%       $label[0] = '/elements/tr-td-label.html'; + +        <% include( @label ) %> +        <TD> +        <% include( @existing ) %> +        </TD> + +%       if ( $f->{'m2_fields'} ) { +%         foreach my $c ( @{ $f->{'m2_fields'} } ) { +%           my $column = $c->{field}; +%           my @column = &{ $column_sub }( %$c, +%                                          'fieldnum' => $fieldnum, +%                                          'curr_value' => $name_obj->$column() +%                                        ); + +            <TD id='<% $field %>__<% $column %>_label<% $fieldnum %>' +                style='text-align:right;vertical-align:top; +                       border-top:1px solid black;padding-top:5px;'> +              <% $c->{'label'} || '' %> +            </TD> +            <TD style='border-top:1px solid black;padding-top:3px;'> +              <% include( @column ) %> +            </TD> +%         } +%       } + +        </TR> + +%       $fieldnum++; +%       $g_row++; +%     } +%     #$field .= $fieldnum; +%     $onchange .= "\nspawn_$field(what);"; +%   } else { +%     if ( $f->{curr_value_callback} ) { +%       $curr_value = &{ $f->{curr_value_callback} }( $cgi, $object, $field ), +%     } else { +%       $curr_value = $object->$field(); +%     } +%     $curr_value = &{ $opt{'value_callback'} }( $f->{'field'}, $curr_value ) +%       if $opt{'value_callback'} && $mode ne 'error'; +%   } +% +%   my @include = &{ $include_sub }( +%     'label'      => $label, +%     'fieldnum'   => $fieldnum, +%     'curr_value' => $curr_value, +%     'object'     => $object, +%     'cgi'        => $cgi, +%     'onchange'   => $onchange, +%     ( $fieldnum ? ('cell_style' => 'border-top:1px solid black') : () ), +%   ); +% +%   if ( $f->{'m2name_table'} || $f->{'m2m_method'} ) { +%     $include[0] =~ s(^/elements/tr-)(/elements/); +%     my @label = @include; +%     $label[0] = '/elements/tr-td-label.html'; + +      <% include( @label ) %> +      <TD> +      <% include( @include ) %> +      </TD> + +%     if ( $f->{'m2_fields'} ) { +%       foreach my $c ( @{ $f->{'m2_fields'} } ) { +%         my $column = $c->{field}; +%         my @column = &{ $column_sub }( %$c, 'fieldnum' => $fieldnum ); + +          <TD id='<% $field %>__<% $column %>_label<% $fieldnum %>' +              style='text-align:right;vertical-align:top; +                     border-top:1px solid black;padding-top:5px;'> +            <% $c->{'label'} || '' %> +          </TD> +          <TD style='border-top:1px solid black;padding-top:3px;'> +            <% include( @column ) %> +          </TD> +%       } +%     } + +      </TR> + +%   } else { + +      <% include( @include ) %> + +%   } +%   if ( $f->{'m2name_table'} || $f->{'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_fields'} ) { +%           foreach my $c ( @{ $f->{'m2_fields'} } ) { +%             my $column = $c->{field}; +%             my @column = &{ $column_sub }(%$c, 'fieldnum' => 'MAGIC_NUMBER'); + +              var column =  <% include(@column, html_only=>1) |js_string %>; +              column  = column.replace(  magic_regex, <%$field%>_fieldnum ); + +              var column_label = document.createElement('TD'); +              column_label.id = +                '<% $field %>__<% $column %>_label' + <%$field%>_fieldnum; + +              column_label.style.textAlign = "right"; +              column_label.style.verticalAlign = "top"; +              column_label.style.borderTop = "1px solid black"; +              column_label.style.paddingTop = "5px"; + +              column_label.innerHTML = '<% $c->{'label'} || '' %>'; + +              row.appendChild(column_label); +           +              var column_widget = document.createElement('TD'); + +              column_widget.style.borderTop = "1px solid black"; +              column_widget.style.paddingTop = "3px"; + +              column_widget.innerHTML = column; + +              row.appendChild(column_widget); + +%           } +%         } + +%         if ( $f->{'m2_new_js'} ) { +            // take out items selected in previous dropdowns +            var new_element = document.getElementById("<%$field%>" + <%$field%>_fieldnum ); +            <% $f->{'m2_new_js'} %>(new_element); + +            if ( new_element.length < 2 ) { +              //just the ** Select new **, so don't display the row +              row.style.display = 'none'; +            } +%         } + +        } + +        function remove_<%$field%>(remove_fieldnum) { +          //alert("remove <%$field%> " + remove_fieldnum); +          var select = document.getElementById('<%$field%>' + remove_fieldnum); + +          if ( ! select ) { +            alert("can't find element <%$field%>" + remove_fieldnum); +            return; +          } + +%         my $warnings = $f->{'m2_remove_warnings'}; +%         if ( $warnings ) { +            var sel_value = select.options[select.selectedIndex].value; +%           foreach my $value ( keys %$warnings ) { +              if ( sel_value == '<% $value %>' ) { +                if ( ! confirm( <% $warnings->{$value} |js_string %> ) ) { +                  return; +                } +              } +%           } +%         } + +          select.disabled = 'disabled'; // this seems to prevent it from being submitted on tested browsers so far (IE, moz, konq at least) +          var label_td = document.getElementById('<%$field%>_label' + remove_fieldnum ); +          label_td.parentNode.style.display = 'none'; + +%         if ( $f->{m2_remove_js} ) { +            var opt = select.options[select.selectedIndex]; +            <% $f->{m2_remove_js} %>( opt.value, opt.text, 'no_match'); +%         } + +        } + +      </SCRIPT> + +%   } + +% }  + +<% ref( $opt{'html_table_bottom'} ) +      ? &{ $opt{'html_table_bottom'} }( $object ) +      : $opt{'html_table_bottom'} +%> + +</TABLE> + +<% ref( $opt{'html_bottom'} ) +      ? &{ $opt{'html_bottom'} }( $object ) +      : $opt{'html_bottom'} +%> + +<BR> + +<INPUT TYPE="submit" ID="submit" VALUE="<% ( !$clone && $object->$pkey() ) ? "Apply changes" : "Add ". ( $opt{'name'} || $opt{'name_singular'} ) %>"> + +</FORM> + +<% ref( $opt{'html_foot'} ) +      ? &{ $opt{'html_foot'} }( $object ) +      : $opt{'html_foot'} +%> + +<% include("/elements/footer.html") %> +<%init> + +my(%opt) = @_; + +my $curuser = $FS::CurrentUser::CurrentUser; + +#false laziness w/process.html +my $table = $opt{'table'}; +my $class = "FS::$table"; +my $pkey = dbdef->table($table)->primary_key; #? $opt{'primary_key'} ||  +my $fields = $opt{'fields'} +             #|| [ grep { $_ ne $pkey } dbdef->table($table)->columns ]; +             || [ grep { $_ ne $pkey } fields($table) ]; +#my @actualfields = map { ref($_) ? $_->{'field'} : $_ } @$fields; + +if ( $cgi->param('redirect') ) { +  my $session = $cgi->param('redirect'); +  my $pref = $curuser->option("redirect$session"); +  die "unknown redirect session $session\n" unless length($pref); +  $cgi = new CGI($pref); +} + +&{$opt{'begin_callback'}}( $cgi, $fields, \%opt ) +  if $opt{'begin_callback'}; + +my %qsearch = ( +    'table'     => $table, +    'extra_sql' => ( $opt{'agent_virt'} +                       ? ' AND '. $curuser->agentnums_sql( +                                    'null_right' => $opt{'agent_null_right'} +                                  ) +                       : '' +                   ), +); + +my $mode; +my $object; +my $clone = ''; +if ( $cgi->param('error') ) { + +  $mode = 'error'; + +  $object = $class->new( { +    map { $_ => scalar($cgi->param($_)) } fields($table) +  }); + +  &{$opt{'error_callback'}}( $cgi, $object, $fields, \%opt ) +    if $opt{'error_callback'}; + +} elsif ( $cgi->param('clone') =~ /^(\d+)$/ ) { + +  $mode = 'clone'; + +  $clone = $1; + +  $qsearch{'extra_sql'} = ' AND '. $opt{'agent_clone_extra_sql'} +    if $opt{'agent_clone_extra_sql'}; + +  $object = qsearchs({ %qsearch, 'hashref' => { $pkey => $clone } }) +    or die "$pkey $clone not found in $table"; + +  &{$opt{'clone_callback'}}( $cgi, $object, $fields, \%opt ) +    if $opt{'clone_callback'}; + +  #$object->$pkey(''); + +  $opt{action} ||= 'Add'; + +} elsif ( $cgi->keywords || $cgi->param($pkey) ) { #editing + +  $mode = 'edit'; + +  my $value; +  if ( $cgi->param($pkey) ) { +    $value = $cgi->param($pkey) +  } else {  +    my( $query ) = $cgi->keywords; +    $value = $query; +  } +  $value =~ /^(\d+)$/ or die "unparsable $pkey"; +  $object = qsearchs({ %qsearch, 'hashref' => { $pkey => $1 } }) +    or die "$pkey $1 not found in $table"; +   +  warn "$table $pkey => $1" +    if $opt{'debug'}; + +  &{$opt{'edit_callback'}}( $cgi, $object, $fields, \%opt ) +    if $opt{'edit_callback'}; + +} else { #adding + +  $mode = 'new'; + +  my $hashref = $opt{'new_hashref_callback'} +                  ? &{$opt{'new_hashref_callback'}} +                  : {}; + +  $object = $opt{'new_object_callback'} +              ? &{$opt{'new_object_callback'}}( $cgi, $hashref, $fields, \%opt ) +              : $class->new( $hashref ); + +  &{$opt{'new_callback'}}( $cgi, $object, $fields, \%opt ) +    if $opt{'new_callback'}; + +} + +&{$opt{'end_callback'}}( $cgi, $object, $fields, \%opt ) +  if $opt{'end_callback'}; + +$opt{action} ||= $object->$pkey() ? 'Edit' : 'Add'; + +my $title = $opt{action}. ' '. ( $opt{name} || $opt{'name_singular'} ); + +my $viewall_url = $p . ( $opt{'viewall_dir'} || 'search' ) . "/$table.html"; +$viewall_url = $opt{'viewall_url'} if $opt{'viewall_url'};   + +my @menubar = (); +if ( $opt{'menubar'} ) { +  @menubar = @{ $opt{'menubar'} }; +} else { +  my $items = $opt{'name'} ? $opt{'name'}.'s' : PL($opt{'name_singular'}); +  @menubar = ( +    "View all $items" => $viewall_url, +  ); +} + +</%init> diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html new file mode 100644 index 000000000..ef04bd04a --- /dev/null +++ b/httemplate/edit/elements/svc_Common.html @@ -0,0 +1,144 @@ +<% include( 'edit.html', + +                 'menubar' => [], + +                 'error_callback' => sub { +                   my( $cgi, $svc_x, $fields, $opt ) = @_; +                   #$svcnum = $svc_x->svcnum; +                   $pkgnum  = $cgi->param('pkgnum'); +                   $svcpart = $cgi->param('svcpart'); + +                   $part_svc = qsearchs( 'part_svc', { svcpart=>$svcpart }); +                   die "No part_svc entry!" unless $part_svc; + +                   label_fixup($part_svc, $opt); + +                   $svc_x->setfield('svcpart', $svcpart); +                 }, + +                 'edit_callback' => sub { +                   my( $cgi, $svc_x, $fields, $opt ) = @_; +                   #$svcnum = $svc_x->svcnum; +                   my $cust_svc = $svc_x->cust_svc +                     or die "Unknown (cust_svc) svcnum!"; + +                   $pkgnum  = $cust_svc->pkgnum; +                   $svcpart = $cust_svc->svcpart; +   +                   $part_svc = qsearchs ('part_svc', { svcpart=>$svcpart }); +                   die "No part_svc entry!" unless $part_svc; + +                   label_fixup($part_svc, $opt); +                 }, + +                 'new_hashref_callback' => sub { +                   #my( $cgi, $svc_x ) = @_; + +                   { svcpart => $svcpart }; + +                 }, + +                 'new_callback' => sub { +                    my( $cgi, $svc_x, $fields, $opt ) = @_;; + +                    $part_svc = qsearchs( 'part_svc', { svcpart=>$svcpart }); +                    die "No part_svc entry!" unless $part_svc; + +                    label_fixup($part_svc, $opt); + +                    #$svcnum=''; + +                    $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 +             ) +%> +<%once> + +sub label_fixup { +  my( $part_svc, $opt ) = @_; + +  #false laziness w/view/svc_Common.html +  #override default labels with service-definition labels if applicable +  my $labels = $opt->{labels}; # with -> here +  foreach my $field ( keys %$labels ) { +    my $col = $part_svc->part_svc_column($field); +    $labels->{$field} = $col->columnlabel if $col->columnlabel !~ /^\S*$/; +  } + +} + +</%once> +<%init> + +my %opt = @_; + +#my( $svcnum, $pkgnum, $svcpart, $part_svc ); +my( $pkgnum, $svcpart, $part_svc ); + +#get & untaint pkgnum & svcpart +if ( ! $cgi->param('error') +     && $cgi->param('pkgnum') && $cgi->param('svcpart') +   ) +{ +  $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; +  $pkgnum = $1; +  $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; +  $svcpart = $1; +  #$cgi->delete_all(); #so edit.html treats this correctly as new?? +} + +</%init> diff --git a/httemplate/edit/inventory_class.html b/httemplate/edit/inventory_class.html new file mode 100644 index 000000000..3ab47fe28 --- /dev/null +++ b/httemplate/edit/inventory_class.html @@ -0,0 +1,16 @@ +<% include( 'elements/edit.html', +                 'name'   => 'Inventory Class', +                 'table'  => 'inventory_class', +                 'labels' => {  +                               'classnum'  => 'Class number', +                               'classname' => 'Class name', +                             }, +                 'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/invoice_logo.html b/httemplate/edit/invoice_logo.html new file mode 100644 index 000000000..e1c61496f --- /dev/null +++ b/httemplate/edit/invoice_logo.html @@ -0,0 +1,136 @@ +<% include("/elements/header.html", "Edit $type2desc{$type} invoice logo", +             menubar( +               'View all invoice templates' => $p.'browse/invoice_template.html' +             ) +          ) +%> + +% if ( $error ) {  +  <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error %></FONT> +  <BR><BR> +% }  + +% if ( $cgi->param('msg') ) {  +  <FONT SIZE="+1"><B><% $cgi->param('msg') |h %></B></FONT> +  <BR><BR> +% }  + +% if ( $mode eq 'upload' ) { +  <FORM ACTION="invoice_logo.html" METHOD="POST" ENCTYPE="multipart/form-data"> +  <INPUT TYPE="hidden" NAME="mode" VALUE="preview"> +% } elsif ( $mode eq 'preview' ) { +  <FORM ACTION="process/invoice_logo.html" METHOD="POST"> +  <INPUT TYPE="hidden" NAME="preview_session" VALUE="<% $session %>"> +% } + +<INPUT TYPE="hidden" NAME="type" VALUE="<% $type %>"> +<INPUT TYPE="hidden" NAME="name" VALUE="<% $name %>"> + +<% include('/elements/table-grid.html') %> + +<TR> +  <TH CLASS="grid" BGCOLOR="#cccccc">Current logo</TH> +  <TH CLASS="grid" BGCOLOR="#cccccc">New logo preview</TH> +</TR> + +<TR> + +  <TD CLASS="grid" BGCOLOR="#ffffff"> + +%   if ( $type eq 'png' ) { + +      <IMG SRC="<% $p %>view/logo.cgi?type=png;name=<% $name %>"> + +%   } elsif ( $type eq 'eps' ) { + +     <i>EPS preview not yet supported</i> + +%   } + +  </TD> + +  <TD CLASS="grid" BGCOLOR="#ffffff"> + +% if ( $mode eq 'upload' ) { + +    Upload new logo (.<%uc($type)%> format): <INPUT TYPE="file" NAME="new_logo"> +    <BR><INPUT TYPE="submit" NAME="submit" VALUE="Upload"> + +% } elsif ( $mode eq 'preview' ) { + +    <IMG SRC="<% $p %>view/logo.cgi?type=png;preview_session=<% $session %>"> + +% } + +  </TD> + + +</TR> + +</TABLE> + +% if ( $mode eq 'preview' ) { +  <BR> +  <INPUT TYPE="submit" NAME="submit" VALUE="Change logo"> +% } + +</FORM> + +<% include("/elements/footer.html") %> + +<%once> + +my %type2desc = ( +  'png'  => 'online', +  'eps'  => 'Print/PDF (typeset)', +); + +</%once> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; + +my $type = $cgi->param('type'); + +$cgi->param('name') =~ /^([^\.\/]*)$/ or die "illegal name"; +my $name = $1; + +$cgi->param('mode') =~ /^(\w*)$/ or die "illegal mode"; +my $mode = $1 || 'upload'; + +my $error = ''; +my $session = ''; +if ( $mode eq 'preview' ) { + +  my $fh = $cgi->upload('new_logo'); + +  if ( defined $fh ) { + +    local $/; +    my $logo_data = <$fh>; + +    $session = int(rand(4294967296)); #XXX +    my $pref = new FS::access_user_pref({ +      'usernum'    => $FS::CurrentUser::CurrentUser->usernum, +      'prefname'   => "logo_preview$session", +      'prefvalue'  => encode_base64($logo_data), +      'expiration' => time + 3600, #1h?  1m? +    }); +    my $pref_error = $pref->insert; +    if ( $pref_error ) { +      die "FATAL: couldn't set preview cookie: $pref_error\n"; +    } + +  } else { + +    $mode = 'upload'; +    $error = 'No file uploaded'; + +  } + +} + +</%init> diff --git a/httemplate/edit/invoice_template.html b/httemplate/edit/invoice_template.html new file mode 100644 index 000000000..9cec62c86 --- /dev/null +++ b/httemplate/edit/invoice_template.html @@ -0,0 +1,69 @@ +<% include("/elements/header.html", "Edit $type2desc{$type} invoice template", +             menubar( +               'View all invoice templates' => $p.'browse/invoice_template.html' +             ) +          ) +%> + +<FORM ACTION="process/invoice_template.html" METHOD="POST"> +<INPUT TYPE="hidden" NAME="confname" VALUE="<% $confname %>"> + +% if ( $type eq 'html' ) { + +  <% include('/elements/htmlarea.html', +               'field'      => 'value', +               'curr_value' => $value, +               'height'     => 800, +            ) +  %> + +% } else { + +  <TEXTAREA NAME="value" ROWS=30 COLS=80 WRAP="off"><%$value |h %></TEXTAREA> + +% } + +<BR><BR> +<INPUT TYPE="submit" VALUE="Change template"> + +</FORM> + +<% include("/elements/footer.html") %> + +<%once> + +my %type2desc = ( +  'html'  => 'HTML', +  'latex' => 'Print/PDF (typeset)', +  'text'  => 'Plaintext', +); + +my %type2base = ( +  'html'  => 'invoice_html', +  'latex' => 'invoice_latex', +  'text'  => 'invoice_template', +); + +</%once> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $type = $cgi->param('type'); +my $name = $cgi->param('name'); +my $suffix = $cgi->param('suffix'); + +#XXX type handling, just testing this out for now + +my $conf = new FS::Conf; + +my $value = length($name) +              ? join("\n", $conf->config_orbase($type2base{$type}.$suffix, $name) ) +              : join("\n", $conf->config($type2base{$type}.$suffix) ); + +my $confname = length($name) +                 ? $type2base{$type}.$suffix. '_'. $name +                 : $type2base{$type}.$suffix; + +</%init> diff --git a/httemplate/edit/msgcat.cgi b/httemplate/edit/msgcat.cgi new file mode 100755 index 000000000..85b300876 --- /dev/null +++ b/httemplate/edit/msgcat.cgi @@ -0,0 +1,54 @@ +<% header("Edit Message catalog" ) %> +<BR> + +<% include('/elements/error.html') %> + +<% $widget->html %> + +    </TABLE> +  </BODY> +</HTML> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $widget = new HTML::Widgets::SelectLayers( +  'selected_layer' => 'en_US', +  'options'        => { 'en_US'=>'en_US' }, +  'form_action'    => 'process/msgcat.cgi', +  'layer_callback' => sub { +    my $layer = shift; +    my $html = qq!<INPUT TYPE="hidden" NAME="locale" VALUE="$layer">!. +               "<BR>Messages for locale $layer<BR>". table(). +               "<TR><TH COLSPAN=2>Code</TH>". +               "<TH>Message</TH>"; +    $html .= "<TH>en_US Message</TH>" unless $layer eq 'en_US'; +    $html .= '</TR>'; + +    #foreach my $msgcat ( sort { $a->msgcode cmp $b->msgcode } +    #                       qsearch('msgcat', { 'locale' => $layer } ) ) { +    foreach my $msgcat ( qsearch('msgcat', { 'locale' => $layer } ) ) { +      $html .= +        '<TR><TD>'. $msgcat->msgnum. '</TD><TD>'. $msgcat->msgcode. '</TD>'. +        '<TD><INPUT TYPE="text" SIZE=32 '. +        qq! NAME="!. $msgcat->msgnum. '" '. +        qq!VALUE="!. ($cgi->param($msgcat->msgnum)||$msgcat->msg). qq!"></TD>!; +      unless ( $layer eq 'en_US' ) { +        my $en_msgcat = qsearchs('msgcat', { +          'locale'  => 'en_US', +          'msgcode' => $msgcat->msgcode, +        } ); +        $html .= '<TD>'. $en_msgcat->msg. '</TD>'; +      } +      $html .= '</TR>'; +    } + +    $html .= '</TABLE><BR><INPUT TYPE="submit" VALUE="Apply changes">'; + +    $html; +  }, + +); + +</%init> diff --git a/httemplate/edit/part_bill_event.cgi b/httemplate/edit/part_bill_event.cgi new file mode 100755 index 000000000..c0ff38689 --- /dev/null +++ b/httemplate/edit/part_bill_event.cgi @@ -0,0 +1,570 @@ +<% include('/elements/header.html', +      "$action Invoice Event Definition", +      menubar( +        'View all invoice events' => popurl(2). 'browse/part_bill_event.cgi', +      ) +    ) +%> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% popurl(1) %>process/part_bill_event.cgi" NAME="editEvent" METHOD=POST> +<INPUT TYPE="hidden" NAME="eventpart" VALUE="<% $part_bill_event->eventpart %>"> +Invoice Event #<% $hashref->{eventpart} ? $hashref->{eventpart} : "(NEW)" %> + +<%  ntable("#cccccc",2) %> + +  <TR> +    <TD ALIGN="right">Event name </TD> +    <TD><INPUT TYPE="text" NAME="event" VALUE="<% $hashref->{event} %>"></TD> +  </TR> + +  <TR> +    <TD ALIGN="right">For </TD> +    <TD> +      <SELECT NAME="payby" <% $hashref->{eventpart} ? '' : 'MULTIPLE SIZE=7'%>> +% tie my %payby, 'Tie::IxHash', FS::payby->cust_payby2longname; +%           foreach my $payby ( keys %payby ) { +          <OPTION VALUE="<% $payby %>"<% ($part_bill_event->payby eq $payby) ? ' SELECTED' : '' %>><% $payby{$payby} %></OPTION> +% }  +      </SELECT> customers +    </TD> +  </TR> +% my $days = $hashref->{seconds}/86400;  + + +  <TR> +    <TD ALIGN="right">After</TD> +    <TD><INPUT TYPE="text" NAME="days" VALUE="<% $days %>"> days</TD> +  </TR> + +  <TR> +    <TD ALIGN="right">Test event</TD> +    <TD> +      <SELECT NAME="freq"> +% tie my %freq, 'Tie::IxHash', '1d' => 'daily', '1m' => 'monthly'; +%           foreach my $freq ( keys %freq ) { +%         + + +          <OPTION VALUE="<% $freq %>"<% ($part_bill_event->freq eq $freq) ? ' SELECTED' : '' %>><% $freq{$freq} %></OPTION> +% }  + + +      </SELECT> +    </TD> +  </TR> + + +  <TR> +    <TD ALIGN="right">Disabled</TD> +    <TD> +      <INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>> +    </TD> +  </TR> + +  <TR> +    <TD VALIGN="top" ALIGN="right">Action</TD> +    <TD> +% +% +%#print ntable(); +% +%sub select_pkgpart { +%  my $label = shift; +%  my $plandata = shift; +%  my %selected = map { $_=>1 } split(/,\s*/, $plandata->{$label}); +%  qq(<SELECT NAME="$label" MULTIPLE>). +%  join("\n", map { +%    '<OPTION VALUE="'. $_->pkgpart. '"'. +%    ( $selected{$_->pkgpart} ? ' SELECTED' : '' ). +%    '>'. $_->pkg_comment +%  } qsearch('part_pkg', { 'disabled' => '' } ) ). +%  '</SELECT>'; +%} +% +%sub select_agentnum { +%  my $plandata = shift; +%  #my $agentnum = $plandata->{'agentnum'}; +%  my %agentnums = map { $_=>1 } split(/,\s*/, $plandata->{'agentnum'}); +%  '<SELECT NAME="agentnum" MULTIPLE>'. +%  join("\n", map { +%    '<OPTION VALUE="'. $_->agentnum. '"'. +%    ( $agentnums{$_->agentnum} ? ' SELECTED' : '' ). +%    '>'. $_->agent +%  } qsearch('agent', { 'disabled' => '' } ) ). +%  '</SELECT>'; +%} +% +%sub honor_dundate { +%  my $label = shift; +%  my $plandata = shift; +%  '<TABLE>'. +%  '<TR><TD ALIGN="right">Allow delay until dun date? </TD>'. +%  qq(<TD><INPUT TYPE="checkbox" NAME="$label" VALUE="$label => 1," ). +%    ( $plandata->{$label} eq "$label => 1," ? 'CHECKED' : '' ). +%  '>'. +%  '</TD></TR>'. +%  '</TABLE>' +%} +% +%my $conf = new FS::Conf; +%my $money_char = $conf->config('money_char') || '$'; +% +%my $late_taxclass = ''; +%my $late_percent_taxclass = ''; +%if ( $conf->exists('enable_taxclasses') ) { +%  $late_taxclass = +%    '<BR>Taxclass '. +%    include('/elements/select-taxclass.html', +%              'curr_value' => '%%%late_taxclass%%%', +%              'name' => 'late_taxclass' ); +%  $late_percent_taxclass = +%    '<BR>Taxclass '. +%    include('/elements/select-taxclass.html', +%              'curr_value' => '%%%late_percent_taxclass%%%', +%              'name' => 'late_percent_taxclass' ); +%} +% +%#this is pretty kludgy right here. +%tie my %events, 'Tie::IxHash', +% +%  'fee' => { +%    'name'   => 'Late fee (flat)', +%    'code'   => '$cust_main->charge( %%%charge%%%, \'%%%reason%%%\', \'$%%%charge%%%\', \'%%%late_taxclass%%%\' );', +%    'html'   =>  +%      'Amount <INPUT TYPE="text" SIZE="7" NAME="charge" VALUE="%%%charge%%%">'. +%      '<BR>Reason <INPUT TYPE="text" NAME="reason" VALUE="%%%reason%%%">'. +%      $late_taxclass, +%    'weight' => 10, +%  }, +%  'fee_percent' => { +%    'name'   => 'Late fee (percentage)', +%    'code'   => '$cust_main->charge( sprintf(\'%.2f\', $cust_bill->owed * %%%percent%%% / 100 ), \'%%%percent_reason%%%\', \'%%%percent%%% percent\', \'%%%late_percent_taxclass%%%\' );', +%    'html'   =>  +%      'Percent <INPUT TYPE="text" SIZE="2" NAME="percent" VALUE="%%%percent%%%">%'. +%      '<BR>Reason <INPUT TYPE="text" NAME="percent_reason" VALUE="%%%percent_reason%%%">'. +%      $late_percent_taxclass, +%    'weight' => 10, +%  }, +%  'suspend' => { +%    'name'   => 'Suspend', +%    'code'   => '$cust_main->suspend(reason => %%%sreason%%%, %%%honor_dundate%%% );', +%    'html'   => sub { &honor_dundate('honor_dundate', @_) }, +%    'weight' => 10, +%    'reason' => 'S', +%  }, +%  'suspend-if-balance' => { +%    'name'   => 'Suspend if balance (this invoice and previous) over', +%    'code'   => '$cust_bill->cust_suspend_if_balance_over( %%%balanceover%%%, reason => %%%sreason%%%, %%%balance_honor_dundate%%% );', +%    'html'   => sub { " $money_char ". '<INPUT TYPE="text" SIZE="7" NAME="balanceover" VALUE="%%%balanceover%%%"> '. &honor_dundate('balance_honor_dundate', @_) }, +%    'weight' => 10, +%    'reason' => 'S', +%  }, +%  'suspend-if-pkgpart' => { +%    'name'   => 'Suspend packages', +%    'code'   => '$cust_main->suspend_if_pkgpart({pkgparts => [%%%if_pkgpart%%%,], reason => %%%sreason%%%, %%%if_pkgpart_honor_dundate%%% });', +%    'html'   => sub { &select_pkgpart('if_pkgpart', @_). &honor_dundate('if_pkgpart_honor_dundate', @_) }, +%    'weight' => 10, +%    'reason' => 'S', +%  }, +%  'suspend-unless-pkgpart' => { +%    'name'   => 'Suspend packages except', +%    'code'   => '$cust_main->suspend_unless_pkgpart({unless_pkgpart => [%%%unless_pkgpart%%%], reason => %%%sreason%%%, %%%unless_pkgpart_honor_dundate%%% });', +%    'html'   => sub { &select_pkgpart('unless_pkgpart', @_). &honor_dundate('unless_pkgpart_honor_dundate' => @_) }, +%    'weight' => 10, +%    'reason' => 'S', +%  }, +%  'cancel' => { +%    'name'   => 'Cancel', +%    'code'   => '$cust_main->cancel(reason => %%%creason%%%);', +%    'weight' => 80, #10, +%    'reason' => 'C', +%  }, +% +%  'addpost' => { +%    'name' => 'Add postal invoicing', +%    'code' => '$cust_main->invoicing_list_addpost(); "";', +%    'weight'  => 20, +%  }, +% +%  'comp' => { +%    'name' => 'Pay invoice with a complimentary "payment"', +%    'code' => '$cust_bill->comp();', +%    'weight' => 90, #30, +%  }, +% +%  'credit' => { +%    'name'   => "Create and apply a credit for the customer's balance (i.e. write off as bad debt)", +%    'code'   => '$cust_main->credit( $cust_main->balance, \'%%%credit_reason%%%\' );', +%    'html'   => '<INPUT TYPE="text" NAME="credit_reason" VALUE="%%%credit_reason%%%">', +%    'weight' => 30, +%  }, +% +%  'realtime-card' => { +%    'name' => 'Run card with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway', +%    'code' => '$cust_bill->realtime_card();', +%    'weight' => 30, +%  }, +% +%  'realtime-check' => { +%    'name' => 'Run check with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway', +%    'code' => '$cust_bill->realtime_ach();', +%    'weight' => 30, +%  }, +% +%  'realtime-lec' => { +%    'name' => 'Run phone bill ("LEC") billing with a <a href="http://search.cpan.org/search?mode=module&query=Business%3A%3AOnlinePayment">Business::OnlinePayment</a> realtime gateway', +%    'code' => '$cust_bill->realtime_lec();', +%    'weight' => 30, +%  }, +% +%  'batch-card' => { +%    'name' => 'Add card or check to a pending batch', +%    'code' => '$cust_bill->batch_card(%options);', +%    'weight' => 40, +%  }, +% +%   +%  #'retriable' => { +%  #  'name' => 'Mark batched card event as retriable', +%  #  'code' => '$cust_pay_batch->retriable();', +%  #  'weight' => 60, +%  #}, +% +%  'send' => { +%    'name' => 'Send invoice (email/print/fax)', +%    'code' => '$cust_bill->send();', +%    'weight' => 50, +%  }, +% +%  'send_email' => { +%    'name' => 'Send invoice (email only)', +%    'code' => '$cust_bill->email();', +%    'weight' => 50, +%  }, +% +%  'send_alternate' => { +%    'name' => 'Send invoice (email/print/fax) with alternate template', +%    'code' => '$cust_bill->send(\'%%%templatename%%%\');', +%    'html' => +%        '<INPUT TYPE="text" NAME="templatename" VALUE="%%%templatename%%%">', +%    'weight' => 50, +%  }, +% +%  'send_if_newest' => { +%    'name' => 'Send invoice (email/print/fax) with alternate template, if it is still the newest invoice (useful for late notices - set to 31 days or later)', +%    'code' => '$cust_bill->send_if_newest(\'%%%if_newest_templatename%%%\');', +%    'html' => +%        '<INPUT TYPE="text" NAME="if_newest_templatename" VALUE="%%%if_newest_templatename%%%">', +%    'weight' => 50, +%  }, +% +%  'send_agent' => { +%    'name' => 'Send invoice (email/print/fax) ', +%    'code' => '$cust_bill->send( \'%%%agent_templatename%%%\', +%                                 [ %%%agentnum%%% ], +%                                 \'%%%agent_invoice_from%%%\', +%                                 %%%agent_balanceover%%% +%                               );', +%    'html' => sub { +%        '<TABLE BORDER=0> +%          <TR> +%            <TD ALIGN="right">only for agent(s) </TD> +%            <TD>'. &select_agentnum(@_). '</TD> +%          </TR> +%          <TR> +%            <TD ALIGN="right">with template </TD> +%            <TD> +%              <INPUT TYPE="text" NAME="agent_templatename" VALUE="%%%agent_templatename%%%"> +%            </TD> +%          </TR> +%          <TR> +%            <TD ALIGN="right">email From: </TD> +%            <TD> +%              <INPUT TYPE="text" NAME="agent_invoice_from" VALUE="%%%agent_invoice_from%%%"> +%            </TD> +%          </TR> +%          <TR> +%            <TD ALIGN="right">if balance (this invoice and previous) over +%            </TD> +%            <TD> +%              '. $money_char. '<INPUT TYPE="text" SIZE="7" NAME="agent_balanceover" VALUE="%%%agent_balanceover%%%"> +%            </TD> +%          </TR> +%        </TABLE>'; +%    }, +%    'weight' => 50, +%  }, +% +%  'send_csv_ftp' => { +%    'name' => 'Upload CSV invoice data to an FTP server', +%    'code' => '$cust_bill->send_csv( protocol   => \'ftp\', +%                                     server     => \'%%%ftpserver%%%\', +%                                     username   => \'%%%ftpusername%%%\', +%                                     password   => \'%%%ftppassword%%%\', +%                                     dir        => \'%%%ftpdir%%%\', +%                                     \'format\' => \'%%%ftpformat%%%\', +%                                   );', +%    'html' => +%        '<TABLE BORDER=0>'. +%        '<TR><TD ALIGN="right">Format ("default" or "billco"): </TD>'. +%          '<TD>'. +%            '<!--'. +%            '<SELECT NAME="ftpformat">'. +%              '<OPTION VALUE="default">Default'. +%              '<OPTION VALUE="billco">Billco'. +%            '</SELECT>'. +%            '-->'. +%            '<INPUT TYPE="text" NAME="ftpformat" VALUE="%%%ftpformat%%%">'. +%          '</TD></TR>'. +%        '<TR><TD ALIGN="right">FTP server: </TD>'. +%          '<TD><INPUT TYPE="text" NAME="ftpserver" VALUE="%%%ftpserver%%%">'. +%          '</TD></TR>'. +%        '<TR><TD ALIGN="right">FTP username: </TD><TD>'. +%          '<INPUT TYPE="text" NAME="ftpusername" VALUE="%%%ftpusername%%%">'. +%          '</TD></TR>'. +%        '<TR><TD ALIGN="right">FTP password: </TD><TD>'. +%          '<INPUT TYPE="text" NAME="ftppassword" VALUE="%%%ftppassword%%%">'. +%          '</TD></TR>'. +%        '<TR><TD ALIGN="right">FTP directory: </TD>'. +%          '<TD><INPUT TYPE="text" NAME="ftpdir" VALUE="%%%ftpdir%%%">'. +%          '</TD></TR>'. +%        '</TABLE>', +%    'weight' => 50, +%  }, +% +%  'spool_csv' => { +%    'name' => 'Spool CSV invoice data', +%    'code' => '$cust_bill->spool_csv( +%                 \'format\' => \'%%%spoolformat%%%\', +%                 \'dest\'   => \'%%%spooldest%%%\', +%                 \'balanceover\' => \'%%%spoolbalanceover%%%\', +%                 \'agent_spools\' => \'%%%spoolagent_spools%%%\', +%               );', +%    'html' => sub { +%       my $plandata = shift; +% +%       my $html = +%       '<TABLE BORDER=0>'. +%       '<TR><TD ALIGN="right">Format: </TD>'. +%         '<TD>'. +%           '<SELECT NAME="spoolformat">'; +% +%       foreach my $option (qw( default billco )) { +%         $html .= qq(<OPTION VALUE="$option"); +%         $html .= ' SELECTED' if $option eq $plandata->{'spoolformat'}; +%         $html .= ">\u$option"; +%       } +% +%       $html .=  +%           '</SELECT>'. +%         '</TD></TR>'. +%       '<TR><TD ALIGN="right">For destination: </TD>'. +%         '<TD>'. +%           '<SELECT NAME="spooldest">'; +% +%       tie my %dest, 'Tie::IxHash',  +%         ''      => '(all)', +%         'POST'  => 'Postal Mail', +%         'EMAIL' => 'Email', +%         'FAX'   => 'Fax', +%       ; +% +%       foreach my $dest (keys %dest) { +%         $html .= qq(<OPTION VALUE="$dest"); +%         $html .= ' SELECTED' if $dest eq $plandata->{'spooldest'}; +%         $html .= '>'. $dest{$dest}; +%       } +% +%       $html .= +%           '</SELECT>'. +%         '</TD></TR>'. +% +%       '<TR>'. +%         '<TD ALIGN="right">if balance (this invoice and previous) over </TD>'. +%         '<TD>'. +%           "$money_char ". +%           '<INPUT TYPE="text" SIZE="7" NAME="spoolbalanceover" VALUE="%%%spoolbalanceover%%%">'. +%         '</TD>'. +%       '<TR><TD ALIGN="right">Individual per-agent spools? </TD>'. +%         '<TD><INPUT TYPE="checkbox" NAME="spoolagent_spools" VALUE="1" '. +%           ( $plandata->{'spoolagent_spools'} ? 'CHECKED' : '' ). +%           '>'. +%         '</TD></TR>'. +%       '</TABLE>'; +% +%       $html; +%    }, +%    'weight' => 50, +%  }, +% +%  'bill' => { +%    'name' => 'Generate invoices (normally only used with a <i>Late Fee</i> event)', +%    'code' => '$cust_main->bill();', +%    'weight'  => 60, +%  }, +% +%  'apply' => { +%    'name' => 'Apply unapplied payments and credits', +%    'code' => '$cust_main->apply_payments_and_credits; "";', +%    'weight'  => 70, +%  }, +% +%; +% +<SCRIPT TYPE="text/javascript">var myreasons = new Array();</SCRIPT> +%foreach my $event ( keys %events ) { +%  my %plandata = map { /^(\w+) (.*)$/; ($1, $2); } +%                   split(/\n/, $part_bill_event->plandata); +%  my $html = $events{$event}{html}; +%  if ( ref($html) eq 'CODE' ) { +%    $html = &{$html}(\%plandata); +%  } +%  while ( $html =~ /%%%(\w+)%%%/ ) { +%    my $field = $1; +%    $html =~ s/%%%$field%%%/$plandata{$field}/; +%  } +% +<SCRIPT TYPE="text/javascript">myreasons.push('<% $events{$event}{reason} %>'); +</SCRIPT> +%  if ($event eq $part_bill_event->plan){ +%    $currentreasonclass=$events{$event}{reason}; +%  } +%  print ntable( "#cccccc", 2). +%        qq!<TR><TD><INPUT TYPE="radio" NAME="plan_weight_eventcode" !; +%  print "CHECKED " if $event eq $part_bill_event->plan; +%  print qq!onClick="showhide_table()" !; +%  print qq!VALUE="!.  $event. ":". $events{$event}{weight}. ":". +%        encode_entities($events{$event}{code}). +%        qq!">$events{$event}{name}</TD>!; +%  print '<TD>'. $html. '</TD>' if $html; +%  print qq!</TR>!; +%  print '</TABLE>'; +%  print qq!<HR WIDTH="90%">!; +%} +% +%  if ($currentreasonclass eq 'C'){ +%    if ($cgi->param('creason') =~ /^(-?\d+)$/){ +%      $creason =  $1; +%    }else{ +%      $creason = $part_bill_event->reason; +%    } +%    if ($cgi->param('newcreasonT') =~ /^(\d+)$/){ +%      $newcreasonT =  $1; +%    } +%    if ($cgi->param('newcreason') =~ /^([\w\s]+)$/){ +%      $newcreason =  $1; +%    } +%  }elsif ($currentreasonclass eq 'S'){ +%    if ($cgi->param('sreason') =~ /^(-?\d+)$/){ +%      $sreason =  $1; +%    }else{ +%      $sreason = $part_bill_event->reason; +%    } +%    if ($cgi->param('newsreasonT') =~ /^(\d+)$/){ +%      $newsreasonT =  $1; +%    } +%    if ($cgi->param('newsreason') =~ /^([\w\s]+)$/){ +%      $newsreason =  $1; +%    } +%  } +% + +</TD></TR> +</TABLE> + +<SCRIPT TYPE="text/javascript"> +  function showhide_table() +  { +    for(i=0;i<document.editEvent.plan_weight_eventcode.length;i++){ +      if (document.editEvent.plan_weight_eventcode[i].checked == true){ +        currentevent=i; +      } +    } +    if(myreasons[currentevent] == 'C'){ +      document.getElementById('Ctable').style.display = 'inline'; +      document.getElementById('Stable').style.display = 'none'; +    }else if(myreasons[currentevent] == 'S'){ +      document.getElementById('Ctable').style.display = 'none'; +      document.getElementById('Stable').style.display = 'inline'; +    }else{ +      document.getElementById('Ctable').style.display = 'none'; +      document.getElementById('Stable').style.display = 'none'; +    } +  } +</SCRIPT> + +<TABLE BGCOLOR="#cccccc" BORDER=0 WIDTH="100%"> +<TR><TD> +<TABLE BORDER=0 id="Ctable" style="display:<% $currentreasonclass eq 'C' ? 'inline' : 'none' %>"> +<% include('/elements/tr-select-reason.html', +             'field'          => 'creason', +             'reason_class'   => 'C', +             'curr_value'     => $creason, +             'init_type'      => $newcreasonT, +             'init_newreason' => $newcreason +          ) +%> +</TABLE> +</TR></TD> +</TABLE> + +<TABLE BGCOLOR="#cccccc" BORDER=0 WIDTH="100%"> +<TR><TD> +<TABLE BORDER=0 id="Stable" style="display:<% $currentreasonclass eq 'S' ? 'inline' : 'none' %>"> +<% include('/elements/tr-select-reason.html', +             'field'          => 'sreason', +             'reason_class'   => 'S', +             'curr_value'     => $sreason, +             'init_type'      => $newsreasonT, +             'init_newreason' => $newsreason +          ) +%> +</TABLE> +</TR></TD> +</TABLE> +     +% +%print qq!<INPUT TYPE="submit" VALUE="!, +%      $hashref->{eventpart} ? "Apply changes" : "Add invoice event", +%      qq!">!; +% + + +    </FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +if ( $cgi->param('eventpart') && $cgi->param('eventpart') =~ /^(\d+)$/ ) { +  $cgi->param('eventpart', $1); +} else { +  $cgi->param('eventpart', ''); +} + +my ($creason, $newcreasonT, $newcreason); +my ($sreason, $newsreasonT, $newsreason); + +my ($query) = $cgi->keywords; +my $action = ''; +my $part_bill_event = ''; +my $currentreasonclass = ''; +if ( $cgi->param('error') ) { +  $part_bill_event = new FS::part_bill_event ( { +    map { $_, scalar($cgi->param($_)) } fields('part_bill_event') +  } ); +} +if ( $query && $query =~ /^(\d+)$/ ) { +  $part_bill_event ||= qsearchs('part_bill_event',{'eventpart'=>$1}); +} else { +  $part_bill_event ||= new FS::part_bill_event {}; +} +$action ||= $part_bill_event->eventpart ? 'Edit' : 'Add'; +my $hashref = $part_bill_event->hashref; + +</%init> diff --git a/httemplate/edit/part_device.html b/httemplate/edit/part_device.html new file mode 100644 index 000000000..4f2fe93b4 --- /dev/null +++ b/httemplate/edit/part_device.html @@ -0,0 +1,16 @@ +<% include( 'elements/edit.html', +                 'name'   => 'Phone device type', +                 'table'  => 'part_device', +                 'labels' => {  +                               'devicepart' => 'Part number', +                               'devicename' => 'Device name', +                             }, +                 'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/part_event.html b/httemplate/edit/part_event.html new file mode 100644 index 000000000..6a532223e --- /dev/null +++ b/httemplate/edit/part_event.html @@ -0,0 +1,679 @@ +<% include( 'elements/edit.html', +              'name'   => 'Billing event definition', +              'table'  => 'part_event', +              'fields' => [ +                            'event', +                            { field   => 'eventtable', +                              type    => 'select', +                              options => [ FS::part_event->eventtables ], +                              labels  => $eventtable_labels, +                              onchange => 'eventtable_changed', +                            }, +                            { field   => 'agentnum', +                              type    => 'select-agent', +                              disable_empty => $disable_empty_agent, +                            }, +                            { field   => 'check_freq', +                              type    => 'select', +                              options => [ '1d', '1m' ], +                              labels  => $check_freq_labels, +                            }, +                            { field   => 'disabled', +                              type    => 'checkbox', +                              value   => 'Y', +                            }, +                            { type    => 'title', +                              value   => 'Event Conditions', +                            }, +                            { field   => 'conditionname', +                              type    => 'selectlayers', +                              options => [ keys %all_conditions ], +                              labels  => \%condition_labels, +                              onchange => 'condition_changed(what);', +                              layer_fields => \%condition_fields, +                              layer_values_callback => $condition_layer_values, +                              html_between       => n_a('action'), +                              m2name_table       => 'part_event_condition', +                              m2name_namecol     => 'conditionname', +                              m2_label           => 'Condition', +                              m2_new_default     => \@implicit_condition_objs, +                              m2_error_callback  => $condition_error_callback, +                              m2_remove_warnings => \%condition_remove_warnings, +                              m2_new_js          => 'condition_repop', +                              m2_remove_js       => 'condition_add', +                            }, +                            { type    => 'title', +                              value   => 'Event Action', +                            }, +                            { field   => 'action', +                              type     => 'selectlayers', +                              options  => [ keys %all_actions ], +                              labels   => \%action_labels, +                              onchange => 'action_changed(what);', +                              layer_fields => \%action_fields, +                              layer_values_callback => $action_layer_values, +                              html_between => n_a('action'), +                            }, + +                          ], +              'labels' => { +                            'eventpart'  => 'Event', +                            'event'      => 'Event name', +                            'eventtable' => 'Type', +                            'agentnum'   => 'Agent', +                            'check_freq' => 'Check frequency', +                            'disabled'   => 'Disable event', + +                            'conditionname' => 'Add new condition', +                            #'weight', +                            'action'     => 'Action', +                          }, +              'viewall_dir' => 'browse', +              'new_callback' => sub { #start empty for new events only +                my( $cgi, $object, $fields_listref ) = @_;  +                unshift @{ $fields_listref->[1]{'options'} }, ''; +              }, +              'error_callback' => $error_callback, + +              'agent_virt'       => 1, +              'agent_null_right' => 'Edit global billing events', +          ) +%> +<SCRIPT TYPE="text/javascript"> + +  window.onload = function () { eventtable_changed(document.getElementById('eventtable')) }; +  var notonload = 0; + +  function eventtable_changed(what) { + +%   if ( $JS_DEBUG ) { +      alert('eventtable_changed called on ' + what ); +%   } + +    var eventtable = what.options[what.selectedIndex].value; +%   if ( $JS_DEBUG ) { +      alert ("eventtable: " + eventtable); +%   } +    var eventdesc  = what.options[what.selectedIndex].text; + +    //remove the ** Select type ** +    if ( what.options[0].value == '' && notonload++ > 0 ) { +      what.options[0] = null; +    } + +    //// +    // XXX gray out conditions that can't apply (in addition to the warning)? +    //// + +    //// +    // update condition selects +    //// + +    for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { +      var cond_id = 'conditionname' + cnum; +      var cond_select = document.getElementById(cond_id); + +%     if ( $JS_DEBUG ) { +        alert('updating ' + cond_id); +%     } + +      // save off the current value +      var conditionname = cond_select.options[cond_select.selectedIndex].value; +      var cond_desc     = cond_select.options[cond_select.selectedIndex].text; + +      var seen_condition = condition_repop(cond_select); + +      var warning = document.getElementById(cond_id + '_warning'); +%     if ( $JS_DEBUG ) { +        alert('turning off warning; setting style.display of '+ cond_id + +              '_warning (' + warning + ') to none'); +%     } +      warning.style.display = 'none'; + +      if ( ! seen_condition && conditionname != '' ) { +        // add the current (not valid) condition back +        opt(cond_select, conditionname, cond_desc, true ); +        if ( ! condition_is_implicit(conditionname) ) { +          cond_select.parentNode.parentNode.style.display = ''; +          cond_select.disabled = ''; +          // turn on a warning and gray out the condition row +%         if ( $JS_DEBUG ) { +            alert('turning on warning; setting style.display of '+ cond_id + +                  '_warning (' + warning + ') to ""'); +%         } +          warning.innerHTML = 'Not applicable to ' + eventdesc + ' events'; +          warning.style.display = ''; +        } else { +          if ( ! condition_in_eventtable(conditionname) ) { +%           if ( $JS_DEBUG ) { +              alert(conditionname + " not in " + eventtable + "; disabling"); +%           } +            cond_select.parentNode.parentNode.style.display = 'none'; +            cond_select.disabled = 'disabled'; +          } else { +%           if ( $JS_DEBUG ) { +              alert(conditionname + " implicit for " + eventtable + "; enabling"); +%           } +            cond_select.parentNode.parentNode.style.display = ''; +            cond_select.disabled = ''; +          } +        } +      } + +    } + + +    //// +    // update action select +    //// + +    // save off the current value first!! +    var action = what.form.action.options[what.form.action.selectedIndex].value; +    var a_desc = what.form.action.options[what.form.action.selectedIndex].text; +    var seen_action = false; + +    // blank the current action select +    for ( var i = what.form.action.length; i >= 0; i-- ) +      what.form.action.options[i] = null; + +    if ( action == '' ) { +      opt(what.form.action, action, a_desc, true ); +    } + +    // repopulate it +%   foreach my $eventtable ( FS::part_event->eventtables ) { +%     tie my %actions, 'Tie::IxHash', FS::part_event->actions($eventtable); +%     #use Data::Dumper; warn Dumper(%actions); + +      if ( eventtable == '<% $eventtable %>' ) { + +%       foreach my $action ( keys %actions ) { +%         ( my $description = $actions{$action}->{'description'} ) =~ s/'/\\'/g; + +          var sel = false; +          if ( action == '<% $action %>' ) { +            seen_action = true; +            sel = true; +          } +          opt( what.form.action, '<% $action %>', '<% $description %>', sel ); +%       } + +      } + +%   } + +    // by default, turn off warnings and enable the submit button +    var warning = document.getElementById('action_warning'); +    warning.style.display = 'none'; +    var submit_button = document.getElementById('submit'); +    submit_button.disabled = ''; + +    if ( ! seen_action && action != '' ) { +      // add the current (not valid) action back +      opt( what.form.action, action, a_desc, true ); +      // turn on a warning and disable the submit button +      //warning.innerHTML = a_desc + ' event not available as a ' + +      warning.innerHTML = 'Not available as a ' + eventdesc + ' action'; +      warning.style.display = ''; +      submit_button.disabled = 'disabled'; +    } + +  } + +  function opt(what,value,text,selected) { +    var optionName = new Option(text, value, false, selected); +    var length = what.length; +    what.options[length] = optionName; +  } + +  function action_changed(what) { +    // remove '** Select new **' +    if ( what.options[0].value == '' ) { +       what.options[0] = null; +    } +    // remove the warning, remove the invalid action, enable the submit button +    var warning = document.getElementById('action_warning'); +    if ( warning.style.display == '' ) { +      warning.style.display = 'none'; +      what.options[what.length-1] = null; +      document.getElementById('submit').disabled = ''; +    } +  } + +  function condition_changed(what) { +    // remove '** Select new **' +    if ( what.options[0].value == '' ) { +       what.options[0] = null; +    } + +    var previousValue = what.getAttribute('previousValue'); +    var previousText = what.getAttribute('previousText'); +    var value = what.options[what.selectedIndex].value; +    var text = what.options[what.selectedIndex].text; + +%   foreach my $value ( keys %condition_remove_warnings ) { +      if ( previousValue == '<% $value %>' ) { +        if ( !confirm( <% $condition_remove_warnings{$value} |js_string %> ) ) { +          for ( var i=0; i < what.length; i++ ) { +            if ( what.options[i].value == previousValue ) { +              what.selectedIndex = i; +            } +          } +          return false; +        } +      } +%   } + +    //alert(previous + ' changed to ' + value); + +    var field_regex = /(\d+)$/; +    var match = field_regex.exec(what.name); +    if ( !match ) { +      alert(what.name + " didn't match?!"); +      return; +    } + +    //add the previous condition *back* to all the other selects... +    condition_add(previousValue, previousText, match[1]); + +    what.setAttribute('previousValue', value); +    what.setAttribute('previousText', text); + +    // remove the new condition from all other selects +    condition_remove(value, match[1]); + +  } + +  function condition_avail(check_cond, curnum) { +    for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { +      if ( cnum == curnum ) continue; + +      var cond_id = 'conditionname' + cnum; +      var cond_select = document.getElementById(cond_id); + +      //alert("checking " + cond_id + " (" + cond_select.disabled + ")"); + +      if ( cond_select.disabled ) continue; + +      // the current value +      var conditionname = cond_select.options[cond_select.selectedIndex].value; + +      if ( check_cond == conditionname ) return false; + +    } + +    return true; + +  } + +  function condition_remove(remove_cond, curnum) { + +    if ( remove_cond.length == 0 ) return; + +    for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { +      if ( cnum == curnum ) continue; + +      var cond_id = 'conditionname' + cnum; +      var cond_select = document.getElementById(cond_id); + +      //for ( var i = cond_select.length; i >= 0; i-- ) { +      for ( var i=0; i < cond_select.length; i++ ) { +        if ( cond_select.options[i].value == remove_cond ) { +          cond_select.options[i] = null; +        } +      } + +    } + +  } + +  function condition_add(add_condname, add_conddesc, curnum) { + +    if ( add_condname.length == 0 ) return; + +    var in_eventtable = condition_in_eventtable(add_condname); + +    if ( ! in_eventtable ) return; + +    for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) { +      if ( cnum == curnum ) continue; + +      var cond_id = 'conditionname' + cnum; +      var cond_select = document.getElementById(cond_id); + +      if ( cond_select.disabled ) continue; + +      //alert("adding " + add_condname + " to " + cond_id); + +      opt(cond_select, add_condname, add_conddesc, false ); + +      cond_select.parentNode.parentNode.style.display = ''; + +    } + +  } + +  function condition_in_eventtable(condname) { + +    var eventtable_el = document.getElementById('eventtable'); +    var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value; + +    var in_eventtable = false; + +%   foreach my $eventtable ( FS::part_event->eventtables ) { +%     tie my %conditions, 'Tie::IxHash', +%       FS::part_event_condition->conditions($eventtable); + +      if ( eventtable == '<% $eventtable %>' ) { + +%       foreach my $conditionname ( keys %conditions ) { + +          if ( condname == '<% $conditionname %>' ) { +            in_eventtable = true; +          } + +%       } + +      } + +%   } + +    return in_eventtable; + +  } + +  function condition_is_implicit(condname) { + +    if ( true <% @implicit_conditions +                   ? ( ' && '. join(' && ', map { "condname != '$_'" } +                                                @implicit_conditions +                                   ) +                     ) +                   : '' +              %> ) { +      return false; +    } else { +      return true; +    } +  } + +  function condition_repop(cond_select) { + +    var eventtable_el = document.getElementById('eventtable'); +    var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value; + +    // save off the current value +    var conditionname = cond_select.options[cond_select.selectedIndex].value; +    var cond_desc     = cond_select.options[cond_select.selectedIndex].text; +    var seen_condition = false; + +    //skip deleted conditions +    if ( cond_select.disabled && conditionname != '' && ! condition_is_implicit(conditionname) ) { +      return false; +    } + +    var field_regex = /(\d+)$/; +    var match = field_regex.exec(cond_select.name); +    if ( !match ) { +      alert(what.name + " didn't match?!"); +      return; +    } +    var cnum = match[1]; + +    // blank the current condition select +    for ( var i = cond_select.length; i >= 0; i-- ) +      cond_select.options[i] = null; + +    if ( conditionname == '' ) { +      opt(cond_select, conditionname, cond_desc, true ); +    } + +    // repopulate it +%   foreach my $eventtable ( FS::part_event->eventtables ) { +%     tie my %conditions, 'Tie::IxHash', +%       FS::part_event_condition->conditions($eventtable); + +      if ( eventtable == '<% $eventtable %>' ) { + +%       foreach my $conditionname ( keys %conditions ) { +%         my $description = $conditions{$conditionname}->{'description'}; +%         $description =~ s/'/\\'/g; + +          var sel = false; +          if ( conditionname == '<% $conditionname %>' ) { +            seen_condition = true; +            sel = true; +          } + +          if ( condition_avail("<% $conditionname %>", cnum) ) { +            opt(cond_select, '<% $conditionname %>', '<% $description %>', sel); +          } + +%       } + +      } +         +%   } + +    if ( cond_select.length > 1 || cond_select.length == 1 && cond_select.options[0].value.length > 0 ) { +        +      cond_select.parentNode.parentNode.style.display = ''; +      cond_select.disabled = ''; + +    } else { +      cond_select.parentNode.parentNode.style.display = 'none'; +      cond_select.disabled = 'disabled'; +    } + +    return seen_condition; + +  } + +</SCRIPT> +<%once> + +#misc (eventtable, check_freq) + +my $eventtable_labels = FS::part_event->eventtable_labels; +$eventtable_labels->{''} = '** Select type **'; + +my $check_freq_labels = FS::part_event->check_freq_labels; + +#conditions + +tie my %all_conditions, 'Tie::IxHash',  +  '' => { 'description' => '*** Select new condition ***', }, +  FS::part_event_condition->conditions(); + +my %condition_labels = map { $_ => $all_conditions{$_}->{'description'} } +                           keys %all_conditions; + +#my %condition_fields = map { $_ => $all_conditions{$_}->{option_fields} }  +#                           keys %all_conditions; +my %condition_fields = map { my $c = $_; +                             tie my %opts, 'Tie::IxHash', +                               @{ $all_conditions{$c}->{'option_fields'} || []}; +                             %opts = ( map { ( "$c.$_" => $opts{$_} ); } +                                           keys %opts +                                     ); +                             ( $c => [ %opts ] ); +                           }  +                           keys %all_conditions; + +my @implicit_conditions = sort { $all_conditions{$a}->{'implicit_flag'} <=> +                                 $all_conditions{$b}->{'implicit_flag'} +                               } +                          grep { $all_conditions{$_}->{'implicit_flag'} } +                          keys %all_conditions; + +my @implicit_condition_objs = map { +                                    new FS::part_event_condition { +                                      'conditionname' => $_, +                                    }; +                                  } +                                  @implicit_conditions; + +my %condition_remove_warnings = +  map  { ( $_ => $all_conditions{$_}->{'remove_warning'} ); } +  grep { $all_conditions{$_}->{'remove_warning'} } +  keys %all_conditions; + +#actions + +tie my %all_actions, 'Tie::IxHash',  +  '' => { 'description' => '*** Select event action ***', }, +  FS::part_event->actions(); + +my %action_labels = map { $_ => $all_actions{$_}->{'description'} } +                        keys %all_actions; + +#my %action_fields = map { $_ => $all_actions{$_}->{option_fields} } +#                        keys %all_actions; +my %action_fields = map { my $action = $_; +                          tie my %opts, 'Tie::IxHash', +                            @{ $all_actions{$action}->{option_fields} || [] }; +                          %opts = ( map { ( "$action.$_" => $opts{$_} ); } +                                        keys %opts +                                  ); +                          ( $action => [ %opts ] ); +                        } +                        keys %all_actions; + +#subs + +sub n_a { +  my $t = shift; + +  return sub { +    my $field = shift; +    qq( <FONT ID="${field}_warning" STYLE="display:none" COLOR="#FF0000">). +      "Party Party Join us Join us". +      '</FONT>'; +  }; +} + +my $action_layer_values = sub { +  my( $cgi, $part_event ) = @_; +  my $action = $cgi->param('action') || $part_event->action; +  return {} unless $action; +  scalar( #force hashref +    { +      #map { $_ => { $part_event->options } } +      #    keys %action_fields +      map { my $action = $_; +            my %fields = @{ $action_fields{$action} }; +            my %obj_opts = $part_event->options; +            %obj_opts = map { ( "$action.$_" => $obj_opts{$_} ); } +                            keys %obj_opts; +            my %opts = +              map { #false laziness w/process/part_event.html +                    my $option = $_; +                    my $value = scalar($cgi->param($_)) || $obj_opts{$_}; + +                    if ( $option =~ /^(.*)\.reasonnum$/ && $value == -1 ) { +                      $value = { +                        'typenum' => scalar( $cgi->param( "new${option}T" ) ), +                        'reason'  => scalar( $cgi->param( "new${option}"  ) ), +                      }; +                    } + +                    ( $option => $value ); + +                  } +                  keys %fields; +            ( $action => \%opts ); +          } +          keys %action_fields +    } +  ); +}; + +tie my %cgi_conditions, 'Tie::IxHash'; + +my $error_callback = sub { +  my( $cgi, $object, $fields_listref ) = @_; + +  my @cond_params = grep /^conditionname\d+$/, $cgi->param; + +  %cgi_conditions = map { +    my $param = $_; +    my $conditionname = $cgi->param($param); +    $conditionname => { +      map {  + +        my $cgi_key = $_; +        $cgi_key =~ /^$param\.$conditionname\.(.*)$/ or die 'wtf!'; +        my $key = $1; +        #my $value = $cgi->param($_); + +        #my $info = $all_conditions->{$conditionname} +        my %cond_opts = +          @{ $all_conditions{$conditionname}->{'option_fields'} || []}; +        my $info = $cond_opts{$key}; + +        my $value; +        #false laziness w/process/part_event.html +        if (      $info->{'type'} =~ /^(select|checkbox)-?multiple$/ +               or $info->{'type'} =~ /^select/ && $info->{'multiple'} ) { +          $value = { map { $_ => 1 } $cgi->param($cgi_key) }; +        } elsif ( $info->{'type'} eq 'freq' ) { +          $value = $cgi->param($cgi_key). $cgi->param($cgi_key.'_units'); +        } else { +          $value = $cgi->param($cgi_key); +        } + +        $key => $value; + +      } grep /^$param\.$conditionname\./, $cgi->param +    }; +  } grep $cgi->param($_), grep /^conditionname\d+$/, $cgi->param; + +}; + +my $condition_error_callback = sub { +  map { +    new FS::part_event_condition { 'conditionname' => $_, }; +  } keys %cgi_conditions; +}; + +my $condition_layer_values = sub { +  #m2_table option causes this to be +  # part_event_condition instead of part_event +  my ( $cgi, $part_event_condition, $switches ) = @_; +  scalar( #force hashref +    { +      #map { $_ => { $part_event_condition->options } } +      #    keys %condition_fields +      map { my $conditionname = $_; +            my %opts = $switches->{'mode'} eq 'error' +                       ? %{ $cgi_conditions{$conditionname} || {} } +                       : $part_event_condition->options; +            %opts = ( +              map { ( "$conditionname.$_" => $opts{$_} ); } +                  keys %opts +            ); +            ( $conditionname => \%opts ); +          } +          keys %condition_fields +    } +  ); +}; + + +</%once> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Edit billing events') +      || $curuser->access_right('Edit global billing events'); + +my $disable_empty_agent= ! $curuser->access_right('Edit global billing events'); + +%cgi_conditions = (); +my $use_cgi_conditions = 0; + +my $JS_DEBUG = 0; + +</%init> diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi new file mode 100644 index 000000000..8b697e142 --- /dev/null +++ b/httemplate/edit/part_export.cgi @@ -0,0 +1,138 @@ +<% include('/elements/header.html', "$action Export", '', ' onLoad="visualize()"') %> + +<% include('/elements/error.html') %> + +<FORM NAME="dummy"> +<INPUT TYPE="hidden" NAME="exportnum" VALUE="<% $part_export->exportnum %>"> + +<% ntable("#cccccc",2) %> +<TR> +  <TD ALIGN="right">Export host</TD> +  <TD> +    <INPUT TYPE="text" NAME="machine" VALUE="<% $part_export->machine %>"> +  </TD> +</TR> +<TR> +  <TD ALIGN="right">Export</TD> +  <TD><% $widget->html %> + +<% include('/elements/footer.html') %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +#if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) { +#  $cgi->param('clone', $1); +#} else { +#  $cgi->param('clone', ''); +#} + +my($query) = $cgi->keywords; +my $action = ''; +my $part_export = ''; +if ( $cgi->param('error') ) { +  $part_export = new FS::part_export ( { +    map { $_, scalar($cgi->param($_)) } fields('part_export') +  } ); +} elsif ( $query =~ /^(\d+)$/ ) { +  $part_export = qsearchs('part_export', { 'exportnum' => $1 } ); +} else { +  $part_export = new FS::part_export; +} +$action ||= $part_export->exportnum ? 'Edit' : 'Add'; + +#my $exports = FS::part_export::export_info($svcdb); +my $exports = FS::part_export::export_info(); + +my %layers = map { $_ => "$_ - ". $exports->{$_}{desc} } keys %$exports; +$layers{''}=''; + +my $widget = new HTML::Widgets::SelectLayers( +  'selected_layer' => $part_export->exporttype, +  'options'        => \%layers, +  'form_name'      => 'dummy', +  'form_action'    => 'process/part_export.cgi', +  'form_text'      => [qw( exportnum machine )], +#  'form_checkbox'  => [qw()], +  'html_between'    => "</TD></TR></TABLE>\n", +  'layer_callback'  => sub { +    my $layer = shift; +    my $html = qq!<INPUT TYPE="hidden" NAME="exporttype" VALUE="$layer">!. +               ntable("#cccccc",2); + +    $html .= '<TR><TD ALIGN="right">Description</TD><TD BGCOLOR=#ffffff>'. +             $exports->{$layer}{notes}. '</TD></TR>' +      if $layer; + +    foreach my $option ( keys %{$exports->{$layer}{options}} ) { +      my $optinfo = $exports->{$layer}{options}{$option}; +      die "Retreived non-ref export info option from $layer export: $optinfo" +        unless ref($optinfo); +      my $label = $optinfo->{label}; +      my $type = defined($optinfo->{type}) ? $optinfo->{type} : 'text'; +      my $value = $cgi->param($option) +                 || ( $part_export->exportnum && $part_export->option($option) ) +                 || ( (exists $optinfo->{default} && !$part_export->exportnum) +                      ? $optinfo->{default} +                      : '' +                    ); +      $html .= qq!<TR><TD ALIGN="right">$label</TD><TD>!; +      if ( $type eq 'select' ) { +        my $size = defined($optinfo->{size}) ? " SIZE=" . $optinfo->{size} : ''; +        my $multi = defined($optinfo->{multi}) ? ' MULTIPLE' : ''; +        $html .= qq!<SELECT NAME="$option"$multi$size>!; +        my @values = split '\s+', $value if $multi; +        my @options; +        if (defined($optinfo->{option_values})) { +          my $valsub = $optinfo->{option_values}; +          @options = &$valsub(); +        } elsif (defined($optinfo->{options})) { +          @options = @{$optinfo->{options}}; +        } +        foreach my $select_option ( @options ) { +          #if ( ref($select_option) ) { +          #} else { +            my $selected = ($multi ? grep {$_ eq $select_option} @values : $select_option eq $value ) ? ' SELECTED' : ''; +            my $label = $select_option; +            if (defined($optinfo->{option_label})) { +              my $labelsub = $optinfo->{option_label}; +              $label = &$labelsub($select_option); +            } +            $html .= qq!<OPTION VALUE="$select_option"$selected>!. +                     qq!$label</OPTION>!; +          #} +        } +        $html .= '</SELECT>'; +      } elsif ( $type eq 'textarea' ) { +        $html .= qq!<TEXTAREA NAME="$option" COLS=80 ROWS=8 WRAP="virtual">!. +                 encode_entities($value). '</TEXTAREA>'; +      } elsif ( $type eq 'text' ) { +        $html .= qq!<INPUT TYPE="text" NAME="$option" VALUE="!. +                 encode_entities($value). '" SIZE=64>'; +      } elsif ( $type eq 'checkbox' ) { +        $html .= qq!<INPUT TYPE="checkbox" NAME="$option" VALUE="1"!; +        $html .= ' CHECKED' if $value; +        $html .= '>'; +      } else { +        $html .= "unknown type $type"; +      } +      $html .= '</TD></TR>'; +    } +    $html .= '</TABLE>'; + +    $html .= '<INPUT TYPE="hidden" NAME="options" VALUE="'. +             join(',', keys %{$exports->{$layer}{options}} ). '">'; + +    $html .= '<INPUT TYPE="hidden" NAME="nodomain" VALUE="'. +             $exports->{$layer}{nodomain}. '">'; + +    $html .= '<INPUT TYPE="submit" VALUE="'. +             ( $part_export->exportnum ? "Apply changes" : "Add export" ). +             '">'; + +    $html; +  }, +); + +</%init> diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi new file mode 100755 index 000000000..f2c7448d5 --- /dev/null +++ b/httemplate/edit/part_pkg.cgi @@ -0,0 +1,716 @@ +<% include( 'elements/edit.html', +              'post_url'              => popurl(1).'process/part_pkg.cgi', +              'name'                  => "Package definition", +              'table'                 => 'part_pkg', + +              'agent_virt'            => 1, +              'agent_null_right'      => $edit_global, +              'agent_clone_extra_sql' => $agent_clone_extra_sql, +              #'viewall_dir'           => 'browse', +              'viewall_url'           => $p.'browse/part_pkg.cgi', +              'html_init'             => include('/elements/init_overlib.html'). +                                         $javascript, +              'html_bottom'           => $html_bottom, +              'body_etc'              => +                'onLoad="agent_changed(document.edit_topform.agentnum)"', + +              'begin_callback'        => $begin_callback, +              'end_callback'          => $end_callback, +              'new_hashref_callback'  => $new_hashref_callback, +              'new_object_callback'   => $new_object_callback, +              'new_callback'          => $new_callback, +              'clone_callback'        => $clone_callback, +              'edit_callback'         => $edit_callback, +              'error_callback'        => $error_callback, +              'field_callback'        => $field_callback, + +              'labels' => {  +                            'pkgpart'          => 'Package Definition', +                            'pkg'              => 'Package (customer-visible)', +                            'comment'          => 'Comment (customer-hidden)', +                            'classnum'         => 'Package class', +                            'addon_classnum'   => 'Restrict additional orders to package class', +                            'promo_code'       => 'Promotional code', +                            'freq'             => 'Recurring fee frequency', +                            'setuptax'         => 'Setup fee tax exempt', +                            'recurtax'         => 'Recurring fee tax exempt', +                            'taxclass'         => 'Tax class', +                            'taxproduct_select'=> 'Tax products', +                            'plan'             => 'Price plan', +                            'disabled'         => 'Disable new orders', +                            'setup_cost'       => 'Setup cost', +                            'recur_cost'       => 'Recur cost', +                            'pay_weight'       => 'Payment weight', +                            'credit_weight'    => 'Credit weight', +                            'agentnum'         => 'Agent', +                            'setup_fee'        => 'Setup fee', +                            'recur_fee'        => 'Recurring fee', +                            'bill_dst_pkgpart' => 'Include line item(s) from package', +                            'svc_dst_pkgpart'  => 'Include services of package', +                            'report_option'    => 'Report classes', +                          }, + +              'fields' => [ +                            { field=>'clone',  type=>'hidden', +                              curr_value_callback => +                                sub { shift->param('clone') }, +                            }, +                            { field=>'pkgnum', type=>'hidden', +                              curr_value_callback => +                                sub { shift->param('pkgnum') }, +                            }, + +                            { field=>'custom',  type=>'hidden' }, + +                            { type => 'columnstart' }, +                             +                              { field     => 'pkg', +                                type      => 'text', +                                size      => 40, #32 +                                maxlength => 50, +                              }, +                              {field=>'comment',  type=>'text', size=>40 }, #32 +                              { field         => 'agentnum', +                                type          => 'select-agent', +                                disable_empty => ! $acl_edit_global, +                                empty_label   => '(global)', +                                onchange      => 'agent_changed', +                              }, +                              {field=>'classnum', type=>'select-pkg_class' }, +                              ( $conf->exists('pkg-addon_classnum') +                                  ? ( { field=>'addon_classnum', +                                        type =>'select-pkg_class', +                                      } +                                    ) +                                   : () +                              ), +                              {field=>'disabled', type=>$disabled_type, value=>'Y'}, + +                              { type  => 'tablebreak-tr-title', +                                value => 'Pricing', #better name? +                              }, +                              { field => 'plan', +                                type  => 'selectlayers-select', +                                options => [ keys %plan_labels ], +                                labels  => \%plan_labels, +                              }, +                              { field => 'setup_fee', +                                type  => 'money', +                              }, +                              { field    => 'freq', +                                type     => 'part_pkg_freq', +                                onchange => 'freq_changed', +                              }, +                              { field    => 'recur_fee', +                                type     => 'money', +                                disabled => sub { $recur_disabled }, +                              }, +                                 +                              #price plan +                              #setup fee +                              #recurring frequency +                              #recurring fee (auto-disable) + +                            { type => 'columnnext' }, + +                              {type=>'justtitle', value=>'Taxation' }, +                              {field=>'setuptax', type=>'checkbox', value=>'Y'}, +                              {field=>'recurtax', type=>'checkbox', value=>'Y'}, +                              {field=>'taxclass', type=>'select-taxclass' }, +                              { field => 'taxproductnums', +                                type  => 'hidden', +                                value => join(',', @taxproductnums), +                              }, +                              { field => 'taxproduct_select', +                                type  => 'selectlayers', +                                options => [ '(default)', @taxproductnums ], +                                curr_value => '(default)', +                                labels  => { ( '(default)' => '(default)' ), +                                             map {($_=>$usage_class{$_})} +                                             @taxproductnums +                                           }, +                                layer_fields => \%taxproduct_fields, +                                layer_values_callback => $taxproduct_values, +                                layers_only  =>   !$taxproducts, +                                cell_style   => ( !$taxproducts +                                                  ? 'display:none' +                                                  : '' +                                                ), +                              }, + +                              { type  => 'tablebreak-tr-title', +                                value => 'Promotions', #better name? +                              }, +                              { field=>'promo_code', type=>'text', size=>15 }, + +                              { type  => 'tablebreak-tr-title', +                                value => 'Cost tracking', #better name? +                              }, +                              { field=>'setup_cost', type=>'money', }, +                              { field=>'recur_cost', type=>'money', }, + +                            { type => 'columnnext' }, + +                              { field    => 'agent_type', +                                type     => 'select-agent_types', +                                disabled => ! $acl_edit_global, +                                curr_value_callback => sub { +                                  my($cgi, $object, $field) = @_; +                                  #in the other callbacks..?  hmm. +                                  \@agent_type; +                                }, +                              }, + +                              { type  => 'tablebreak-tr-title', +                                value => 'Line-item revenue recogition', #better name? +                              }, +                              { field=>'pay_weight',    type=>'text', size=>6 }, +                              { field=>'credit_weight', type=>'text', size=>6 }, + + +                            { type => 'columnend' }, + +                            { 'type'  => $census ? 'tablebreak-tr-title' +                                                 : 'hidden', +                              'value' => 'Optional report classes', +                              'field' => 'census_title', +                            }, +                            { 'field'    => 'report_option', +                              'type'     => $census ? 'select-table' : 'hidden', +                              'table'    => 'part_pkg_report_option', +                              'name_col' => 'name', +                              'multiple' => 1, +                            }, + + +                            { 'type'    => 'tablebreak-tr-title', +                              'value'   => 'Pricing add-ons', +                              'colspan' => 4, +                            }, +                            { 'field'      => 'bill_dst_pkgpart', +                              'type'       => 'select-part_pkg', +                              'm2_label'   => 'Include line item(s) from package', +                              'm2m_method' => 'bill_part_pkg_link', +                              'm2m_dstcol' => 'dst_pkgpart', +                              'm2_error_callback' => +                                &{$m2_error_callback_maker}('bill'), +                              'm2_fields' => [ { 'field' => 'hidden', +                                                 'type'  => 'checkbox', +                                                 'value' => 'Y', +                                                 'curr_value' => '', +                                                 'label' => 'Bundle', +                                               }, +                                             ], +                            }, + +                            { type  => 'tablebreak-tr-title', +                              value => 'Services', +                            }, +                            { type => 'pkg_svc', }, + +                            { 'field'      => 'svc_dst_pkgpart', +                              'label'      => 'Also include services from package: ', +                              'type'       => 'select-part_pkg', +                              'm2_label'   => 'Include services of package: ', +                              'm2m_method' => 'svc_part_pkg_link', +                              'm2m_dstcol' => 'dst_pkgpart', +                              'm2_error_callback' => +                                &{$m2_error_callback_maker}('svc'), +                            }, + +                            { type  => 'tablebreak-tr-title', +                              value => 'Price plan options', +                            }, + +                          ], + +           ) +%> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $edit_global = 'Edit global package definitions'; +my $acl_edit        = $curuser->access_right('Edit package definitions'); +my $acl_edit_global = $curuser->access_right($edit_global); + +my $acl_edit_either = $acl_edit || $acl_edit_global; + +my $begin_callback = sub { +  my( $cgi, $fields, $opt ) = @_; +  die "access denied" +    unless $acl_edit_either +        || ( $cgi->param('pkgnum') +             && $curuser->access_right('Customize customer package') +           ); +}; + +my $disabled_type = $acl_edit_either ? 'checkbox' : 'hidden'; + +#arg.  access rights for cloning are Hard. +# on the one hand we don't really want cloning (customizing a package) to fail  +#  for want of finding the source package in normal usage +# on the other hand, we don't want people using the clone link to be able to +#  see  +my $agent_clone_extra_sql =  +  ' ( '. FS::part_pkg->curuser_pkgs_sql. +  "   OR ( part_pkg.custom = 'Y' ) ". +  ' ) '; + +my $conf = new FS::Conf; +my $taxproducts = $conf->exists('enable_taxproducts'); + +my $sth = dbh->prepare("SELECT COUNT(*) FROM part_pkg_report_option". +                       "  WHERE disabled IS NULL OR disabled = ''  ") +  or die dbh->errstr; +$sth->execute or die $sth->errstr; +my $census = $sth->fetchrow_arrayref->[0]; + +#XXX +# - tr-part_pkg_freq: month_increments_only (from price plans) +# - test cloning +# - test errors cloning +# - test custom pricing +# - move the selectlayer divs away from lame layer_callback + +#my ($query) = $cgi->keywords; +# +#my $part_pkg = ''; + +my @agent_type = (); +my %tax_override = (); + +my %taxproductnums = map { ($_->classnum => 1) } +                     qsearch('usage_class', { 'disabled' => '' }); +my @taxproductnums = ( qw( setup recur ), sort (keys %taxproductnums) ); + +my %options = (); +my $recur_disabled = 1; + +my $error_callback = sub { +  my($cgi, $object, $fields, $opt ) = @_; + +  (@agent_type) = $cgi->param('agent_type'); + +  $opt->{action} = 'Custom' if $cgi->param('pkgnum'); + +  $recur_disabled = $cgi->param('freq') ? 0 : 1; + +  foreach ($cgi->param) { +    /^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1); +  } +  $tax_override{''} = $cgi->param('tax_override'); +  $tax_override{$_} = $cgi->param('tax_override_$_') +    foreach(grep { /^tax_override_(\w+)$/ } $cgi->param); + +  #some false laziness w/process +  $cgi->param('plan') =~ /^(\w+)$/ or die 'unparsable plan'; +  my $plan = $1; +  my $options = $cgi->param($plan."__OPTIONS"); +  my @options = split(',', $options); +  %options = +    map { my $optionname = $_; +          my $param = $plan."__$optionname"; +          my $value = join(', ', $cgi->param($param)); +          ( $optionname => $value ); +        } +        @options; + +  #$cgi->param($_, $options{$_}) foreach (qw( setup_fee recur_fee )); +  $object->set($_ => scalar($cgi->param($_)) ) +    foreach (qw( setup_fee recur_fee )); + +}; + +my $new_hashref_callback = sub { { 'plan' => 'flat' }; }; + +my $new_object_callback = sub { +  my( $cgi, $hashref, $fields, $opt ) = @_; + +  my $part_pkg = FS::part_pkg->new( $hashref ); +  $part_pkg->set($_ => '0') +    foreach (qw( setup_fee recur_fee )); + +  $part_pkg; + +}; + +my $edit_callback = sub { +  my( $cgi, $object, $fields, $opt ) = @_; + +  $recur_disabled = $object->freq ? 0 : 1; + +  (@agent_type) = +    map {$_->typenum} qsearch('type_pkgs', { 'pkgpart' => $object->pkgpart } ); + +  my @report_option = (); +  foreach ($object->options) { +    /^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1); +    /^report_option_(\d+)$/ && (push @report_option, $1); +  } +  foreach ($object->part_pkg_taxoverride) { +    $taxproductnums{$_->usage_class} = 1 +      if $_->usage_class; +  } + +  $cgi->param('report_option', join(',', @report_option)); +  foreach my $field ( @$fields ) { +    next unless (  +      ref($field) eq 'HASH' && +      $field->{field} && +      $field->{field} eq 'report_option' +    ); +    #$field->{curr_value} = join(',', @report_option); +    $field->{value} = join(',', @report_option); +  } + +  %options = $object->options; + +  $object->set($_ => $object->option($_)) +    foreach (qw( setup_fee recur_fee )); + +}; + +my $new_callback = sub { +  my( $cgi, $object, $fields ) = @_; + +  my $conf = new FS::Conf;  +  if ( $conf->exists('agent_defaultpkg') ) { +    #my @all_agent_types = map {$_->typenum} qsearch('agent_type',{}); +    @agent_type = map {$_->typenum} qsearch('agent_type',{}); +  } + +}; + +my $clone_callback = sub { +  my( $cgi, $object, $fields, $opt ) = @_; + +  if ( $cgi->param('pkgnum') ) { + +    my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cgi->param('pkgnum') } ); +    $object->agentnum( $cust_pkg->cust_main->agentnum ); + +    $opt->{action} = 'Custom'; + +    #my $part_pkg = $clone_part_pkg->clone; +    #this is all clone does anyway +    $object->custom('Y'); + +    $object->disabled('Y'); + +  } else { #not when cloning... + +    (@agent_type) = +      map {$_->typenum} qsearch('type_pkgs',{ 'pkgpart' => $object->pkgpart } ); + +  } + +  %options = $object->options; + +  $object->set($_ => $options{$_}) +    foreach (qw( setup_fee recur_fee )); + +  $recur_disabled = $object->freq ? 0 : 1; +}; + +my $m2_error_callback_maker = sub { +  my $link_type = shift; #yay closures +  return sub { +    my( $cgi, $object ) = @_; +    my $num; +    map { + +          if ( /^${link_type}_dst_pkgpart(\d+)$/ && +               ( my $dst = $cgi->param("${link_type}_dst_pkgpart$1") ) ) +          { + +            my $hidden = $cgi->param("${link_type}_dst_pkgpart__hidden$1") +                         || ''; +            new FS::part_pkg_link { +              'link_type'   => $link_type, +              'src_pkgpart' => $object->pkgpart, +              'dst_pkgpart' => $dst, +              'hidden'      => $hidden, +            }; +          } else { +            (); +          } +        } +    $cgi->param; +  }; +}; + +my $javascript = <<'END'; +  <SCRIPT TYPE="text/javascript"> + +    function freq_changed(what) { +      var freq = what.options[what.selectedIndex].value; + +      if ( freq == '0' ) { +        what.form.recur_fee.disabled = true; +        what.form.recur_fee.style.backgroundColor = '#dddddd'; +      } else { +        what.form.recur_fee.disabled = false; +        what.form.recur_fee.style.backgroundColor = '#ffffff'; +      } + +    } + +    function agent_changed(what) { + +      var agentnum = what.options[what.selectedIndex].value; + +      if ( agentnum == 0 ) { +        what.form.agent_type.disabled = false; +        //what.form.agent_type.style.backgroundColor = '#ffffff'; +        what.form.agent_type.style.visibility = ''; +      } else { +        what.form.agent_type.disabled = true; +        //what.form.agent_type.style.backgroundColor = '#dddddd'; +        what.form.agent_type.style.visibility = 'hidden'; +      } + +    } + +  </SCRIPT> +END + +tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() }; + +tie my %plan_labels, 'Tie::IxHash', +  map {  $_ => ( $plans{$_}->{'shortname'} || $plans{$_}->{'name'} ) } +      keys %plans; + +my $html_bottom = sub { +  my( $object ) = @_; + +  #warn join("\n", map { "$_: $options{$_}" } keys %options ). "\n"; + +  my $layer_callback = sub { +   +    my $layer = shift; +    my $html = ntable("#cccccc",2); +   +    #$html .= ' +    #  <TR> +    #    <TD ALIGN="right">Recurring fee frequency </TD> +    #    <TD><SELECT NAME="freq"> +    #'; +    # +    #my @freq = keys %freq; +    #@freq = grep { /^\d+$/ } @freq +  #XXX this bit#  #  if exists($plans{$layer}->{'freq'}) && $plans{$layer}->{'freq'} eq 'm'; +    #foreach my $freq ( @freq ) { +    #  $html .= qq(<OPTION VALUE="$freq"); +    #  $html .= ' SELECTED' if $freq eq $part_pkg->freq; +    #  $html .= ">$freq{$freq}"; +    #} +    #$html .= '</SELECT></TD></TR>'; +   +    my $href = $plans{$layer}->{'fields'}; +    my @fields = exists($plans{$layer}->{'fieldorder'}) +                   ? @{$plans{$layer}->{'fieldorder'}} +                   : keys %{ $href }; +   +    foreach my $field ( grep $_ !~ /^(setup|recur)_fee$/, @fields ) { +   +      $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_report_option.html b/httemplate/edit/part_pkg_report_option.html new file mode 100644 index 000000000..a6f8e57b7 --- /dev/null +++ b/httemplate/edit/part_pkg_report_option.html @@ -0,0 +1,23 @@ +<% include( 'elements/edit.html', +              'name'   => 'Package optional report class', +              'table'  => 'part_pkg_report_option', +              'fields' => [ +                            'name', +                            { field=>'num', type=>'hidden' }, +                            { field=>'disabled', type=>'checkbox', value=>'Y', }, +                          ], +              'labels' => {  +                            'num'      => 'Class number', +                            'name'     => 'Class name', +                            'disabled' => 'Disable class', +                          }, +              'viewall_dir' => 'browse', +           ) +           +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/part_pkg_taxclass.html b/httemplate/edit/part_pkg_taxclass.html new file mode 100644 index 000000000..ad030449f --- /dev/null +++ b/httemplate/edit/part_pkg_taxclass.html @@ -0,0 +1,23 @@ +<% include('elements/edit.html', +     'name_singular' => 'tax class', +     'table'         => 'part_pkg_taxclass', +     'labels'        => { +                          'taxclassnum' => 'Tax class', +                          'taxclass'    => 'Tax class', +                          'disabled'    => 'Disabled', +                        }, +     'fields'        => [ 'taxclass', +                          { 'field' => 'disabled', +                            'type'  => 'checkbox', +                            'value' => 'Y', +                          }, +                        ], +     'viewall_dir'   => 'browse', +   ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/part_pkg_taxoverride.html b/httemplate/edit/part_pkg_taxoverride.html new file mode 100644 index 000000000..61dfa2ac5 --- /dev/null +++ b/httemplate/edit/part_pkg_taxoverride.html @@ -0,0 +1,132 @@ +<% include('/elements/header-popup.html', 'Override taxes', '', 'onload="resizeFrames()"') %> + +<TABLE WIDTH="100%" HEIGHT="100%"> +  <TR><TD> +    <iframe name="selected" src="<% $p %>browse/tax_class.html?_type=select;magic=select;maxrecords=15;offset=<% $selected_offset %>;selected=<% $selected %>;" width="100%" frameborder="0" border="0" id="selectorSelected" scrolling="no"> +</iframe> +    <BR> +  </TD></TR> + +  <TR><TD> +<FORM="dummy"> +  <CENTER> +    <INPUT type="submit" value="Finish" onclick="s=fetchSelected(); s.shift(); parent.document.getElementById('<% $element_name || "tax_override" %>').value=s.toString(); parent.<% $onclick %>();"> +    <INPUT type="reset" value="Cancel" onclick="parent.<% $onclick %>();"> +  </CENTER> +</FORM> +  </TD></TR> + +  <TR><TD> +    <iframe name="unselected" src="<% $p %>browse/tax_class.html?_type=select;magic=omit;maxrecords=15;offset=<% $unselected_offset %>;omit=<% $selected %>;" width="100%" frameborder="0" border="0" id="selectorUnselected" scrolling="no"> +</iframe> +    <BR> +  </TD></TR> + +</TABLE> +<SCRIPT> + +    function resizeFrames() { +      //frames['selected'].style.height = +      //  frames['selected'].contentWindow.document.body.scrollHeight + "px"; +      //frames['unselected'].style.height = +      //  frames['unselected'].contentWindow.document.body.scrollHeight + "px"; +      var f = document.getElementById('selectorSelected'); +      f.style.height = f.contentWindow.document.body.scrollHeight + "px"; +      var f = document.getElementById('selectorUnselected'); +      f.style.height = f.contentWindow.document.body.scrollHeight + "px"; +    } + +    function fetchOffset(search) { +      var value = 0; +      if (search.length > 1) { +        var params = search.split(';'); +        for (i=0; i<params.length; i++) { +          if (params[i].substr(0,7) == 'offset=') { +            value = params[i].substr(7); +          } +        } +      } +      return value; +    } + +    function fetchOffsetStrings() { +      return 'selected_offset=' + +             fetchOffset(frames['selected'].location.search) + ';' + +             'unselected_offset=' + +             fetchOffset(frames['unselected'].location.search) + ';'; +    } + +    function fetchSelected() { +      var i; +      var selected = new Array; +      var replace = '?'; +      if (window.location.search.length > 1) { +        var search = window.location.search.substr(1).split(';'); +        for (i=0; i<search.length; i++) { +          if (search[i].substr(0,9) == 'selected=') { +            selected = search[i].substr(9).split(',') +          }else if (search[i].substr(0,16) ==   'selected_offset=') { +          }else if (search[i].substr(0,18) == 'unselected_offset=') { +          }else if (search[i].length) { +            replace += search[i] + ';'; +          } +        } +      } +      selected.unshift(replace); +      return selected; +    } +    function doUnselect(classnum) { +      var selected = fetchSelected(); +      var search = selected.shift(); +      //alert("discovered: "+selected.toString()); +      var i=-1, j=-1, k=selected.length; +      while(++j < k) { +        if (!(selected[j]==classnum)) { +          selected[++i]=selected[j]; +        } +      } +      selected.length = ++i; +      //alert("finished: "+selected.toString()); + +      search += "selected=" + selected.toString() + ';'; +      window.location.search = search + fetchOffsetStrings(); +    } +    function doSelect(classnum) { +      var selected = fetchSelected(); +      var search = selected.shift(); +      selected.push(classnum); +      search += "selected=" + selected.toString() + ';'; +      window.location.search = search + fetchOffsetStrings(); +    } +</SCRIPT> + +<% include('/elements/footer.html') %> +<%once> + +my $conf = new FS::Conf; + +</%once> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + + +my $selected_offset = $1 +  if $cgi->param('selected_offset') =~/^(\d+)$/; + +my $unselected_offset = $1 +  if $cgi->param('unselected_offset') =~/^(\d+)$/; + +my $selected = $1 +  if $cgi->param('selected') =~/^([,\d]+)$/; + +my $element_name = $1 +  if $cgi->param('element_name') =~/^(\w+)$/; + +my $onclick = $1 +  if $cgi->param('onclick') =~/^(\w+)$/; + +$onclick = 'cClick' unless $onclick; + +</%init> diff --git a/httemplate/edit/part_referral.html b/httemplate/edit/part_referral.html new file mode 100755 index 000000000..daf8773f0 --- /dev/null +++ b/httemplate/edit/part_referral.html @@ -0,0 +1,19 @@ +<% include( 'elements/edit.html', +                'name'        => 'Advertising source', +                'table'       => 'part_referral', +                'fields'      => [ 'referral', +                                   { field=>'agentnum', type=>'select-agent', }, +                                 ], +                'labels'      => { 'referral' => 'Advertising source', +                                   'agentnum' => 'Agent', +                                 }, +                'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Edit advertising sources') +      || $FS::CurrentUser::CurrentUser->access_right('Edit global advertising sources'); + +</%init> diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi new file mode 100755 index 000000000..79703435c --- /dev/null +++ b/httemplate/edit/part_svc.cgi @@ -0,0 +1,383 @@ +<% 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">Label</TH>'. +%                 '<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>'. +%               '</TR>'; +% +%      my $bgcolor1 = '#eeeeee'; +%      my $bgcolor2 = '#ffffff'; +%      my $bgcolor; +% +%      #yucky kludge +%      my @fields = defined( dbdef->table($layer) ) +%                      ? grep { +%                               $_ ne 'svcnum' && +%                               ( !FS::part_svc->svc_table_fields($layer) +%                                     ->{$_}->{disable_part_svc_column}   || +%                                 $part_svc->part_svc_column($_)->columnflag +%                               ) +%                             } +%                             fields($layer) +%                      : (); +%      push @fields, 'usergroup' if $layer eq 'svc_acct'; #kludge +%      $part_svc->svcpart($clone) if $clone; #haha, undone below +% +% +%      foreach my $field (@fields) { +% +%        #a few lines of false laziness w/browse/part_svc.cgi +%        my $def = FS::part_svc->svc_table_fields($layer)->{$field}; +%        my $def_info  = $def->{'def_info'}; +%        my $formatter = $def->{'format'} || sub { shift }; +% +%        my $part_svc_column = $part_svc->part_svc_column($field); +%        my $label = $part_svc_column->columnlabel || $def->{'label'}; +%        my $value = &$formatter($part_svc_column->columnvalue); +%        my $flag  = $part_svc_column->columnflag; +% +%        if ( $bgcolor eq $bgcolor1 ) { +%          $bgcolor = $bgcolor2; +%        } else { +%          $bgcolor = $bgcolor1; +%        } +%         +%        $html .= qq!<TR><TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor" ALIGN="right">!. +%                 ( $def->{'label'} || $field ). +%                 "</TD>"; +% +%        $html .= qq!<TD ROWSPAN=2 CLASS="grid" BGCOLOR="$bgcolor"><INPUT NAME="${layer}__${field}_label" VALUE="!. encode_entities($label). '" STYLE="text-align:right"></TD>'; +% +%        $flag = '' if $def->{type} eq 'disabled'; +% +%        $html .= qq!<TD CLASS="grid" BGCOLOR="$bgcolor">!; +% +%        if ( $def->{type} eq 'disabled' ) { +%         +%          $html .= 'No default'; +% +%        } else { +% +%          $html .= qq!<SELECT NAME="${layer}__${field}_flag"!. +%                      qq! onChange="${layer}__${field}_flag_changed(this)">!; +% +%          foreach my $f ( keys %flag ) { +% +%            #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"; + +%        $def_info = "($def_info)" if $def_info; +%        $html .= +%          qq!<TR>!. +%          qq!  <TD COLSPAN=2 BGCOLOR="$bgcolor" ALIGN="center" !. +%          qq!      STYLE="padding:0; border-top: none">!. +%          qq!    <FONT SIZE="-1"><I>$def_info</I></FONT>!. +%          qq!  </TD>!. +%          qq!</TR>\n!; +% +%      } #foreach my $field (@fields) { +% +%      $part_svc->svcpart('') if $clone; #undone +%      $html .= "</TABLE>"; +% +%      $html .= include('/elements/progress-init.html', +%                         $layer, #form name +%                         [ qw(svc svcpart disabled exportnum), @fields ], +%                         'process/part_svc.cgi', +%                         $p.'browse/part_svc.cgi', +%                         $layer, +%                      ); +%      $html .= '<BR><INPUT NAME="submit" TYPE="button" VALUE="'. +%               ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '" '. +%               ' onClick="document.'. "$layer.submit.disabled=true; ". +%               "fixup(document.$layer); $layer". 'process();">'; +% +%      #$html .= '<BR><INPUT TYPE="submit" VALUE="'. +%      #         ($hashref->{svcpart} ? 'Apply changes' : 'Add service'). '">'; +% +%      $html; +% +%    }, +%  ); +% +% + +Table <% $widget->html %> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $part_svc; +my $clone = ''; +if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {#clone +  #$cgi->param('clone') =~ /^(\d+)$/ or die "malformed query: $query"; +  $part_svc = qsearchs('part_svc', { 'svcpart'=>$1 } ) +    or die "unknown svcpart: $1"; +  $clone = $part_svc->svcpart; +  $part_svc->svcpart(''); +} elsif ( $cgi->keywords ) { #edit +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/ or die "malformed query: $query"; +  $part_svc=qsearchs('part_svc', { 'svcpart'=>$1 } ) +    or die "unknown svcpart: $1"; +} else { #adding +  $part_svc = new FS::part_svc {}; +} + +my $action = $part_svc->svcpart ? 'Edit' : 'Add'; +my $hashref = $part_svc->hashref; +#   my $p_svcdb = $part_svc->svcdb || 'svc_acct'; + + + +</%init> + + + diff --git a/httemplate/edit/part_virtual_field.cgi b/httemplate/edit/part_virtual_field.cgi new file mode 100644 index 000000000..04ba9b0c0 --- /dev/null +++ b/httemplate/edit/part_virtual_field.cgi @@ -0,0 +1,104 @@ +<% include('/elements/header.html', "$action Virtual Field Definition") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/generic.cgi" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="table" VALUE="part_virtual_field"> +<INPUT TYPE="hidden" NAME="redirect_ok"  +    VALUE="<%popurl(2)%>browse/part_virtual_field.cgi"> +<INPUT TYPE="hidden" NAME="vfieldpart" VALUE="<% +  $vfieldpart%>"> +Field #<B><%$vfieldpart or "(NEW)"%></B><BR><BR> + +<%ntable("#cccccc",2)%> +  <TR> +    <TD ALIGN="right">Name</TD> +    <TD><INPUT TYPE="text" NAME="name" MAXLENGTH=32 VALUE="<% +    $part_virtual_field->name%>"></TD> +  </TR> +  <TR> +    <TD ALIGN="right">Table</TD> +    <TD> +% if ($action eq 'Add') {  + +      <SELECT SIZE=1 NAME="dbtable"> +% +%        my $dbdef = dbdef;  # ick +%        #foreach my $dbtable (sort { $a cmp $b } $dbdef->tables) { +%        foreach my $dbtable (qw( svc_broadband router )) { +%          if ($dbtable !~ /^h_/ +%          and $dbdef->table($dbtable)->primary_key) {  + +            <OPTION VALUE="<%$dbtable%>"><%$dbtable%></OPTION> +% +%          } +%        } +%       +</SELECT> +% +%    } else { # Edit +%     +<%$part_virtual_field->dbtable%> +    <INPUT TYPE="hidden" NAME="dbtable" VALUE="<%$part_virtual_field->dbtable%>"> +% }  + +    </TD> +  <TR> +    <TD ALIGN="right">Label</TD> +    <TD><INPUT TYPE="text" NAME="label" MAXLENGTH="80" VALUE="<% +    $part_virtual_field->label%>"></TD> +  </TR> +  <TR> +    <TD ALIGN="right">Length</TD> +    <TD><INPUT TYPE="text" NAME="length" MAXLENGTH=4 VALUE="<% +    $part_virtual_field->length%>"></TD> +  </TR> +  <TR> +    <TD ALIGN="right">Check</TD> +    <TD><TEXTAREA COLS="20" ROWS="4" NAME="check_block"><% +    $part_virtual_field->check_block%></TEXTAREA></TD> +  </TR> +  <TR> +    <TD ALIGN="right">List source</TD> +    <TD><TEXTAREA COLS="20" ROWS="4" NAME="list_source"><% +    $part_virtual_field->list_source%></TEXTAREA></TD> +  </TR> +</TABLE><BR><INPUT TYPE="submit" VALUE="Submit"> + +</FORM> + +<BR> +<FONT SIZE=-2>If you don't understand what <I>check_block</I> and  +<I>list_source</I> mean, <B>LEAVE THEM BLANK</B>.  We mean it.</FONT> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my ($vfieldpart, $part_virtual_field); + +if ( $cgi->param('error') ) { +  $part_virtual_field = new FS::part_virtual_field ( { +    map { $_, scalar($cgi->param($_)) } fields('part_virtual_field')}); +  $vfieldpart = $part_virtual_field->vfieldpart; +} else { +  my($query) = $cgi->keywords; +  if ( $query =~ /^(\d+)$/ ) { #editing +    $vfieldpart=$1; +    $part_virtual_field=qsearchs('part_virtual_field', +        {'vfieldpart' => $vfieldpart}) +      or die "Unknown vfieldpart!"; +   +  } else { #adding +    $part_virtual_field = new FS::part_virtual_field({}); +  } +} +my $action = $part_virtual_field->vfieldpart ? 'Edit' : 'Add'; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html new file mode 100644 index 000000000..a9909c365 --- /dev/null +++ b/httemplate/edit/payment_gateway.html @@ -0,0 +1,139 @@ +<% include( 'elements/edit.html', +            'table'          => 'payment_gateway', +            'name_singular'  => 'Payment gateway', +            'viewall_dir'    => 'browse', +            'fields'         => $fields, +            'field_callback' => $field_callback, +            'labels'         => { +                                  'gatewaynum'           => 'Gateway #', +                                  'gateway_module'       => 'Gateway', +                                  'gateway_username'     => 'Username', +                                  'gateway_password'     => 'Password', +                                  'gateway_action'       => 'Action', +                                  'gateway_options'      => 'Options: (Name/Value pairs, one element per line)', +                                  'gateway_callback_url' => 'Callback URL', +                                }, +          ) +%> + + +<SCRIPT TYPE="text/javascript"> +  var gatewayNamespace = new Array; + +% foreach my $module ( sort { lc($a) cmp lc ($b) } keys %modules ) { +    gatewayNamespace.push('<% $modules{$module} %>') +% }  + +  // document.getElementById('gateway_namespace').value = gatewayNamespace[0]; +  function setNamespace(what) { +    document.getElementById('gateway_namespace').value = +      gatewayNamespace[what.selectedIndex]; +  } + +</SCRIPT> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %modules =  ( +  '2CheckOut'             => 'Business::OnlinePayment', +  'AuthorizeNet'          => 'Business::OnlinePayment', +  'BankOfAmerica'         => 'Business::OnlinePayment', #deprecated? +  'Beanstream'            => 'Business::OnlinePayment', +  'Capstone'              => 'Business::OnlinePayment', +  'Cardstream'            => 'Business::OnlinePayment', +  'CashCow'               => 'Business::OnlinePayment', +  'CyberSource'           => 'Business::OnlinePayment', +  'eSec'                  => 'Business::OnlinePayment', +  'eSelectPlus'           => 'Business::OnlinePayment', +  'ElavonVirtualMerchant' => 'Business::OnlinePayment', +  'Exact'                 => 'Business::OnlinePayment', +  'iAuthorizer'           => 'Business::OnlinePayment', +  'Ingotz'                => 'Business::OnlinePayment', +  'InternetSecure'        => 'Business::OnlinePayment', +  'Interswitchng'         => 'Business::OnlineThirdPartyPayment', +  'IPaymentTPG'           => 'Business::OnlinePayment', +  'IPPay'                 => 'Business::OnlinePayment', +  'Iridium'               => 'Business::OnlinePayment', +  'Jettis'                => 'Business::OnlinePayment', +  'Jety'                  => 'Business::OnlinePayment', +  'LinkPoint'             => 'Business::OnlinePayment', +  'MerchantCommerce'      => 'Business::OnlinePayment', +  'Network1Financial'     => 'Business::OnlinePayment', +  'OCV'                   => 'Business::OnlinePayment', +  'OpenECHO'              => 'Business::OnlinePayment', +  'PayConnect'            => 'Business::OnlinePayment', +  'PayflowPro'            => 'Business::OnlinePayment', +  'PaymenTech'            => 'Business::OnlinePayment', +  'PaymentsGateway'       => 'Business::OnlinePayment', +  'PayPal'                => 'Business::OnlinePayment', +  #'PaySystems'            => 'Business::OnlinePayment', +  'PlugnPay'              => 'Business::OnlinePayment', +  'PPIPayMover'           => 'Business::OnlinePayment', +  'Protx'                 => 'Business::OnlinePayment', #now SagePay +  'PXPost'                => 'Business::OnlinePayment', +  'SagePay'               => 'Business::OnlinePayment', +  'SecureHostingUPG'      => 'Business::OnlinePayment', +  'Skipjack'              => 'Business::OnlinePayment', +  'StGeorge'              => 'Business::OnlinePayment', +  'SurePay'               => 'Business::OnlinePayment', +  'TCLink'                => 'Business::OnlinePayment', +  'TransactionCentral'    => 'Business::OnlinePayment', +  'TransFirsteLink'       => 'Business::OnlinePayment', +  'Vanco'                 => 'Business::OnlinePayment', +  'viaKLIX'               => 'Business::OnlinePayment', +  'VirtualNet'            => 'Business::OnlinePayment', +  'WesternACH'            => 'Business::OnlinePayment', +  'WorldPay'              => 'Business::OnlinePayment', +);  + +my @actions = ( +                'Normal Authorization', +                'Authorization Only', +                'Authorization Only, Post Authorization', +              ); + +my $fields = [ +               { +                 field               => 'gateway_namespace', +                 type                => 'hidden', +                 curr_value_callback => sub { my($cgi, $object, $fref) = @_; +                                              $modules{$object->gateway_module} +                                              || 'Business::OnlinePayment' +                                            }, +               }, +               { +                 field    => 'gateway_module', +                 type     => 'select', +                 options  => [ sort { lc($a) cmp lc ($b) } keys %modules ], +                 onchange => 'setNamespace', +               }, +               'gateway_username', +               'gateway_password', +               { +                 field    => 'gateway_action', +                 type     => 'select', +                 options  => \@actions, +               }, +               'gateway_callback_url', +               { +                 field               => 'gateway_options', +                 type                => 'textarea', +                 curr_value_callback => sub { my($cgi, $object, $fref) = @_; +                                              join("\r", $object->options ); +                                            }, +               }, +             ]; + +my $field_callback = sub { +  my ($cgi, $object, $field_hashref ) = @_; +  if ($object->gatewaynum) { +    if ( $field_hashref->{field} eq 'gateway_module' ) { +      $field_hashref->{type} = 'fixed'; +    } +  } +}; + +</%init> diff --git a/httemplate/edit/phone_device.html b/httemplate/edit/phone_device.html new file mode 100644 index 000000000..a1aa16620 --- /dev/null +++ b/httemplate/edit/phone_device.html @@ -0,0 +1,37 @@ +<% include( 'elements/edit.html', +                 'name'   => 'Phone device', +                 'table'  => 'phone_device', +                 'labels' => {  +                               'devicenum'  => 'Device', +                               'devicepart' => 'Device type', +                               'mac_addr'   => 'MAC address', +                             }, +                 'fields' => [ { 'field'    => 'devicepart', +                                 'type'     => 'select-table', +                                 'table'    => 'part_device', +                                 'name_col' => 'devicename', +                                 'empty_label' =>'Select device type', +                                 #'hashref'        =>{ disabled => '' }, +                               }, +                               'mac_addr', +                               { 'field' => 'svcnum', +                                 'type'  => 'hidden', +                               }, +                             ], +                 'menubar' => [], #disable viewall +                 #'viewall_dir' => 'browse', +                 'new_callback' => sub { +                                     my( $cgi, $object ) = @_; +                                     $object->svcnum( $cgi->param('svcnum') ); +                                   }, +           ) +%> +<%init> + +# :/  needs agent-virt so you can't futz with arbitrary devices + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + + +</%init> diff --git a/httemplate/edit/pkg_category.html b/httemplate/edit/pkg_category.html new file mode 100644 index 000000000..20e109383 --- /dev/null +++ b/httemplate/edit/pkg_category.html @@ -0,0 +1,28 @@ +<% include( 'elements/edit.html', +              'name'   => 'Package Category', +              'table'  => 'pkg_category', +              'fields' => [ +                            'categoryname', +                            'weight', +                            { field=>'condense', type=>'checkbox', value=>'Y', }, +                            { field=>'disabled', type=>'checkbox', value=>'Y', }, +                          ], +              'labels' => { +                            'categorynum'  => 'Category number', +                            'categoryname' => 'Category name', +                            'weight'       => 'Weight', +                            'condense'     => 'Collapse identical items to one', +                            'disabled'     => 'Disable category', +                          }, +              'viewall_dir' => 'browse', +              %opt, +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my %opt = @_; + +</%init> diff --git a/httemplate/edit/pkg_class.html b/httemplate/edit/pkg_class.html new file mode 100644 index 000000000..4f7a729bd --- /dev/null +++ b/httemplate/edit/pkg_class.html @@ -0,0 +1,5 @@ +<% include( 'elements/class_Common.html', +              'name'   => 'Package Class', +              'table'  => 'pkg_class', +          ) +%> diff --git a/httemplate/edit/prepay_credit.cgi b/httemplate/edit/prepay_credit.cgi new file mode 100644 index 000000000..ed404b7cd --- /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', +  1024       => 'Kbytes', +  1048576    => 'Mbytes', +  1073741824 => 'Gbytes', +; + +$cgi->param('multiplier',     '60')      unless $cgi->param('multiplier'); +$cgi->param('upmultiplier',   '1048576') unless $cgi->param('upmultiplier'); +$cgi->param('downmultiplier', '1048576') unless $cgi->param('downmultiplier'); +$cgi->param('totalmultiplier','1048576') unless $cgi->param('totalmultiplier'); + +</%init> diff --git a/httemplate/edit/process/REAL_cust_pkg.cgi b/httemplate/edit/process/REAL_cust_pkg.cgi new file mode 100755 index 000000000..22aab44e8 --- /dev/null +++ b/httemplate/edit/process/REAL_cust_pkg.cgi @@ -0,0 +1,54 @@ +%if ( $error ) { +%  $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "REAL_cust_pkg.cgi?". $cgi->query_string ) %> +%} else {  +%  my $custnum = $new->custnum; +%  my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/ +%               ? '' +%               : ';show=packages'; +%  my $frag = "cust_pkg$pkgnum"; #hack for IE ignoring real #fragment +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?custnum=$custnum$show;fragment=$frag#$frag" ) %> +%} +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Edit customer package dates'); + +my $pkgnum = $cgi->param('pkgnum') or die; +my $old = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); +my %hash = $old->hash; +$hash{'start_date'} = $cgi->param('start_date') ? str2time($cgi->param('start_date')) : ''; +$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 @errors = (); + +push @errors, '_bill_areyousure' +  if $hash{'bill'} != $old->bill             # if the next bill date was changed +  && $hash{'bill'} < time                    # to a date in the past +  && ! $cgi->param('bill_areyousure');       # and it wasn't confirmed + +push @errors, '_setup_areyousure' +  if ! $hash{'setup'} && $old->setup         # if the setup date was removed +  && ! $cgi->param('setup_areyousure');      # and it wasn't confirmed  + +push @errors, '_start' +  if $hash{'start_date'} && $old->start_date # if a start date was added +  && $hash{'setup'};                         # but there's a setup date + +my $new; +my $error; +if ( @errors ) { +  $error = join(',', @errors); +} else { +  $new = new FS::cust_pkg \%hash; +  $error = $new->replace($old); +} + +</%init> diff --git a/httemplate/edit/process/access_group.html b/httemplate/edit/process/access_group.html new file mode 100644 index 000000000..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..af9e49500 --- /dev/null +++ b/httemplate/edit/process/bulk-cust_main_county.html @@ -0,0 +1,66 @@ +% if ( $error ) { #better to redirect back to  +%# <% $cgi->redirect("$url?". $cgi->query_string ) %> +  <% include('/elements/header-popup.html', "Error ${action}ing taxes" ) %> + +  <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT> +  <BR><BR> + +  </BODY> +  </HTML> + +% } else { +  <% include('/elements/header-popup.html', "Taxes ${action}ed") %> + +  <SCRIPT TYPE="text/javascript"> +    window.top.location.reload(); +  </SCRIPT> + +  </BODY> +  </HTML> +% } +<%init> + +$cgi->param('taxnum') =~ /^([\d,]+)$/ +  or die 'Guru Meditation #69'; #??? should have been passed in +my @taxnum = split(',', $1); + +$cgi->param('action') =~ /^(add|edit)$/ or die "unknown action"; +my $action = $1; + +my $error = ''; +foreach my $taxnum ( @taxnum ) { + +  my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } ) +    or die "unknown taxnum: $taxnum"; + +  if ( $action eq 'edit' || $cust_main_county->tax == 0 ) { #let's replace + +    foreach (qw( taxname tax exempt_amount setuptax recurtax )) { +      $cust_main_county->set( $_ => scalar($cgi->param($_)) ) +    } + +    $error = $cust_main_county->replace and last; + +  } else { #let's insert a new record + +    my $new = +      new FS::cust_main_county { +        ( map { $_ => scalar($cgi->param($_)) } +              qw( taxname tax exempt_amount setuptax recurtax ) +        ), +        ( map { $_ => $cust_main_county->get($_) } +              qw( country state county taxclass )  +        ) +      }; + +    $error = $new->insert and last; +     +  } + +} + +if ( $error ) { +  $cgi->param('error', $error); +} + +</%init> diff --git a/httemplate/edit/process/bulk-cust_svc.cgi b/httemplate/edit/process/bulk-cust_svc.cgi new file mode 100644 index 000000000..313b061ff --- /dev/null +++ b/httemplate/edit/process/bulk-cust_svc.cgi @@ -0,0 +1,9 @@ +<% $server->process %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $server = new FS::UI::Web::JSRPC 'FS::part_svc::process_bulk_cust_svc', $cgi; + +</%init> diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html new file mode 100644 index 000000000..7356e6122 --- /dev/null +++ b/httemplate/edit/process/change-cust_pkg.html @@ -0,0 +1,46 @@ +% if ($error) { +%   $cgi->param('error', $error); +%   $cgi->redirect(popurl(3). 'misc/change_pkg.cgi?'. $cgi->query_string ); +% } else { + +    <% header("Package changed") %> +      <SCRIPT TYPE="text/javascript"> +        window.top.location.reload(); +      </SCRIPT> +    </BODY> +    </HTML> + +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Change customer package'); + +my $cust_pkg = qsearchs({ +  #'select'    => 'cust_pkg.*', +  'table'     => 'cust_pkg', +  'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', +  'hashref'   => { 'pkgnum' => scalar($cgi->param('pkgnum')), }, +  'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); +die 'unknown pkgnum' unless $cust_pkg; + +my %change = map { $_ => scalar($cgi->param($_)) } +                 qw( locationnum pkgpart ); + +if ( $cgi->param('locationnum') == -1 ) { +  my $cust_location = new FS::cust_location { +    'custnum' => $cust_pkg->custnum, +    map { $_ => scalar($cgi->param($_)) } +        qw( address1 address2 city county state zip country ) +  }; +  $change{'cust_location'} = $cust_location; +} + +my $pkg_or_error = $cust_pkg->change( \%change ); + +my $error = ref($pkg_or_error) ? '' : $pkg_or_error; + +</%init> diff --git a/httemplate/edit/process/cust_bill_pay.cgi b/httemplate/edit/process/cust_bill_pay.cgi new file mode 100755 index 000000000..2845d3233 --- /dev/null +++ b/httemplate/edit/process/cust_bill_pay.cgi @@ -0,0 +1,13 @@ +<% include('elements/ApplicationCommon.html', +     'error_redirect' => 'cust_bill_pay.cgi', +     'src_table'      => 'cust_pay', +     'src_thing'      => 'payment', +     'link_table'     => 'cust_bill_pay', +   ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/process/cust_category.html b/httemplate/edit/process/cust_category.html new file mode 100644 index 000000000..c3a880944 --- /dev/null +++ b/httemplate/edit/process/cust_category.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', +               'table'       => 'cust_category', +               'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/cust_class.html b/httemplate/edit/process/cust_class.html new file mode 100644 index 000000000..3f63ea4b3 --- /dev/null +++ b/httemplate/edit/process/cust_class.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', +               'table'       => 'cust_class', +               'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/cust_credit.cgi b/httemplate/edit/process/cust_credit.cgi new file mode 100755 index 000000000..8715ad61e --- /dev/null +++ b/httemplate/edit/process/cust_credit.cgi @@ -0,0 +1,63 @@ +%if ( $error ) { +%  $cgi->param('reasonnum', $reasonnum); +%  $cgi->param('error', $error); +%  $dbh->rollback if $oldAutoCommit; +%   +<% $cgi->redirect(popurl(2). "cust_credit.cgi?". $cgi->query_string ) %> +% +%} else { +% +%  if ( $cgi->param('apply') eq 'yes' ) { +%    my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum }) +%      or die "unknown custnum $custnum"; +%    $cust_main->apply_credits; +%  } +%  #print $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum"); +% +%  $dbh->commit or die $dbh->errstr if $oldAutoCommit; +%   +<% header('Credit sucessful') %> +  <SCRIPT TYPE="text/javascript"> +    window.top.location.reload(); +  </SCRIPT> + +  </BODY></HTML> +% }  +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Post credit'); + +$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!"; +my $custnum = $1; + +$cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum"; +my $reasonnum = $1; + +my $oldAutoCommit = $FS::UID::AutoCommit; +local $FS::UID::AutoCommit = 0; +my $dbh = dbh; + +my $error = ''; +if ($reasonnum == -1) { + +  $error = 'Enter a new reason (or select an existing one)' +    unless $cgi->param('newreasonnum') !~ /^\s*$/; +  my $reason = new FS::reason({ 'reason_type' => $cgi->param('newreasonnumT'), +                                'reason'      => $cgi->param('newreasonnum'), +                              }); +  $error ||= $reason->insert; +  $cgi->param('reasonnum', $reason->reasonnum) +    unless $error; +} + +unless ($error) { +  my $new = new FS::cust_credit ( { +    map { +      $_, scalar($cgi->param($_)); +    } fields('cust_credit') +  } ); +  $error = $new->insert; +} + +</%init> diff --git a/httemplate/edit/process/cust_credit_bill.cgi b/httemplate/edit/process/cust_credit_bill.cgi new file mode 100755 index 000000000..d3847dc40 --- /dev/null +++ b/httemplate/edit/process/cust_credit_bill.cgi @@ -0,0 +1,19 @@ +<% include('elements/ApplicationCommon.html', +     'error_redirect' => 'cust_credit_bill.cgi', +     'src_table'      => 'cust_credit', +     'src_thing'      => 'credit', +     'link_table'     => 'cust_credit_bill', +   ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Apply credit'); + +if ( $cgi->param('src_amount') ) { +  die "access denied" +    unless ( $FS::CurrentUser::CurrentUser->access_right('Post credit') && +           $FS::CurrentUser::CurrentUser->access_right('Delete credit') ); +} + +</%init> diff --git a/httemplate/edit/process/cust_credit_refund.cgi b/httemplate/edit/process/cust_credit_refund.cgi new file mode 100755 index 000000000..88420f8ab --- /dev/null +++ b/httemplate/edit/process/cust_credit_refund.cgi @@ -0,0 +1,13 @@ +<% include('elements/ApplicationCommon.html', +     'error_redirect' => 'cust_credit_refund.cgi', +     'src_table'      => 'cust_credit', +     'src_thing'      => 'credit', +     'link_table'     => 'cust_credit_refund', +   ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Apply credit'); + +</%init> diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi new file mode 100755 index 000000000..f72ca0a81 --- /dev/null +++ b/httemplate/edit/process/cust_main.cgi @@ -0,0 +1,260 @@ +% if ( $error ) { +%   $cgi->param('error', $error); +% +<% $cgi->redirect(popurl(2). "cust_main.cgi?". $cgi->query_string ) %> +% +% } else {  +% +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?". $new->custnum) %> +% +% } +<%once> + +my $me = '[edit/process/cust_main.cgi]'; +my $DEBUG = 0; + +</%once> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Edit customer'); + +my $conf = new FS::Conf; + +my $error = ''; + +#unmunge stuff + +$cgi->param('tax','') unless defined $cgi->param('tax'); + +$cgi->param('refnum', (split(/:/, ($cgi->param('refnum'))[0] ))[0] ); + +my $payby = $cgi->param('payby'); + +my %noauto = ( +  'CARD' => 'DCRD', +  'CHEK' => 'DCHK', +); +$payby = $noauto{$payby} +  if ! $cgi->param('payauto') && exists $noauto{$payby}; + +$cgi->param('payby', $payby); + +if ( $payby ) { +  if ( $payby eq 'CHEK' || $payby eq 'DCHK' ) { +    $cgi->param('payinfo', +      $cgi->param('payinfo1'). '@'. $cgi->param('payinfo2') ); +  } +  $cgi->param('paydate', +    $cgi->param( 'exp_month' ). '-'. $cgi->param( 'exp_year' ) ); +} + +my @invoicing_list = split( /\s*\,\s*/, $cgi->param('invoicing_list') ); +push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST'); +push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX'); +$cgi->param('invoicing_list', join(',', @invoicing_list) ); + + +#create new record object + +my $new = new FS::cust_main ( { +  map { +    $_, scalar($cgi->param($_)) +#  } qw(custnum agentnum last first ss company address1 address2 city county +#       state zip daytime night fax payby payinfo paydate payname tax +#       otaker refnum) +  } fields('cust_main') +} ); + +if ( defined($cgi->param('same')) && $cgi->param('same') eq "Y" ) { +  $new->setfield("ship_$_", '') foreach qw( +    last first company address1 address2 city county state zip +    country daytime night fax +  ); +} + +my %usedatetime = ( 'birthdate' => 1 ); + +foreach my $dfield (qw( birthdate signupdate )) { + +  if ( $cgi->param($dfield) && $cgi->param($dfield) =~ /^([ 0-9\-\/]{0,10})$/) { + +    my $value = $1; +    my $parsed = ''; + +    if ( exists $usedatetime{$dfield} && $usedatetime{$dfield} ) { + +      my $format = $conf->config('date_format') || "%m/%d/%Y"; +      my $parser = DateTime::Format::Strptime->new( pattern   => $format, +                                                    time_zone => 'floating', +                                                  ); +      my $dt = $parser->parse_datetime($value); +      if ( $dt ) { +        $parsed = $dt->epoch; +      } else { +    #    $error ||= $cgi->param('birthdate') . " is an invalid birthdate:" . $parser->errmsg; +        $error ||= "Invalid $dfield: $value"; +      } + +    } else { + +      $parsed = str2time($value) +        or $error ||= "Invalid $dfield: $value"; + +    } + +    $new->setfield( $dfield, $parsed ); +    $cgi->param(    $dfield, $parsed ); + +  } + +} + +$new->setfield('paid', $cgi->param('paid') ) +  if $cgi->param('paid'); + +my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups'); +my @tax_exempt = grep { $cgi->param("tax_$_") eq 'Y' } @exempt_groups; + +#perhaps this stuff should go to cust_main.pm +if ( $new->custnum eq '' ) { + +  my $cust_pkg = ''; +  my $svc; + +  if ( $cgi->param('pkgpart_svcpart') ) { + +    my $x = $cgi->param('pkgpart_svcpart'); +    $x =~ /^(\d+)_(\d+)$/ or die "illegal pkgpart_svcpart $x\n"; +    my($pkgpart, $svcpart) = ($1, $2); +    my $part_pkg = qsearchs('part_pkg', { 'pkgpart' => $pkgpart } ); +    #false laziness: copied from FS::cust_pkg::order (which should become a +    #FS::cust_main method) +    my(%part_pkg); +    # generate %part_pkg +    # $part_pkg{$pkgpart} is true iff $custnum may purchase $pkgpart +    my $agent = qsearchs('agent',{'agentnum'=> $new->agentnum }); + +    if ( $agent ) { +      # $pkgpart_href->{PKGPART} is true iff $custnum may purchase $pkgpart +      my $pkgpart_href = $agent->pkgpart_hashref +        if $agent; +      #eslaf + +      # this should wind up in FS::cust_pkg! +      $error ||= "Agent ". $new->agentnum. " (type ". $agent->typenum. +                 ") can't purchase pkgpart ". $pkgpart +        #unless $part_pkg{ $pkgpart }; +        unless $pkgpart_href->{ $pkgpart } +            || $agent->agentnum == $part_pkg->agentnum; +    } else { +      $error = 'Select agent'; +    } + +    $cust_pkg = new FS::cust_pkg ( { +      #later         'custnum' => $custnum, +      'pkgpart' => $pkgpart, +    } ); +    #$error ||= $cust_pkg->check; + +    #$cust_svc = new FS::cust_svc ( { 'svcpart' => $svcpart } ); + +    #$error ||= $cust_svc->check; + +    my $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } ); +    my $svcdb = $part_svc->svcdb; + +    if ( $svcdb eq 'svc_acct' ) { + +      my %svc_acct = ( +                       'svcpart'   => $svcpart, +                       'username'  => scalar($cgi->param('username')), +                       '_password' => scalar($cgi->param('_password')), +                       'popnum'    => scalar($cgi->param('popnum')), +                     ); +      $svc_acct{'domsvc'} = $cgi->param('domsvc') +        if $cgi->param('domsvc'); + +      $svc = new FS::svc_acct \%svc_acct; + +      #and just in case you were silly +      $svc->svcpart($svcpart); +      $svc->username($cgi->param('username')); +      $svc->_password($cgi->param('_password')); +      $svc->popnum($cgi->param('popnum')); + +    } elsif ( $svcdb eq 'svc_phone' ) { + +      my %svc_phone = ( +                        'svcpart' => $svcpart, +                        map { $_ => scalar($cgi->param($_)) } +                          qw( countrycode phonenum sip_password pin phone_name ) +                      ); + +      $svc = new FS::svc_phone \%svc_phone; + +    } else { +      die "$svcdb not handled on new customer yet"; +    } + +    #$error ||= $svc_acct->check; + +  } + +  use Tie::RefHash; +  tie my %hash, 'Tie::RefHash'; +  %hash = ( $cust_pkg => [ $svc ] ) if $cust_pkg; +  $error ||= $new->insert( \%hash, \@invoicing_list, +                           'tax_exemption' => \@tax_exempt, +                         ); + +  my $conf = new FS::Conf; +  if ( $conf->exists('backend-realtime') && ! $error ) { + +    my $berror =    $new->bill +                 || $new->apply_payments_and_credits +                 || $new->collect( 'realtime' => 1 ); +    warn "Warning, error billing during backend-realtime: $berror" if $berror; + +  } +   +} else { #create old record object + +  my $old = qsearchs( 'cust_main', { 'custnum' => $new->custnum } );  +  $error ||= "Old record not found!" unless $old; +  if ( length($old->paycvv) && $new->paycvv =~ /^\s*\*+\s*$/ ) { +    $new->paycvv($old->paycvv); +  } +  if ($new->ss =~ /xx/) { +    $new->ss($old->ss); +  } +  if ($new->stateid =~ /^xxx/) { +    $new->stateid($old->stateid); +  } +  if ($new->payby =~ /^(CARD|DCRD)$/ && $new->payinfo =~ /xx/) { +    $new->payinfo($old->payinfo); +  } elsif ($new->payby =~ /^(CHEK|DCHK)$/ && $new->payinfo =~ /xx/) { +    #fix for #3085 "edit of customer's routing code only surprisingly causes +    #nothing to happen... +    # this probably won't do the right thing when we don't have the +    # public key (can't actually get the real $old->payinfo) +    my($new_account, $new_aba) = split('@', $new->payinfo); +    my($old_account, $old_aba) = split('@', $old->payinfo); +    $new_account = $old_account if $new_account =~ /xx/; +    $new_aba     = $old_aba     if $new_aba     =~ /xx/; +    $new->payinfo($new_account.'@'.$new_aba); +  } + +  warn "$me calling $new -> replace( $old, \ @invoicing_list )" if $DEBUG; +  local($FS::cust_main::DEBUG) = $DEBUG if $DEBUG; +  local($FS::Record::DEBUG)    = $DEBUG if $DEBUG; + +  $error ||= $new->replace( $old, \@invoicing_list, +                            'tax_exemption' => \@tax_exempt, +                          ); + +  warn "$me returned from replace" if $DEBUG; +   +} + +</%init> diff --git a/httemplate/edit/process/cust_main_attach.cgi b/httemplate/edit/process/cust_main_attach.cgi new file mode 100644 index 000000000..092714122 --- /dev/null +++ b/httemplate/edit/process/cust_main_attach.cgi @@ -0,0 +1,101 @@ +%if ($error) { +%  $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). 'cust_main_attach.cgi?'. $cgi->query_string ) %> +%} else { +% my $act = 'added'; +% $act = 'updated' if ($attachnum); +% $act = 'purged' if($attachnum and $purge); +% $act = 'undeleted' if($attachnum and $undelete); +% $act = 'deleted' if($attachnum and $delete); +<% header('Attachment ' . $act ) %> +    <SCRIPT TYPE="text/javascript"> +      window.top.location.reload(); +    </SCRIPT> +    </BODY></HTML> +% } +<%init> + +my $error; +$cgi->param('custnum') =~ /^(\d+)$/ +  or die "Illegal custnum: ". $cgi->param('custnum'); +my $custnum = $1; + +$cgi->param('attachnum') =~ /^(\d*)$/ +  or die "Illegal attachnum: ". $cgi->param('attachnum'); +my $attachnum = $1; + +my $curuser = $FS::CurrentUser::CurrentUser; +my $otaker = $curuser->name; +$otaker = $curuser->username if ($otaker eq "User, Legacy"); + +my $delete = $cgi->param('delete'); +my $undelete = $cgi->param('undelete'); +my $purge = $cgi->param('purge'); + +my $new = new FS::cust_attachment ( { +  attachnum => $attachnum, +  custnum   => $custnum, +  _date     => time, +  otaker    => $otaker, +  disabled  => '', +}); +my $old; + +if($attachnum) { +  $old = qsearchs('cust_attachment', { attachnum => $attachnum }); +  if(!$old) { +    $error = "Attachnum '$attachnum' not found"; +  } +  elsif($purge) { # do nothing +  } +  else { +    map { $new->$_($old->$_) }  +      ('_date', 'otaker', 'body', 'disabled'); +    $new->filename($cgi->param('filename') || $old->filename); +    $new->mime_type($cgi->param('mime_type') || $old->mime_type); +    $new->title($cgi->param('title')); +    if($delete and not $old->disabled) { +      $new->disabled(time); +    } +    if($undelete and $old->disabled) { +      $new->disabled(''); +    } +  } +} +else { # This is a new attachment, so require a file. + +  my $filename = $cgi->param('file'); +  if($filename) { +    $new->filename($filename); +    $new->mime_type($cgi->uploadInfo($filename)->{'Content-Type'}); +    $new->title($cgi->param('title')); +     +    local $/; +    my $fh = $cgi->upload('file'); +    $new->body(<$fh>); +  } +  else { +    $error = 'No file uploaded'; +  } +} +my $action = 'Add'; +$action = 'Edit' if $attachnum; +$action = 'Delete' if $attachnum and $delete; +$action = 'Undelete' if $attachnum and $undelete; +$action = 'Purge' if $attachnum and $purge; + +$error = 'access denied' unless $curuser->access_right($action . ' attachment'); + +if(!$error) { +  if($old and $old->disabled and $purge) { +    $error = $old->delete; +  } +  elsif($old) { +    $error = $new->replace($old); +  } +  else { +    $error = $new->insert; +  } +} + +</%init> diff --git a/httemplate/edit/process/cust_main_county-collapse.cgi b/httemplate/edit/process/cust_main_county-collapse.cgi new file mode 100755 index 000000000..9608fc919 --- /dev/null +++ b/httemplate/edit/process/cust_main_county-collapse.cgi @@ -0,0 +1,46 @@ +<% $cgi->redirect(popurl(3). "browse/cust_main_county.cgi") %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my($query) = $cgi->keywords; +$query =~ /^(\d+)$/ or die "Illegal taxnum!"; +my $taxnum = $1; +my $cust_main_county = qsearchs('cust_main_county', { 'taxnum' => $taxnum } ) +  or die "Unknown taxnum $taxnum"; + +#really should do this in a .pm & start transaction + +my %search = ( +               'country' => $cust_main_county->country, +               'state'   => $cust_main_county->state, +             ); + +$search{'county'} = $cust_main_county->county +  if $cust_main_county->city; + +foreach my $delete ( qsearch('cust_main_county', \%search) ) { +#  unless ( qsearch('cust_main',{ +#    'state'  => $cust_main_county->getfield('state'), +#    'county' => $cust_main_county->getfield('county'), +#    'country' =>  $cust_main_county->getfield('country'), +#  } ) ) { +    my $error = $delete->delete; +    die $error if $error; +#  } else { +    #should really fix the $cust_main record +#  } + +} + +$cust_main_county->taxnum(''); +if ( $cust_main_county->city ) { +  $cust_main_county->city(''); +} else { +  $cust_main_county->county(''); +} +my $error = $cust_main_county->insert; +die $error if $error; + +</%init> diff --git a/httemplate/edit/process/cust_main_county-expand.cgi b/httemplate/edit/process/cust_main_county-expand.cgi new file mode 100755 index 000000000..9984b08fa --- /dev/null +++ b/httemplate/edit/process/cust_main_county-expand.cgi @@ -0,0 +1,83 @@ +<% include('/elements/header-popup.html', 'Addition successful' ) %> + +<SCRIPT TYPE="text/javascript"> +  window.top.location.reload(); +</SCRIPT> + +</BODY> +</HTML> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +$cgi->param('taxnum') =~ /^(\d+)$/ or die "Illegal taxnum!"; +my $taxnum = $1; +my $cust_main_county = qsearchs('cust_main_county',{'taxnum'=>$taxnum}) +  or die ("Unknown taxnum!"); + +my @expansion; +if ( $cgi->param('taxclass') ) { +  my $sth = dbh->prepare('SELECT taxclass FROM part_pkg_taxclass') +    or die dbh->errstr; +  $sth->execute or die $sth->errstr; +  @expansion = map $_->[0], @{$sth->fetchall_arrayref}; +  die "no taxclasses - add one first" unless @expansion;#XXX better err handling +} else { +  @expansion = split /[\n\r]{1,2}/, $cgi->param('expansion'); + +  #warn scalar(@expansion); +  #warn "$_: $expansion[$_]\n" foreach (0..$#expansion); + +  @expansion=map { +    unless ( /^\s*([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]+)\s*$/ ) { +      $cgi->param('error', "Illegal item in expansion: $_"); +      print $cgi->redirect(popurl(2). "cust_main_county-expand.cgi?". $cgi->query_string ); +      myexit(); +    } +    $1; +  } @expansion; + +} + +foreach ( @expansion) { +  my(%hash)=$cust_main_county->hash; +  my($new)=new FS::cust_main_county \%hash; +  $new->setfield('taxnum',''); +  if ( $cgi->param('taxclass') ) { +    $new->setfield('taxclass', $_); +  } elsif ( ! $cust_main_county->state ) { +    $new->setfield('state',$_); +  } elsif ( ! $cust_main_county->county ) { +    $new->setfield('county',$_); +  } else { +    #uppercase cities in the US to try and agree with USPS validation +    $new->setfield('city', $new->country eq 'US' ? uc($_) : $_ ); +  } +  my $error = $new->insert; +  die $error if $error; +} + +unless ( qsearch( 'cust_main', { +                                 'city'    => $cust_main_county->city, +                                 'county'  => $cust_main_county->county, +                                 'state'   => $cust_main_county->state, +                                 'country' => $cust_main_county->country, +                               } ) +         || ! @expansion +) { +  my $error = $cust_main_county->delete; +  die $error if $error; +} + +if ( $cgi->param('taxclass') ) { +  print $cgi->redirect(popurl(3). "browse/cust_main_county.cgi?". +                         'city='.    uri_escape($cust_main_county->city   ).';'. +                         'county='.  uri_escape($cust_main_county->county ).';'. +                         'state='.   uri_escape($cust_main_county->state  ).';'. +                         'country='. uri_escape($cust_main_county->country) +                      ); +  myexit; +} + +</%init> diff --git a/httemplate/edit/process/cust_main_county.html b/httemplate/edit/process/cust_main_county.html new file mode 100644 index 000000000..cb56166c8 --- /dev/null +++ b/httemplate/edit/process/cust_main_county.html @@ -0,0 +1,13 @@ +<% include( 'elements/process.html', +              'table' => 'cust_main_county', +              'popup_reload' => 'Tax changed', #a popup "parent reload" for now +              #someday change the individual element and go away instead +          ) +%> +<%init> + +my $conf = new FS::Conf; +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/cust_main_note.cgi b/httemplate/edit/process/cust_main_note.cgi new file mode 100755 index 000000000..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..a310c5306 --- /dev/null +++ b/httemplate/edit/process/cust_pay.cgi @@ -0,0 +1,57 @@ +%if ($error) { +%  $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). 'cust_pay.cgi?'. $cgi->query_string ) %> +%} elsif ( $field eq 'invnum' ) { +<% $cgi->redirect(popurl(3). "view/cust_bill.cgi?$linknum") %> +%} elsif ( $field eq 'custnum' ) { +%  if ( $cgi->param('apply') eq 'yes' ) { +%    my $cust_main = qsearchs('cust_main', { 'custnum' => $linknum }) +%      or die "unknown custnum $linknum"; +%    $cust_main->apply_payments( 'manual' => 1 ); +%  } +%  if ( $link eq 'popup' ) { +%     +<% header('Payment entered') %> +    <SCRIPT TYPE="text/javascript"> +      window.top.location.reload(); +    </SCRIPT> + +    </BODY></HTML> +% +%  } elsif ( $link eq 'custnum' ) { +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$linknum") %> +%  } else { +%    die "unknown link $link"; +%  } +% +%} +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Post payment'); + +$cgi->param('linknum') =~ /^(\d+)$/ +  or die "Illegal linknum: ". $cgi->param('linknum'); +my $linknum = $1; + +$cgi->param('link') =~ /^(custnum|invnum|popup)$/ +  or die "Illegal link: ". $cgi->param('link'); +my $field = my $link = $1; +$field = 'custnum' if $field eq 'popup'; + +my $_date = str2time($cgi->param('_date')); + +my $new = new FS::cust_pay ( { +  $field => $linknum, +  _date  => $_date, +  map { +    $_, scalar($cgi->param($_)); +  } qw( paid payby payinfo paybatch +        pkgnum +      ) +  #} fields('cust_pay') +} ); + +my $error = $new->insert( 'manual' => 1 ); + +</%init> diff --git a/httemplate/edit/process/cust_pay_pending.html b/httemplate/edit/process/cust_pay_pending.html new file mode 100644 index 000000000..1bad6cffe --- /dev/null +++ b/httemplate/edit/process/cust_pay_pending.html @@ -0,0 +1,68 @@ +<% include('/elements/header-popup.html', $title ) %> +% if ( $error ) { +  <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT> +% } else { +    <SCRIPT TYPE="text/javascript"> +      window.top.location.reload(); +    </SCRIPT> +% } +</BODY> +</HTML> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Edit customer pending payments'); + +$cgi->param('action') =~ /^(\w+)$/ or die 'illegal action'; +my $action = $1; + +$cgi->param('paypendingnum') =~ /^(\d+)$/ or die 'illegal paypendingnum'; +my $paypendingnum = $1; +my $cust_pay_pending = +  qsearchs({ +    'select'    => 'cust_pay_pending.*', +    'table'     => 'cust_pay_pending', +    'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', +    'hashref'   => { 'paypendingnum' => $paypendingnum }, +    'extra_sql' => ' AND '. $curuser->agentnums_sql, +  }) +  or die 'unknown paypendingnum'; + +my $error; +my $title; +if ( $action eq 'delete' ) { + +  $error = $cust_pay_pending->delete; +  if ( $error ) { +    $title = 'Error deleting pending payment'; +  } else { +    $title = 'Pending payment deletion sucessful'; +  } + +} elsif ( $action eq 'insert_cust_pay' ) {  + +  $error = $cust_pay_pending->insert_cust_pay; +  if ( $error ) { +    $title = 'Error completing pending payment'; +  } else { +    $title = 'Pending payment completed'; +  } + +} elsif ( $action eq 'decline' ) { + +  $error = $cust_pay_pending->decline; +  if ( $error ) { +    $title = 'Error declining pending payment'; +  } else { +    $title = 'Pending payment completed (decline)'; +  } + +} else { + +  die "unknown action $action"; + +} + +</%init>  diff --git a/httemplate/edit/process/cust_pay_refund.cgi b/httemplate/edit/process/cust_pay_refund.cgi new file mode 100755 index 000000000..2616cad8c --- /dev/null +++ b/httemplate/edit/process/cust_pay_refund.cgi @@ -0,0 +1,13 @@ +<% include('elements/ApplicationCommon.html', +     'error_redirect' => 'cust_pay_refund.cgi', +     'src_table'      => 'cust_pay', +     'src_thing'      => 'payment', +     'link_table'     => 'cust_pay_refund', +   ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Apply payment'); + +</%init> diff --git a/httemplate/edit/process/cust_pkg.cgi b/httemplate/edit/process/cust_pkg.cgi new file mode 100755 index 000000000..c564c417e --- /dev/null +++ b/httemplate/edit/process/cust_pkg.cgi @@ -0,0 +1,42 @@ +% if ($error) { +%   $cgi->param('error', $error); +%   $cgi->redirect(popurl(3). 'edit/cust_pkg.cgi?'. $cgi->query_string ); +% } else { +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum") %> +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Bulk change customer packages'); + +my $error = ''; + +#untaint custnum +$cgi->param('custnum') =~ /^(\d+)$/; +my $custnum = $1; + +my @remove_pkgnums = map { +  /^(\d+)$/ or die "Illegal remove_pkg value!"; +  $1; +} $cgi->param('remove_pkg'); + +my( $action, $error_redirect ) = ( '', '' ); +my @pkgparts = (); + +foreach my $pkgpart ( map /^pkg(\d+)$/ ? $1 : (), $cgi->param ) { +  if ( $cgi->param("pkg$pkgpart") =~ /^(\d+)$/ ) { +    my $num_pkgs = $1; +    while ( $num_pkgs-- ) { +      push @pkgparts,$pkgpart; +    } +  } else { +    $error = "Illegal quantity"; +    last; +  } +} + +$error ||= FS::cust_pkg::order($custnum,\@pkgparts,\@remove_pkgnums); + +</%init> diff --git a/httemplate/edit/process/cust_pkg_detail.html b/httemplate/edit/process/cust_pkg_detail.html new file mode 100644 index 000000000..132ff63c5 --- /dev/null +++ b/httemplate/edit/process/cust_pkg_detail.html @@ -0,0 +1,59 @@ +% if ( $error ) { +<% header('Error') %> +<FONT COLOR="#ff0000"><B><% $error |h %></B></FONT><BR><BR> +<CENTER><INPUT TYPE="BUTTON" VALUE="OK" onClick="parent.cClick()"></CENTER> +</BODY></HTML> +% } else { +<% header($action) %> +  <SCRIPT TYPE="text/javascript"> +    window.top.location.reload(); +  </SCRIPT> +  </BODY></HTML> +% } +<%init> + +my %access_right = ( +  'I' => 'Edit customer package invoice details',  +  'C' => 'Edit customer package comments', +); + +my %name = ( +  'I' => 'invoice details', +  'C' => 'package comments', +); + +my $curuser = $FS::CurrentUser::CurrentUser; + +$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype'; +my $detailtype = $1; + +my $right = $access_right{$detailtype}; +die "access denied" +  unless $curuser->access_right($right); + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum'; +my $pkgnum = $1; + +my $cust_pkg = qsearchs({ +  'table'     => 'cust_pkg', +  'addl_from' => 'LEFT JOIN cust_main USING ( custnum )', +  'hashref'   => { 'pkgnum' => $pkgnum }, +  'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); + + +my @orig_details = $cust_pkg->cust_pkg_detail($detailtype); + +my $action = ucfirst($name{$detailtype}). +             ( scalar(@orig_details) ? ' changed ' : ' added ' ); + +my $param = $cgi->Vars; +my @details = (); +for ( my $row = 0; exists($param->{"detail$row"}); $row++ ) { +  push @details, $param->{"detail$row"} +    if $param->{"detail$row"} =~ /\S/; +} + +my $error = $cust_pkg->set_cust_pkg_detail($detailtype, @details); + +</%init> diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi new file mode 100755 index 000000000..5749e5346 --- /dev/null +++ b/httemplate/edit/process/cust_refund.cgi @@ -0,0 +1,56 @@ +%if ( $error ) { +%  $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "cust_refund.cgi?". $cgi->query_string ) %> +%} else { +% +%  if ( $link eq 'popup' ) { +% +<% header('Refund entered') %> +    <SCRIPT TYPE="text/javascript"> +      window.top.location.reload(); +    </SCRIPT> + +    </BODY></HTML> +%  } else { +<% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum") %> +%  } +%} +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Refund payment') +      || $FS::CurrentUser::CurrentUser->access_right('Post refund'); + +$cgi->param('custnum') =~ /^(\d*)$/ or die "Illegal custnum!"; +my $custnum = $1; +my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) +  or die "unknown custnum $custnum"; + +my $link    = $cgi->param('popup') ? 'popup' : ''; + +my $error = ''; +if ( $cgi->param('payby') =~ /^(CARD|CHEK)$/ ) {  +  my %options = (); +  my $bop = $FS::payby::payby2bop{$1}; +  $cgi->param('refund') =~ /^(\d*)(\.\d{2})?$/ +    or die "illegal refund amount ". $cgi->param('refund'); +  my $refund = "$1$2"; +  $cgi->param('paynum') =~ /^(\d*)$/ or die "Illegal paynum!"; +  my $paynum = $1; +  my $reason = $cgi->param('reason'); +  my $paydate = $cgi->param('exp_year'). '-'. $cgi->param('exp_month'). '-01'; +  $options{'paydate'} = $paydate if $paydate =~ /^\d{2,4}-\d{1,2}-01$/; +  $error = $cust_main->realtime_refund_bop( $bop, 'amount' => $refund, +                                                  'paynum' => $paynum, +                                                  'reason' => $reason, +                                                  %options ); +} else { +  my $new = new FS::cust_refund ( { +    map { +      $_, scalar($cgi->param($_)); +    } fields('cust_refund') #huh? , 'paynum' ) +  } ); +  $error = $new->insert; +} + +</%init> diff --git a/httemplate/edit/process/cust_svc.cgi b/httemplate/edit/process/cust_svc.cgi new file mode 100644 index 000000000..e22cbb201 --- /dev/null +++ b/httemplate/edit/process/cust_svc.cgi @@ -0,0 +1,30 @@ +%if ( $error ) { +%  errorpage($error); +%} else {  +%  my $svcdb = $new->part_svc->svcdb; +<% $cgi->redirect(popurl(3). "view/$svcdb.cgi?$svcnum") %> +%} +<%init> + +die 'access deined' + unless $FS::CurrentUser::CurrentUser->access_right('Change customer service'); + +my $svcnum = $cgi->param('svcnum'); + +my $old = qsearchs('cust_svc',{'svcnum'=>$svcnum}) if $svcnum; + +my $new = new FS::cust_svc ( { +  map { +    $_, scalar($cgi->param($_)); +  } fields('cust_svc') +} ); + +my $error; +if ( $svcnum ) { +  $error=$new->replace($old); +} else { +  $error=$new->insert; +  $svcnum=$new->getfield('svcnum'); +} + +</%init> diff --git a/httemplate/edit/process/cust_tax_adjustment.html b/httemplate/edit/process/cust_tax_adjustment.html new file mode 100644 index 000000000..204b5b9f7 --- /dev/null +++ b/httemplate/edit/process/cust_tax_adjustment.html @@ -0,0 +1,41 @@ +% if ( $error ) { +%   $cgi->param('error', $error ); +<% $cgi->redirect($p.'cust_tax_adjustment.html?'. $cgi->query_string) %> +% } else { +<% header("Tax adjustment added") %> +  <SCRIPT TYPE="text/javascript"> +    //window.top.location.reload(); +    parent.cClick(); +  </SCRIPT> +  </BODY></HTML> +% } +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Add customer tax adjustment'); + +my $error = ''; +my $conf = new FS::conf; +my $param = $cgi->Vars; + +$param->{"custnum"} =~ /^(\d+)$/ +  or $error .= "Illegal customer number " . $param->{"custnum"} . "  "; +my $custnum = $1; + +$param->{"amount"} =~ /^\s*(\d*(?:\.?\d{1,2}))\s*$/ +  or $error .= "Illegal amount " . $param->{"amount"} . "  "; +my $amount = $1; + +unless ( $error ) { + +  my $cust_tax_adjustment = new FS::cust_tax_adjustment { +    'custnum' => $custnum, +    'taxname' => $param->{'taxname'}, +    'amount'  => $amount, +    'comment' => $param->{'comment'}, +  }; +  $error = $cust_tax_adjustment->insert; + +} + +</%init> diff --git a/httemplate/edit/process/domain_record.cgi b/httemplate/edit/process/domain_record.cgi new file mode 100755 index 000000000..2e427e4fb --- /dev/null +++ b/httemplate/edit/process/domain_record.cgi @@ -0,0 +1,30 @@ +%if ( $error ) { +%  errorpage($error); +%} else {  +%  my $svcnum = $new->svcnum; +<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %> +%} +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice'); + +my $recnum = $cgi->param('recnum'); + +my $old = qsearchs('agent',{'recnum'=>$recnum}) if $recnum; + +my $new = new FS::domain_record ( { +  map { +    $_, scalar($cgi->param($_)); +  } fields('domain_record') +} ); + +my $error; +if ( $recnum ) { +  $error=$new->replace($old); +} else { +  $error=$new->insert; +  $recnum=$new->getfield('recnum'); +} + +</%init> diff --git a/httemplate/edit/process/domreg.cgi b/httemplate/edit/process/domreg.cgi new file mode 100755 index 000000000..a95474e44 --- /dev/null +++ b/httemplate/edit/process/domreg.cgi @@ -0,0 +1,62 @@ +%if ($error) { +%  $cgi->param('error', $error); +%    errorpage($error); +%} else { +<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %> +%} +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('op') =~ /^(register|transfer|revoke|renew)$/ or die "Illegal operation"; +my $operation = $1; +#my($query) = $cgi->keywords; +#$query =~ /^(\d+)$/; +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum = $1; +my $svc_domain = qsearchs({ +  'select'    => 'svc_domain.*', +  'table'     => 'svc_domain', +  'addl_from' => ' LEFT JOIN cust_svc  USING ( svcnum  ) '. +                 ' LEFT JOIN cust_pkg  USING ( pkgnum  ) '. +                 ' LEFT JOIN cust_main USING ( custnum ) ', +  'hashref'   => {'svcnum'=>$svcnum}, +  'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql, +}); +die "Unknown svcnum" unless $svc_domain; + +my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum}); +my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } ); +die "Unknown svcpart" unless $part_svc; + +my $error = ''; + +my @exports = $part_svc->part_export(); + +my $registrar; +my $export; + +# Find the first export that does domain registration +foreach (@exports) { +	$export = $_ if $_->can('registrar'); +} + +my $period = 1; # Current OpenSRS export can only handle 1 year registrations + +# If we have a domain registration export, get the registrar object +if ($export) { +	if ($operation eq 'register') { +		$error = $export->register( $svc_domain, $period ); +	} elsif ($operation eq 'transfer') { +		$error = $export->transfer( $svc_domain ); +	} elsif ($operation eq 'revoke') { +		$error = $export->revoke( $svc_domain ); +	} elsif ($operation eq 'renew') { +		$cgi->param('period') =~ /^(\d+)$/ or die "Illegal renewal period!"; +		$period = $1; +		$error = $export->renew( $svc_domain, $period ); +	} +} + +</%init> diff --git a/httemplate/edit/process/elements/ApplicationCommon.html b/httemplate/edit/process/elements/ApplicationCommon.html new file mode 100644 index 000000000..c7bdd3ea2 --- /dev/null +++ b/httemplate/edit/process/elements/ApplicationCommon.html @@ -0,0 +1,103 @@ +<%doc> + +Examples: + +  #cust_bill_pay +  include('elements/ApplicationCommon.html', +    'error_redirect' => 'cust_bill_pay.cgi', +    'src_table'      => 'cust_pay', +    'src_thing'      => 'payment', +    'link_table'     => 'cust_bill_pay', +  ) + +  #cust_credit_bill +  include('elements/ApplicationCommon.html', +    'error_redirect' => 'cust_credit_bill.cgi', +    'src_table'      => 'cust_credit', +    'src_thing'      => 'credit', +    'link_table'     => 'cust_credit_bill', +  ) + +</%doc> +%if ( $error ) { +%  $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). $opt{error_redirect}. '?'. $cgi->query_string ) %> +%} else { +<% header("$src_thing application$to sucessful") %> +  <SCRIPT TYPE="text/javascript"> +    window.top.location.reload(); +  </SCRIPT> +  </BODY> +  </HTML> +% }  +<%init> + +my %opt = @_; + +my $error = ''; + +my $src_thing = ucfirst($opt{'src_thing'}); +my $src_table = $opt{'src_table'}; +my $src_pkey = dbdef->table($src_table)->primary_key; + +my $to = $opt{'link_table'} =~  /refund/ ? ' to Refund' : ''; + +$cgi->param($src_pkey) =~ /^(\d+)$/ or die "Illegal $src_pkey!"; +my $src_pkeyvalue = $1; + +my $src = qsearchs($src_table, { $src_pkey => $src_pkeyvalue } ) +  or die "No such $src_pkey: $src_pkeyvalue"; + +my $cust_main = qsearchs('cust_main', { 'custnum' => $src->custnum } ) +  or die "Bogus $src_thing: not attached to customer"; + +my $custnum = $cust_main->custnum; + +my @subnames = grep { /.+/ } map { /^subnum(\d+)$/ ? $1 : '' } $cgi->param; +my @subitems = map { [ $cgi->param("subnum$_"), $cgi->param("subamount$_"), $cgi->param("taxXlocationnum$_") ] } +               @subnames; +{ local $^W = 0; @subitems = grep { $_->[1] + 0 } @subitems; } + +my %options = (); +$options{subitems} = \@subitems if scalar(@subitems); + +my $oldAutoCommit = $FS::UID::AutoCommit; +local $FS::UID::AutoCommit = 0; +my $dbh = dbh; +  +my $new; +#  $new = new FS::cust_refund ( { +#    'reason'  => 'Refunding payment', #enter reason in UI +#    'refund'  => $cgi->param('amount'), +#    'payby'   => 'BILL', +#    #'_date'   => $cgi->param('_date'), +#    'payinfo' => 'Cash', #enter payinfo in UI +#    'paynum' => $paynum, +#  } ); +#} else { + +  if ($src->amount != $cgi->param('src_amount')) { +    $src->amount($cgi->param('src_amount')); +    $error = $src->replace; +  } + +  my $class = 'FS::'. $opt{link_table}; + +  $new = $class->new( { +    map { +      $_ => scalar($cgi->param($_)); +    } fields($opt{link_table}) +  } ); + +#} + + +$options{manual} = 1; +$error ||= $new->insert( %options ); + +if ($error) { +  $dbh->rollback if $oldAutoCommit; +} else { +  $dbh->commit or die $dbh->errstr if $oldAutoCommit; +} +</%init> diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html new file mode 100644 index 000000000..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_device.html b/httemplate/edit/process/part_device.html new file mode 100644 index 000000000..2b7e1da49 --- /dev/null +++ b/httemplate/edit/process/part_device.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', +               'table'       => 'part_device', +               'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/part_event.html b/httemplate/edit/process/part_event.html new file mode 100644 index 000000000..428025fd1 --- /dev/null +++ b/httemplate/edit/process/part_event.html @@ -0,0 +1,86 @@ +<% include( 'elements/process.html', +    #'debug'          => 1, +    'table'          => 'part_event', +    'viewall_dir'    => 'browse', +    'process_m2name' => +      { +        'link_table'    => 'part_event_condition', +        'num_col'       => 'eventpart', +        'name_col'      => 'conditionname', +        'names_list'    => [ FS::part_event_condition->all_conditionnames() ], +        'param_style'   => 'name_colN values', +        'args_callback' => sub { # FS/FS/m2name_Common.pm +          my( $object, $prefix, $params, $listref ) = @_; +          #warn "$object $prefix $params $listref\n"; + +          my $cond = $object->conditionname; + +          my %option_fields = $object->option_fields; + +          push @$listref, map { +                                my $field = $_; + +                                my $cgi_field = "$prefix$cond.$field"; + +                                my $value = $params->{$cgi_field}; + +                                my $info = $option_fields{$_}; +                                $info = { label=>$info, type=>'text' } +                                  unless ref($info); + +                                if ( $info->{'type'} =~ +                                       /^(select|checkbox)-?multiple$/ +                                     or $info->{'type'} =~ /^select/ +                                        && $info->{'multiple'} +                                   ) +                                { +                                  #special processing for compound fields +                                  $value = { map { $_ => 1 } +                                                 split(/\0/, $value) +                                           }; +                                } elsif ( $info->{'type'} eq 'freq' ) { +                                  $value .= $params->{$cgi_field.'_units'}; +                                } + +                                #warn "value of $cgi_field is $value\n"; + +                                ( $field => $value ); +                              } +                              keys %option_fields; +        }, +      }, + +    'args_callback' => sub { + +      my( $cgi, $object ) = @_; + +      my $prefix = $object->action.'.'; + +      map { my $option = $_; +            #my $value = scalar( $cgi->param( "$prefix$option" ) ); +            my $value = join(',', $cgi->param( "$prefix$option" ) ); + +            if ( $option eq 'reasonnum' && $value == -1 ) { +              $value = { +                'typenum' => scalar( $cgi->param( "new$prefix${option}T" ) ), +                'reason'  => scalar( $cgi->param( "new$prefix${option}"  ) ), +              }; +            } + +            ( $option => $value ); +          } +          @{ $object->option_fields_listref }; + +    }, + +    'agent_virt'       => 1, +    'agent_null_right' => 'Edit global billing events', +) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Edit billing events') +      || $FS::CurrentUser::CurrentUser->access_right('Edit global billing events'); + +</%init> diff --git a/httemplate/edit/process/part_export.cgi b/httemplate/edit/process/part_export.cgi new file mode 100644 index 000000000..209419f0b --- /dev/null +++ b/httemplate/edit/process/part_export.cgi @@ -0,0 +1,42 @@ +%if ( $error ) { +%  $cgi->param('error', $error ); +<% $cgi->redirect(popurl(2). "part_export.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "browse/part_export.cgi") %> +%} +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $exportnum = $cgi->param('exportnum'); + +my $old = qsearchs('part_export', { 'exportnum'=>$exportnum } ) if $exportnum; + +#fixup options +#warn join('-', split(',',$cgi->param('options'))); +my %options = map { +  my @values = $cgi->param($_); +  my $value = scalar(@values) > 1 ? join (' ', @values) : $values[0]; +  $value =~ s/\r\n/\n/g; #browsers? (textarea) +  $_ => $value; +} split(',', $cgi->param('options')); + +my $new = new FS::part_export ( { +  map { +    $_, scalar($cgi->param($_)); +  } fields('part_export') +} ); + +my $error; +if ( $exportnum ) { +  #warn $old; +  #warn $exportnum; +  #warn $new->machine; +  $error = $new->replace($old,\%options); +} else { +  $error = $new->insert(\%options); +#  $exportnum = $new->exportnum; +} + +</%init> diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi new file mode 100755 index 000000000..019224c4e --- /dev/null +++ b/httemplate/edit/process/part_pkg.cgi @@ -0,0 +1,222 @@ +<% include( 'elements/process.html', +              #'debug'             => 1, +              'table'             => 'part_pkg', +              'agent_virt'        => 1, +              'agent_null_right'  => \@agent_null_right, +              'redirect'          => $redirect_callback, +              'viewall_dir'       => 'browse', +              'viewall_ext'       => 'cgi', +              'edit_ext'          => 'cgi', +              'precheck_callback' => $precheck_callback, +              'args_callback'     => $args_callback, +              'process_m2m'       => \@process_m2m, +          ) +%> +<%init> + +my $customizing = ( ! $cgi->param('pkgpart') && $cgi->param('pkgnum') ); + +my $curuser = $FS::CurrentUser::CurrentUser; + +my $edit_global = 'Edit global package definitions'; +my $customize   = 'Customize customer package'; + +die "access denied" +  unless $curuser->access_right('Edit package definitions') +      || $curuser->access_right($edit_global) +      || ( $customizing && $curuser->access_right($customize) ); + +my @agent_null_right = ( $edit_global ); +push @agent_null_right, $customize if $customizing; + + +my $precheck_callback = sub { +  my( $cgi ) = @_; + +  my $conf = new FS::Conf; + +  foreach (qw( setuptax recurtax disabled )) { +    $cgi->param($_, '') unless defined $cgi->param($_); +  } + +  return 'Must select a tax class' +    if $cgi->param('taxclass') eq '(select)'; + +  my @agents = (); +  foreach ($cgi->param('agent_type')) { +    /^(\d+)$/; +    push @agents, $1 if $1; +  } +  return "At least one agent type must be specified." +    unless scalar(@agents) +           || ( $cgi->param('clone') && $cgi->param('clone') =~ /^\d+$/ ) +           || ( !$cgi->param('pkgpart') && $conf->exists('agent-defaultpkg') ) +           || $cgi->param('disabled') +           || $cgi->param('agentnum'); + +  return ''; + +}; + +my $custnum = ''; + +my $args_callback = sub { +  my( $cgi, $new ) = @_; +   +  my @args = ( 'primary_svc' => scalar($cgi->param('pkg_svc_primary')) ); + +  ## +  #options +  ## +   +  $cgi->param('plan') =~ /^(\w+)$/ or die 'unparsable plan'; +  my $plan = $1; +   +  tie my %plans, 'Tie::IxHash', %{ FS::part_pkg::plan_info() }; +  my $href = $plans{$plan}->{'fields'}; +   +  my $error = ''; +  my $options = $cgi->param($plan."__OPTIONS"); +  my @options = split(',', $options); +  my %options = +    map { my $optionname = $_; +          my $param = $plan."__$optionname"; +          my $parser = exists($href->{$optionname}{parse}) +                         ? $href->{$optionname}{parse} +                         : sub { shift }; +          my $value = join(', ', &$parser($cgi->param($param))); +          my $check = $href->{$optionname}{check}; +          if ( $check && ! &$check($value) ) { +            $value = join(', ', $cgi->param($param)); +            $error ||= "Illegal ". +                         ($href->{$optionname}{name}||$optionname). ": $value"; +          } +          ( $optionname => $value ); +        } +        grep { $_ !~ /^report_option_/ } +        @options; + +  foreach ( split(',', $cgi->param('taxproductnums') ) ) { +    my $value = $cgi->param("taxproductnum_$_"); +    $error ||= "Illegal taxproductnum_$_: $value" +      unless ( $value =~ /^\d*$/  ); +    $options{"usage_taxproductnum_$_"} = $value; +  } + +  foreach ( $cgi->param('report_option') ) { +    $error ||= "Illegal optional report class: $_" unless ( $_ =~ /^\d*$/  ); +    $options{"report_option_$_"} = 1; +  } + +  $options{$_} = scalar( $cgi->param($_) ) +    for (qw( setup_fee recur_fee )); +   +  push @args, 'options' => \%options; + +  ### +  #pkg_svc +  ### + +  my %pkg_svc = map { $_ => scalar($cgi->param("pkg_svc$_")) } +                map { $_->svcpart } +                qsearch('part_svc', {} ); + +  push @args, 'pkg_svc' => \%pkg_svc; + +  ### +  # cust_pkg and custnum_ref (inserts only) +  ### +  unless ( $cgi->param('pkgpart') ) { +    push @args, 'cust_pkg'    => scalar($cgi->param('pkgnum')), +                'custnum_ref' => \$custnum; +  } + +  warn "args: ".join('/', @args). "\n"; + +  @args; + +}; + +my $redirect_callback = sub { +  #my( $cgi, $new ) = @_; +  return '' unless $custnum; +  my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/ +               ? '' +               : ';show=packages'; +  #my $frag = "cust_pkg$pkgnum"; #hack for IE ignoring real #fragment +  +  #can we link back to the specific customized package?  it would be nice... +  popurl(3). "view/cust_main.cgi?custnum=$custnum$show;dummy="; +}; + +#these should probably move to @args above and be processed by part_pkg.pm... + +$cgi->param('tax_override') =~ /^([\d,]+)$/; +my (@tax_overrides) = (grep "$_", split (",", $1)); + +my @process_m2m = ( +  { +    'link_table'   => 'part_pkg_taxoverride', +    'target_table' => 'tax_class', +    'params'       => \@tax_overrides, +  }, +  { 'link_table'   => 'part_pkg_link', +    'target_table' => 'part_pkg', +    'base_field'   => 'src_pkgpart', +    'target_field' => 'dst_pkgpart', +    'hashref'      => { 'link_type' => 'svc', 'hidden' => '' }, +    'params'       => [ map $cgi->param($_), +                        grep /^svc_dst_pkgpart/, $cgi->param +                      ], +  }, +  map {  +    my $hidden = $_; +    { 'link_table'   => 'part_pkg_link', +      'target_table' => 'part_pkg', +      'base_field'   => 'src_pkgpart', +      'target_field' => 'dst_pkgpart', +      'hashref'      => { 'link_type' => 'bill', 'hidden' => $hidden }, +      'params'       => [ map { $cgi->param($_) } +                          grep { my $param = "bill_dst_pkgpart__hidden"; +                                 my $digit = ''; +                                 (($digit) = /^bill_dst_pkgpart(\d+)/ ) && +                                 $cgi->param("$param$digit") eq $hidden; +                               } +                          $cgi->param +                        ], +    }, +  } ( '', 'Y' ), +); + +foreach my $override_class ($cgi->param) { +  next unless $override_class =~ /^tax_override_(\w+)$/; +  my $class = $1; + +  my (@tax_overrides) = (grep "$_", split (",", $1)) +    if $cgi->param($override_class) =~ /^([\d,]+)$/; + +  push @process_m2m, { +    'link_table'   => 'part_pkg_taxoverride', +    'target_table' => 'tax_class', +    'hashref'      => { 'usage_class' => $class }, +    'params'       => [ @tax_overrides ], +  }; + +} + +my $conf = new FS::Conf; + +if ( $cgi->param('pkgpart') || ! $conf->exists('agent_defaultpkg') ) { +  my @agents = (); +  foreach ($cgi->param('agent_type')) { +    /^(\d+)$/; +    push @agents, $1 if $1; +  } +  push @process_m2m, { +    'link_table'   => 'type_pkgs', +    'target_table' => 'agent_type', +    'params'       => \@agents, +  }; +} + +</%init> diff --git a/httemplate/edit/process/part_pkg_report_option.html b/httemplate/edit/process/part_pkg_report_option.html new file mode 100644 index 000000000..052aabd72 --- /dev/null +++ b/httemplate/edit/process/part_pkg_report_option.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', +               'table'       => 'part_pkg_report_option', +               'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/part_pkg_taxclass.html b/httemplate/edit/process/part_pkg_taxclass.html new file mode 100644 index 000000000..b37279fb3 --- /dev/null +++ b/httemplate/edit/process/part_pkg_taxclass.html @@ -0,0 +1,17 @@ +<% include( 'elements/process.html', +              'table'    => 'part_pkg_taxclass', +              'redirect' => sub { +                my( $cgi, $part_pkg_taxclass ) = @_; + +                popurl(3). 'browse/cust_main_county.cgi?'. +                  'taxclass='. uri_escape($part_pkg_taxclass->taxclass). +                  ';dummy='; +              }, +          ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/part_referral.html b/httemplate/edit/process/part_referral.html new file mode 100755 index 000000000..40cbc97bf --- /dev/null +++ b/httemplate/edit/process/part_referral.html @@ -0,0 +1,12 @@ +<% include( 'elements/process.html', +                 'table'       => 'part_referral', +                 'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Edit advertising sources') +      || $FS::CurrentUser::CurrentUser->access_right('Edit global advertising sources'); + +</%init> diff --git a/httemplate/edit/process/part_svc.cgi b/httemplate/edit/process/part_svc.cgi new file mode 100755 index 000000000..65de3fc6c --- /dev/null +++ b/httemplate/edit/process/part_svc.cgi @@ -0,0 +1,9 @@ +<% $server->process %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $server = new FS::UI::Web::JSRPC 'FS::part_svc::process', $cgi; + +</%init> diff --git a/httemplate/edit/process/payment_gateway.html b/httemplate/edit/process/payment_gateway.html new file mode 100644 index 000000000..812c988c5 --- /dev/null +++ b/httemplate/edit/process/payment_gateway.html @@ -0,0 +1,22 @@ +<% include( 'elements/process.html', +            'table'         => 'payment_gateway', +            'viewall_dir'   => 'browse', +            'args_callback' => $args_callback, +          ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $args_callback = sub { +  my ( $cgi, $new ) = @_; + +  my @options = split(/\r?\n/, $cgi->param('gateway_options') ); +  pop @options +    if scalar(@options) % 2 && $options[-1] =~ /^\s*$/; +  (@options) +}; + + +</%init> diff --git a/httemplate/edit/process/phone_device.html b/httemplate/edit/process/phone_device.html new file mode 100644 index 000000000..df9d5e793 --- /dev/null +++ b/httemplate/edit/process/phone_device.html @@ -0,0 +1,18 @@ +<% include( 'elements/process.html', +               'table'    => 'phone_device', +               'redirect' => sub { +                 my( $cgi, $phone_device ) = @_; +                 popurl(3).'view/svc_phone.cgi?'. +                   'svcnum='. $phone_device->svcnum. +                   ';devicenum='; +               }, +           ) +%> +<%init> + +# :/  needs agent-virt so you can't futz with arbitrary devices + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +</%init> diff --git a/httemplate/edit/process/pkg_category.html b/httemplate/edit/process/pkg_category.html new file mode 100644 index 000000000..50cd5cb29 --- /dev/null +++ b/httemplate/edit/process/pkg_category.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', +               'table'       => 'pkg_category', +               'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/pkg_class.html b/httemplate/edit/process/pkg_class.html new file mode 100644 index 000000000..b196df3f7 --- /dev/null +++ b/httemplate/edit/process/pkg_class.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', +               'table'       => 'pkg_class', +               'viewall_dir' => 'browse', +           ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/prepay_credit.cgi b/httemplate/edit/process/prepay_credit.cgi new file mode 100644 index 000000000..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..827530e50 --- /dev/null +++ b/httemplate/edit/process/quick-charge.cgi @@ -0,0 +1,74 @@ +% if ( $error ) { +%   $cgi->param('error', $error ); +<% $cgi->redirect($p.'quick-charge.html?'. $cgi->query_string) %> +% } else { +<% header("One-time charge added") %> +  <SCRIPT TYPE="text/javascript"> +    window.top.location.reload(); +  </SCRIPT> +  </BODY></HTML> +% } +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('One-time charge'); + +my $error = ''; +my $conf = new FS::conf; +my $param = $cgi->Vars; + +my @description = (); +for ( my $row = 0; exists($param->{"description$row"}); $row++ ) { +  push @description, $param->{"description$row"} +    if ($param->{"description$row"} =~ /\S/); +} + +$param->{"custnum"} =~ /^(\d+)$/ +  or $error .= "Illegal customer number " . $param->{"custnum"} . "  "; +my $custnum = $1; + +$param->{"amount"} =~ /^\s*(\d*(?:\.?\d{1,2}))\s*$/ +  or $error .= "Illegal amount " . $param->{"amount"} . "  "; +my $amount = $1; + +my $quantity = 1; +if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) { +  $quantity = $1; +} + +$param->{'tax_override'} =~ /^\s*([,\d]*)\s*$/ +  or $error .= "Illegal tax override " . $param->{"tax_override"} . "  "; +my $override = $1; + +if ( $param->{'taxclass'} eq '(select)' ) { +  $error .= "Must select a tax class.  " +    unless ($conf->exists('enable_taxproducts') && +             ( $override || $param->{taxproductnum} ) +           ); +  $cgi->param('taxclass', ''); +} + +unless ( $error ) { +  my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ) +    or $error .= "Unknown customer number $custnum.  "; + +  $error ||= $cust_main->charge( { +    'amount'        => $amount, +    'quantity'      => $quantity, +    'bill_now'      => scalar($cgi->param('bill_now')), +    'invoice_terms' => scalar($cgi->param('invoice_terms')), +    'start_date'    => ( scalar($cgi->param('start_date')) +                           ? str2time($cgi->param('start_date')) +                           : '' +                       ), +    '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..7a0f08280 --- /dev/null +++ b/httemplate/edit/process/quick-cust_pkg.cgi @@ -0,0 +1,72 @@ +%if ($error) { +%  $cgi->param('error', $error); +<% $cgi->redirect(popurl(3). 'misc/order_pkg.html?'. $cgi->query_string ) %> +%} else { +%  my $frag = "cust_pkg". $cust_pkg->pkgnum; +%  my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/ +%               ? '' +%               : ';show=packages'; +<% header('Package ordered') %> +  <SCRIPT TYPE="text/javascript"> +    // XXX fancy ajax rebuild table at some point, but a page reload will do for now + +    // XXX chop off trailing #target and replace... ? +    window.top.location = '<% popurl(3). "view/cust_main.cgi?custnum=$custnum$show;fragment=$frag#$frag" %>'; + +  </SCRIPT> + +  </BODY></HTML> +%} +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Order customer package'); + +#untaint custnum (probably not necessary, searching for it is escape enough) +$cgi->param('custnum') =~ /^(\d+)$/ +  or die 'illegal custnum '. $cgi->param('custnum'); +my $custnum = $1; +my $cust_main = qsearchs({ +  'table'     => 'cust_main', +  'hashref'   => { 'custnum' => $custnum }, +  'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql, +}); +die 'unknown custnum' unless $cust_main; + +#probably not necessary, taken care of by cust_pkg::check +$cgi->param('pkgpart') =~ /^(\d+)$/ +  or die 'illegal pkgpart '. $cgi->param('pkgpart'); +my $pkgpart = $1; +$cgi->param('refnum') =~ /^(\d*)$/ +  or die 'illegal refnum '. $cgi->param('refnum'); +my $refnum = $1; +$cgi->param('locationnum') =~ /^(\-?\d*)$/ +  or die 'illegal locationnum '. $cgi->param('locationnum'); +my $locationnum = $1; + +my $cust_pkg = new FS::cust_pkg { +  'custnum'     => $custnum, +  'pkgpart'     => $pkgpart, +  'start_date'  => ( scalar($cgi->param('start_date')) +                       ? str2time($cgi->param('start_date')) +                       : '' +                   ), +  '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..c19c2a51f --- /dev/null +++ b/httemplate/edit/process/svc_acct.cgi @@ -0,0 +1,67 @@ +%if ( $error ) { +%  $cgi->param('error', $error); +<% $cgi->redirect(popurl(2). "svc_acct.cgi?". $cgi->query_string ) %> +%} else { +<% $cgi->redirect(popurl(3). "view/svc_acct.cgi?" . $svcnum ) %> +%} +<%init> +use CGI::Carp; +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +$cgi->param('svcnum') =~ /^(\d*)$/ or die "Illegal svcnum!"; +my $svcnum = $1; + +my $old; +if ( $svcnum ) { +  $old = qsearchs('svc_acct', { 'svcnum' => $svcnum } ) +    or die "fatal: can't find account (svcnum $svcnum)!"; +} else { +  $old = ''; +} + +#unmunge popnum +$cgi->param('popnum', (split(/:/, $cgi->param('popnum') ))[0] ); + +#unmunge usergroup +$cgi->param('usergroup', [ $cgi->param('radius_usergroup') ] ); + +#unmunge bytecounts +foreach (map { $_,$_."_threshold" } qw( upbytes downbytes totalbytes )) { +  $cgi->param($_, FS::UI::bytecount::parse_bytecount($cgi->param($_)) ); +} + +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 ); + +$new->_password($old->_password) if $old; +if(  $cgi->param('clear_password') eq '*HIDDEN*' +  or $cgi->param('clear_password') =~ /^\(.* encrypted\)$/ ) { +  die "fatal: no previous account to recall hidden password from!" unless $old; +}  +else { +  $new->set_password($cgi->param('clear_password')); +} + +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..59b518097 --- /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) +  } ( fields('svc_domain'), qw( pkgnum svcpart action ) ) +} ); + +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..c96fa6c81 --- /dev/null +++ b/httemplate/edit/quick-charge.html @@ -0,0 +1,286 @@ +<% include("/elements/header-popup.html", 'One-time charge', '', +            ( $cgi->param('error') ? '' : 'onload="addRow()"' ), +          ) +%> + +<LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2"> +<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar_stripped.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-en.js"></SCRIPT> +<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT> + +<% include('/elements/error.html') %> + +<SCRIPT TYPE="text/javascript"> + +function enable_quick_charge () { +  if (    document.QuickChargeForm.amount.value +       && document.QuickChargeForm.pkg.value    ) { +    document.QuickChargeForm.submit.disabled = false; +  } else { +    document.QuickChargeForm.submit.disabled = true; +  } +} + +function validate_quick_charge () { +  var pkg = document.QuickChargeForm.pkg.value; +  var pkg_regex = /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ ; +  var amount = document.QuickChargeForm.amount.value; +  var amount_regex = /^\s*\$?\s*(\d*(\.?\d{1,2}))\s*$/ ; +  var rval = true; + +  if ( ! amount_regex.test(amount) ) { +    alert('Illegal amount - enter an amount to charge, for example, "5" or "43" or "21.46".'); +    return false; +  } +  if ( String(pkg).length < 1 ) { +    rval = false; +  } +  if ( ! pkg_regex.test(pkg) ) { +    rval = false; +  } +  var i=0; +  for (i=0; i < rownum; i++) { +    if (! eval('pkg_regex.test(document.QuickChargeForm.description' + i + '.value)')){ +      rval = false; +      break; +    } +  } +  if (rval == true) { +    return true; +  } + +  if ( ! pkg ) { +    alert('Enter a description for the one-time charge'); +    return false; +  } + +  alert('Illegal description - spaces, letters, numbers, and the following punctuation characters are allowed: . , ! ? @ # $ % & ( ) - + ; : ' + "'" + ' " = [ ]' ); +  return false; +} + +function bill_now_changed (what) { +  var form = what.form; +  if ( what.checked ) { +    form.start_date_text.disabled = true; +    form.start_date.style.backgroundColor = '#dddddd'; +    form.start_date_button.style.display = 'none'; +    form.start_date_button_disabled.style.display = ''; +    form.invoice_terms.disabled = false; +  } else { +    form.start_date_text.disabled = false; +    form.start_date.style.backgroundColor = '#ffffff'; +    form.start_date_button.style.display = ''; +    form.start_date_button_disabled.style.display = 'none'; +    form.invoice_terms.disabled = true; +  } +} + +</SCRIPT> + +<FORM ACTION="process/quick-charge.cgi" NAME="QuickChargeForm" ID="QuickChargeForm" METHOD="POST" onsubmit="document.QuickChargeForm.submit.disabled=true;return validate_quick_charge();"> + +<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> + +<TABLE ID="QuickChargeTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 STYLE="background-color: #cccccc"> + +<TR> +  <TD ALIGN="right">Amount </TD> +  <TD> +    $<INPUT TYPE="text" NAME="amount" SIZE=6 VALUE="<% $amount %>" onChange="enable_quick_charge()" onKeyPress="enable_quick_charge()"> +  </TD> +</TR> + +% if ( $conf->exists('invoice-unitprice') ) { +    <TR> +      <TD ALIGN="right">Quantity </TD> +      <TD> +        <INPUT TYPE="text" NAME="quantity" SIZE=4 VALUE="<% $quantity %>"> +      </TD> +    </TR> +% } + +<% include('/elements/tr-select-pkg_class.html', 'curr_value' => $cgi->param('classnum') ) %> + +<TR> +  <TD ALIGN="right">Invoice now</TD> +  <TD> +    <INPUT TYPE  = "checkbox" +           NAME  = "bill_now" +           VALUE = "1" +           <% $cgi->param('bill_now') ? 'CHECKED' : '' %> +           onChange = "bill_now_changed(this);" +    > +    with terms  +    <% include('/elements/select-terms.html', +                 'curr_value'  => scalar($cgi->param('invoice_terms')), +                 'empty_value' => $default_terms, +                 'disabled'    => ( $cgi->param('bill_now') ? 0 : 1 ), +              ) +    %> +  </TD> +</TR> + +%# false laziness w/misc/order_pkg.html +<TR> +  <TD ALIGN="right">Charge date </TD> +  <TD> +    <INPUT TYPE  = "text" +           NAME  = "start_date" +           SIZE  = 32 +           ID    = "start_date_text" +           VALUE = "<% $start_date %>" +           <% $cgi->param('bill_now') ? 'STYLE = "background-color:#dddddd" DISABLED' : '' %> +    > +    <IMG SRC   = "<%$fsurl%>images/calendar.png" +         ID    = "start_date_button" +         TITLE = "Select date" +         STYLE = "cursor:pointer<% $cgi->param('bill_now') ? ';display:none' : '' %>" +    > +    <IMG SRC   = "<%$fsurl%>images/calendar-disabled.png" +         ID    = "start_date_button_disabled" +         <% $cgi->param('bill_now') ? '' : 'STYLE="display:none"' %> +    > +    <FONT SIZE=-1>(leave blank to charge immediately)</FONT> +  </TD> +</TR> + +<SCRIPT TYPE="text/javascript"> +  Calendar.setup({ +    inputField: "start_date_text", +    ifFormat:   "%m/%d/%Y", +    button:     "start_date_button", +    align:      "BR" +  }); +</SCRIPT> + + +<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 $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } ); #XXX agent-virt + +my $format = "%m/%d/%Y %T %z (%Z)"; #false laziness w/REAL_cust_pkg.cgi? +my $start_date = $cust_main->next_bill_date; +$start_date = $start_date ? time2str($format, $start_date) : ''; + +my $amount = ''; +if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) { +  $amount = $1; +} + +my $quantity = 1; +if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) { +  $quantity = $1; +} + +$cgi->param('pkg') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/  +  or die 'illegal description'; +my $pkg = $1; + +my $default_terms; +if ( $cust_main->invoice_terms ) { +  $default_terms = 'Customer default ('. $cust_main->invoice_terms. ')'; +} else { +  $default_terms = +    'Default ('. +       ($conf->config('invoice_default_terms') || 'Payable upon receipt'). +    ')'; +} + +</%init> diff --git a/httemplate/edit/rate.cgi b/httemplate/edit/rate.cgi new file mode 100644 index 000000000..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..76790ab02 --- /dev/null +++ b/httemplate/edit/reg_code.cgi @@ -0,0 +1,44 @@ +<% include('/elements/header.html', 'Generate registration codes for '. $agent->agent) %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%popurl(1)%>process/reg_code.cgi" METHOD="POST" NAME="OneTrueForm" onSubmit="document.OneTrueForm.submit.disabled=true"> +<INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agent->agentnum %>"> + +Generate +% my $num = ''; +% if ( $cgi->param('num') =~ /^\s*(\d+)\s*$/ ) { +%   $num = $1; +% } +<INPUT TYPE="text" NAME="num" VALUE="<% $num %>" SIZE=5 MAXLENGTH=4> +registration codes for <B><% $agent->agent %></B> allowing the following packages: +<BR><BR> + +% foreach my $part_pkg ( qsearch('part_pkg', { 'disabled' => '' } ) ) {  +%   my $pkgpart = $part_pkg->pkgpart; + +    <INPUT TYPE="checkbox" NAME="pkgpart<% $pkgpart %>" <% $cgi->param("pkgpart$pkgpart") ? 'CHECKED' : '' %>> +    <% $part_pkg->pkg_comment %> +    <BR> + +% }  + + +<BR> +<INPUT TYPE="submit" NAME="submit" VALUE="Generate"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $agentnum = $cgi->param('agentnum'); +$agentnum =~ /^(\d+)$/ or errorpage("illegal agentnum $agentnum"); +$agentnum = $1; +my $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); + +</%init> diff --git a/httemplate/edit/router.cgi b/httemplate/edit/router.cgi new file mode 100755 index 000000000..70eaa4576 --- /dev/null +++ b/httemplate/edit/router.cgi @@ -0,0 +1,54 @@ +<% include('elements/edit.html', +     'post_url'    => popurl(1).'process/router.cgi', +     'name'        => 'router', +     'table'       => 'router', +     'viewall_url' => "${p}browse/router.cgi", +     'labels'      => { 'routernum'  => 'Router', +                        'routername' => 'Name', +                        'svc_part'   => 'Service', +                      }, +     'fields'      => [ +                        { 'field'=>'routername', 'type'=>'text', 'size'=>32 }, +                        { 'field'=>'agentnum',   'type'=>'select-agent' }, +                        { 'field'=>'svcnum',     'type'=>'hidden' }, +                      ], +     'error_callback' => $callback, +     'edit_callback'  => $callback, +     'new_callback'   => $callback, +     'html_table_bottom' => $html_table_bottom, +   ) +%> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Broadband configuration') +    || $curuser->access_right('Broadband global configuration'); + +my $callback = sub { +  my ($cgi, $object, $fields) = (shift, shift, shift); +  unless ($object->svcnum) { +    push @{$fields}, +      { 'type'          => 'tablebreak-tr-title', +        'value'         => 'Select the service types available on this router', +      }, +      { 'field'         => 'svc_part', +        'type'          => 'checkboxes-table', +        'target_table'  => 'part_svc', +        'link_table'    => 'part_svc_router', +        'name_col'      => 'svc', +        'hashref'       => { 'svcdb' => 'svc_broadband', 'disabled' => '' }, +      }; +  } +}; + +my $html_table_bottom = sub { +  my $router = shift; +  my $html = ''; +  foreach my $field ($router->virtual_fields) { +    $html .= $router->pvf($field)->widget('HTML', 'edit', $router->get($field)); +  } +  $html; +}; +</%init> diff --git a/httemplate/edit/svc_Common.html b/httemplate/edit/svc_Common.html new file mode 100644 index 000000000..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..9c3e8de03 --- /dev/null +++ b/httemplate/edit/svc_acct.cgi @@ -0,0 +1,476 @@ +<% include('/elements/header.html', "$action $svc account") %> + +<% include('/elements/error.html') %> + +% if ( $cust_main ) {  + +  <% include( '/elements/small_custview.html', $cust_main, '', 1, +              popurl(2) . "view/cust_main.cgi") %> +  <BR> +% }  + +<SCRIPT TYPE="text/javascript"> +function randomPass() { +  var i=0; +  var pw_set='<% join('', 'a'..'z', 'A'..'Z', '0'..'9', '.', '/') %>'; +  var pass=''; +  while(i < 8) { +    i++; +    pass += pw_set.charAt(Math.floor(Math.random() * pw_set.length)); +  } +  document.OneTrueForm.clear_password.value = pass; +} +</SCRIPT> + +<FORM NAME="OneTrueForm" ACTION="<% $p1 %>process/svc_acct.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>"> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>"> + +Service # <% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR> + +<% ntable("#cccccc",2) %> + +<TR> +  <TD ALIGN="right">Service</TD> +  <TD BGCOLOR="#eeeeee"><% $part_svc->svc %></TD> +</TR> + +<TR> +  <TD ALIGN="right">Username</TD> +  <TD> +    <INPUT TYPE="text" NAME="username" VALUE="<% $username %>" SIZE=<% $ulen2 %> MAXLENGTH=<% $ulen %>> +  </TD> +</TR> + +%if ( $part_svc->part_svc_column('_password')->columnflag ne 'F' ) { +<TR> +  <TD ALIGN="right">Password</TD> +  <TD> +    <INPUT TYPE="text" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>> +    <INPUT TYPE="button" VALUE="Randomize" onclick="randomPass();"> +  </TD> +</TR> +%}else{ +    <INPUT TYPE="hidden" NAME="clear_password" VALUE="<% $password %>"> +%} +<INPUT TYPE="hidden" NAME="_password_encoding" VALUE="<% $password_encoding %>"> +% +%my $sec_phrase = $svc_acct->sec_phrase; +%if ( $conf->exists('security_phrase')  +%  && $part_svc->part_svc_column('sec_phrase')->columnflag ne 'F' ) { +% + + +  <TR> +    <TD ALIGN="right">Security phrase</TD> +    <TD> +      <INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $sec_phrase %>" SIZE=32> +      (for forgotten passwords) +    </TD> +  </TD> +% } else {  + + +  <INPUT TYPE="hidden" NAME="sec_phrase" VALUE="<% $sec_phrase %>"> +% }  +% +%#domain +%my $domsvc = $svc_acct->domsvc || 0; +%if ( $part_svc->part_svc_column('domsvc')->columnflag eq 'F' ) { +% + + +  <INPUT TYPE="hidden" NAME="domsvc" VALUE="<% $domsvc %>"> +% } else {  +% +%  my %svc_domain = (); +% +%  if ( $domsvc ) { +%    my $svc_domain = qsearchs('svc_domain', { 'svcnum' => $domsvc, } ); +%    if ( $svc_domain ) { +%      $svc_domain{$svc_domain->svcnum} = $svc_domain; +%    } else { +%      warn "unknown svc_domain.svcnum for svc_acct.domsvc: $domsvc"; +%    } +%  } +% +%  %svc_domain = (%svc_domain, +%                 domain_select_hash FS::svc_acct('svcpart' => $svcpart, +%                                                 'pkgnum'  => $pkgnum, +%                                                ) +%                ); +% + + +  <TR> +    <TD ALIGN="right">Domain</TD> +    <TD> +      <SELECT NAME="domsvc" SIZE=1> +% foreach my $svcnum ( +%             sort { $svc_domain{$a} cmp $svc_domain{$b} } +%                  keys %svc_domain +%           ) { +%             my $svc_domain = $svc_domain{$svcnum}; +%         + + +             <OPTION VALUE="<% $svcnum %>" <% $svcnum == $domsvc ? ' SELECTED' : '' %>><% $svc_domain{$svcnum} %> +% }  + +      </SELECT> +    </TD> +  </TR> +% }  +% +%#pop +%my $popnum = $svc_acct->popnum || 0; +%if ( $part_svc->part_svc_column('popnum')->columnflag eq 'F' ) { +% + + +  <INPUT TYPE="hidden" NAME="popnum" VALUE="<% $popnum %>"> +% } else {  + + +  <TR> +    <TD ALIGN="right">Access number</TD> +    <TD><% FS::svc_acct_pop::popselector($popnum) %></TD> +  </TR> +% }  +% #uid/gid  +% foreach my $xid (qw( uid gid )) {  +% +%  if ( $part_svc->part_svc_column($xid)->columnflag =~ /^[FA]$/ +%       || ! $conf->exists("svc_acct-edit_$xid") +%     ) { +%   +% if ( length($svc_acct->$xid()) ) {  + +   +      <TR> +        <TD ALIGN="right"><% uc($xid) %></TD> +          <TD BGCOLOR="#eeeeee"><% $svc_acct->$xid() %></TD> +        <TD> +        </TD> +      </TR> +% }  + +   +    <INPUT TYPE="hidden" NAME="<% $xid %>" VALUE="<% $svc_acct->$xid() %>"> +% } else {  + +   +    <TR> +      <TD ALIGN="right"><% uc($xid) %></TD> +      <TD> +        <INPUT TYPE="text" NAME="<% $xid %>" SIZE=8 MAXLENGTH=6 VALUE="<% $svc_acct->$xid() %>"> +      </TD> +    </TR> +% }  +% }  +% +%#finger +%if ( $part_svc->part_svc_column('uid')->columnflag eq 'F' +%     && ! $svc_acct->finger ) {  +% + + +  <INPUT TYPE="hidden" NAME="finger" VALUE=""> +% } else {  + + +  <TR> +    <TD ALIGN="right">Real Name</TD> +    <TD> +      <INPUT TYPE="text" NAME="finger" VALUE="<% $svc_acct->finger %>"> +    </TD> +  </TR> +% }  +% +%#dir +%if ( $part_svc->part_svc_column('dir')->columnflag eq 'F' +%     || !$curuser->access_right('Edit home dir') +%   ) {  + + +<INPUT TYPE="hidden" NAME="dir" VALUE="<% $svc_acct->dir %>"> +% } else { + + +  <TR> +    <TD ALIGN="right">Home directory</TD> +    <TD><INPUT TYPE="text" NAME="dir" VALUE="<% $svc_acct->dir %>"></TD> +  </TR> +% }  +% +%#shell +%my $shell = $svc_acct->shell; +%if ( $part_svc->part_svc_column('shell')->columnflag eq 'F' +%     || ( !$shell && $part_svc->part_svc_column('uid')->columnflag eq 'F' ) +%   ) { +% + + +  <INPUT TYPE="hidden" NAME="shell" VALUE="<% $shell %>"> +% } else {  + + +  <TR> +    <TD ALIGN="right">Shell</TD> +    <TD> +      <SELECT NAME="shell" SIZE=1> +% +%           my($etc_shell); +%           foreach $etc_shell (@shells) { +%         + + +          <OPTION<% $etc_shell eq $shell ? ' SELECTED' : '' %>><% $etc_shell %> +% }  + + +      </SELECT> +    </TD> +  </TR> +% }  +% if ( $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; +my $password_encryption = $svc_acct->_password_encryption; +my $password_encoding = $svc_acct->_password_encoding; + +if($svcnum) { +  if($password = $svc_acct->get_cleartext_password) { +    if (! $conf->exists('showpasswords')) { +        $password = '*HIDDEN*'; +    } +  } +  elsif($svc_acct->_password and $password_encryption ne 'plain') { +    $password = "(".uc($password_encryption)." encrypted)"; +  } +  else { +    $password = ''; +  } +} + +my $ulen =  +  $conf->exists('usernamemax') +  ? $conf->config('usernamemax') +  : dbdef->table('svc_acct')->column('username')->length; +my $ulen2 = $ulen+2; + +my $pmax = max($conf->config('passwordmax') || 13); +my $pmax2 = $pmax+2; + +my $p1 = popurl(1); + +sub max { +  (sort(@_))[-1] +} + +</%init> diff --git a/httemplate/edit/svc_acct_pop.cgi b/httemplate/edit/svc_acct_pop.cgi new file mode 100755 index 000000000..5930a38be --- /dev/null +++ b/httemplate/edit/svc_acct_pop.cgi @@ -0,0 +1,53 @@ +<% include('/elements/header.html', "$action Access Number", menubar( +     'View all Access Numbers' => popurl(2). "browse/svc_acct_pop.cgi", +   )) +%> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/svc_acct_pop.cgi" METHOD=POST> + +<INPUT TYPE="hidden" NAME="popnum" VALUE="<% $hashref->{popnum} %>"> +Access Number #<% $hashref->{popnum} ? $hashref->{popnum} : "(NEW)" %> + +<PRE> +City      <INPUT TYPE="text" NAME="city" SIZE=32 VALUE="<% $hashref->{city} %>"> +State     <INPUT TYPE="text" NAME="state" SIZE=16 MAXLENGTH=16 VALUE="<% $hashref->{state} %>"> +Area Code <INPUT TYPE="text" NAME="ac" SIZE=4 MAXLENGTH=3 VALUE="<% $hashref->{ac} %>"> +Exchange  <INPUT TYPE="text" NAME="exch" SIZE=4 MAXLENGTH=3 VALUE="<% $hashref->{exch} %>"> +Local     <INPUT TYPE="text" NAME="loc" SIZE=5 MAXLENGTH=4 VALUE="<% $hashref->{loc} %>"> +</PRE> + +<BR> +<INPUT TYPE="submit" VALUE="<% $hashref->{popnum} ? "Apply changes" : "Add Access Number" %>"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" +  unless $curuser->access_right('Dialup configuration') +      || $curuser->access_right('Dialup global configuration'); + +my $svc_acct_pop; +if ( $cgi->param('error') ) { +  $svc_acct_pop = new FS::svc_acct_pop ( { +    map { $_, scalar($cgi->param($_)) } fields('svc_acct_pop') +  } ); +} elsif ( $cgi->keywords ) { #editing +  my($query)=$cgi->keywords; +  $query =~ /^(\d+)$/; +  $svc_acct_pop=qsearchs('svc_acct_pop',{'popnum'=>$1}); +} else { #adding +  $svc_acct_pop = new FS::svc_acct_pop {}; +} +my $action = $svc_acct_pop->popnum ? 'Edit' : 'Add'; +my $hashref = $svc_acct_pop->hashref; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi new file mode 100644 index 000000000..8a108f891 --- /dev/null +++ b/httemplate/edit/svc_broadband.cgi @@ -0,0 +1,106 @@ +<% include('elements/svc_Common.html', +     'post_url'             => popurl(1). 'process/svc_broadband.cgi', +     'name'                 => 'broadband service', +     'table'                => 'svc_broadband', +     'labels'               => { 'svcnum'       => 'Service', +                                 'description'  => 'Description', +                                 'ip_addr'      => 'IP address', +                                 'speed_down'   => 'Download speed', +                                 'speed_up'     => 'Upload speed', +                                 'blocknum'     => 'Router/Block', +                                 'block_label'  => 'Router/Block', +                                 'mac_addr'     => 'MAC address', +                                 'latitude'     => 'Latitude', +                                 'longitude'    => 'Longitude', +                                 'altitude'     => 'Altitude', +                                 'vlan_profile' => 'VLAN profile', +                                 'performance_profile' => 'Performance profile', +                                 'authkey'      => 'Authentication key', +                               }, +     'fields'               => \@fields,  +     'field_callback'       => $callback, +     'dummy'                => $cgi->query_string, +     ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +# If it's stupid but it works, it's still stupid. +#  -Kristian + +my $conf = new FS::Conf; + +my @fields = ( +  qw( description ip_addr speed_down speed_up blocknum ), +  { field=>'block_label', type=>'fixed' }, +  qw( mac_addr latitude longitude altitude vlan_profile performance_profile authkey ) +); + +my $fixedblock = ''; + +my $callback = sub { +  my ($cgi, $object, $fieldref) = @_; + +  my $svcpart = $object->svcnum ? $object->cust_svc->svcpart +                                : $cgi->param('svcpart'); + +  my $part_svc = qsearchs( 'part_svc', { svcpart => $svcpart } ); +  die "No part_svc entry!" unless $part_svc; + +  my $columndef = $part_svc->part_svc_column($fieldref->{'field'}); +  if ($columndef->columnflag eq 'F') { +    $fieldref->{'type'} = 'fixed'; +    $fieldref->{'value'} = $columndef->columnvalue; +    $fixedblock = $fieldref->{value} +      if $fieldref->{field} eq 'blocknum'; +  } + +  if ($object->svcnum) {  + +    $fieldref->{type} = 'hidden' +      if $fieldref->{field} eq 'blocknum'; +       +    $fieldref->{value} = $object->addr_block->label +      if $fieldref->{field} eq 'block_label'; + +  } 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..10079ce98 --- /dev/null +++ b/httemplate/edit/svc_domain.cgi @@ -0,0 +1,117 @@ +<% include('/elements/header.html', "$action $svc", '') %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/svc_domain.cgi" METHOD=POST> +<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>"> +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>"> + +<% ntable("#cccccc",2) %> +<TR> +<P>Domain <INPUT TYPE="text" NAME="domain" VALUE="<% $domain %>" SIZE=28 MAXLENGTH=63> +<BR> +% if ($export) { +Available top-level domains: <% $export->option('tlds') %> +</TR> + +<TR> +<INPUT TYPE="radio" NAME="action" VALUE="N"<% $kludge_action eq 'N' ? ' CHECKED' : '' %>>Register at <% $registrar->{'name'} %> +<BR> + +<INPUT TYPE="radio" NAME="action" VALUE="M"<% $kludge_action eq 'M' ? ' CHECKED' : '' %>>Transfer to <% $registrar->{'name'} %> +<BR> + +<INPUT TYPE="radio" NAME="action" VALUE="I"<% $kludge_action eq 'I' ? ' CHECKED' : '' %>>Registered elsewhere + +</TR> + +% } + +<TR> +<P><INPUT TYPE="submit" VALUE="Submit"> +</TR> +</TABLE> +</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, $part_svc, +   $svc_domain); +if ( $cgi->param('error') ) { + +  $svc_domain = new FS::svc_domain ( { +    map { $_, scalar($cgi->param($_)) } fields('svc_domain') +  } ); +  $svcnum = $svc_domain->svcnum; +  $pkgnum = $cgi->param('pkgnum'); +  $svcpart = $cgi->param('svcpart'); +  $kludge_action = $cgi->param('action'); +  $part_svc = qsearchs('part_svc', { 'svcpart' => $svcpart } ); +  die "No part_svc entry!" unless $part_svc; + +} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding + +  $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; +  $pkgnum = $1; +  $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; +  $svcpart = $1; + +  $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); +  die "No part_svc entry!" unless $part_svc; + +  $svc_domain = new FS::svc_domain({}); + +  $svcnum=''; + +  $svc_domain->set_default_and_fixed; + +} else { #editing + +  $kludge_action = ''; +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/ or die "unparsable svcnum"; +  $svcnum=$1; +  $svc_domain=qsearchs('svc_domain',{'svcnum'=>$svcnum}) +    or die "Unknown (svc_domain) svcnum!"; + +  my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) +    or die "Unknown (cust_svc) svcnum!"; + +  $pkgnum=$cust_svc->pkgnum; +  $svcpart=$cust_svc->svcpart; + +  $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); +  die "No part_svc entry!" unless $part_svc; + +} +my $action = $svcnum ? 'Edit' : 'Add'; + +my $svc = $part_svc->getfield('svc'); + +my @exports = $part_svc->part_export(); + +my $registrar; +my $export; + +# Find the first export that does domain registration +foreach (@exports) { +	$export = $_ if $_->can('registrar'); +} +# If we have a domain registration export, get the registrar object +if ($export) { +	$registrar = $export->registrar; +} + +my $otaker = getotaker; + +my $domain = $svc_domain->domain; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/svc_external.cgi b/httemplate/edit/svc_external.cgi new file mode 100644 index 000000000..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..cd4db7545 --- /dev/null +++ b/httemplate/edit/svc_www.cgi @@ -0,0 +1,240 @@ +<% include('/elements/header.html', "Web Hosting $action") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<%$p1%>process/svc_www.cgi" METHOD=POST> + +<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>"> +Service #<B><% $svcnum ? $svcnum : "(NEW)" %></B> +<BR><BR> + +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> + +<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>"> + +% my $recnum  = $svc_www->recnum; +% my $usersvc = $svc_www->usersvc; + +<% &ntable("#cccccc",2) %> + +  <TR> +    <TD ALIGN="right">Zone</TD> +    <TD> +      <SELECT NAME="recnum" SIZE=1> +%       foreach $_ (keys %arec) { +          <OPTION<% $_ eq $recnum ? " SELECTED" : "" %> VALUE="<%$_%>"><%$arec{$_}%> +%       } +      </SELECT> +    </TD> +  </TR> + +% if ( $part_svc->part_svc_column('usersvc')->columnflag ne 'F' +%     || $part_svc->part_svc_column('usersvc')->columnvalue !~ /^\s*$/) { +    <TR> +      <TD ALIGN="right">Username</TD> +      <TD> +        <SELECT NAME="usersvc" SIZE=1> +          <OPTION VALUE="">(none) +%         foreach $_ (keys %svc_acct) { +            <OPTION<% ($_ eq $usersvc) ? " SELECTED" : "" %> VALUE="<%$_%>"><% $svc_acct{$_} %> +%         } +        </SELECT> +      </TD> +    </TR> +% } + +% if ( $part_svc->part_svc_column('config')->columnflag ne 'F' && +%      $FS::CurrentUser::CurrentUser->access_right('Edit www config') ) { +    <TR> +      <TD ALIGN="right">Config lines</TD> +      <TD> +        <TEXTAREA NAME="config" rows="15" cols="80"><% $config |h %></TEXTAREA> +      </TD> +    </TR> +% } else { +    <INPUT TYPE="hidden" NAME="config" VALUE="<% $config |h %>"> +%} + +% foreach my $field ($svc_www->virtual_fields) { +%   if ( $part_svc->part_svc_column($field)->columnflag ne 'F' ) { +%     # If the flag is X, it won't even show up in $svc_acct->virtual_fields. +      <% $svc_www->pvf($field)->widget( 'HTML', 'edit', +                                        $svc_www->getfield($field) +                                      ) +      %> +%   } +% } + +</TABLE> +<BR> + +<INPUT TYPE="submit" VALUE="Submit"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Provision customer service'); #something else more specific? + +my $conf = new FS::Conf; + +my( $svcnum,  $pkgnum, $svcpart, $part_svc, $svc_www, $config ); + +if ( $cgi->param('error') ) { + +  $svc_www = new FS::svc_www ( { +    map { $_, scalar($cgi->param($_)) } fields('svc_www') +  } ); +  $svcnum = $svc_www->svcnum; +  $pkgnum = $cgi->param('pkgnum'); +  $svcpart = $cgi->param('svcpart'); +  $config = $cgi->param('config'); +  $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); +  die "No part_svc entry!" unless $part_svc; + +} elsif ( $cgi->param('pkgnum') && $cgi->param('svcpart') ) { #adding + +  $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'unparsable pkgnum'; +  $pkgnum = $1; +  $cgi->param('svcpart') =~ /^(\d+)$/ or die 'unparsable svcpart'; +  $svcpart = $1; + +  $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); +  die "No part_svc entry!" unless $part_svc; + +  $svc_www = new FS::svc_www { svcpart => $svcpart }; + +  $svcnum=''; + +  $svc_www->set_default_and_fixed; + +} else { #editing + +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/ or die "unparsable svcnum"; +  $svcnum=$1; +  $svc_www=qsearchs('svc_www',{'svcnum'=>$svcnum}) +    or die "Unknown (svc_www) svcnum!"; + +  my($cust_svc)=qsearchs('cust_svc',{'svcnum'=>$svcnum}) +    or die "Unknown (cust_svc) svcnum!"; + +  $pkgnum  = $cust_svc->pkgnum; +  $svcpart = $cust_svc->svcpart; +  $config  = $svc_www->config; +   +  $part_svc=qsearchs('part_svc',{'svcpart'=>$svcpart}); +  die "No part_svc entry!" unless $part_svc; + +} +my $action = $svc_www->svcnum ? 'Edit' : 'Add'; + +my( %svc_acct, %arec ); +if ($pkgnum) { + +  my @u_acct_svcparts; +  foreach my $svcpart ( +    map { $_->svcpart } qsearch( 'part_svc', { 'svcdb' => 'svc_acct' } ) +  ) { +    next if $conf->exists('svc_www-usersvc_svcpart') +            && ! grep { $svcpart == $_ } +                      $conf->config('svc_www-usersvc_svcpart'); +    push @u_acct_svcparts, $svcpart; +  } + +  my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum}); +  my($custnum)=$cust_pkg->getfield('custnum'); +  my($i_cust_pkg); +  foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) { +    my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum'); +    my($acct_svcpart); +    foreach $acct_svcpart (@u_acct_svcparts) {   #now find the corresponding  +                                              #record(s) in cust_svc ( for this +                                              #pkgnum ! ) +      my($i_cust_svc); +      foreach $i_cust_svc ( qsearch('cust_svc',{'pkgnum'=>$cust_pkgnum,'svcpart'=>$acct_svcpart}) ) { +        my($svc_acct)=qsearchs('svc_acct',{'svcnum'=>$i_cust_svc->getfield('svcnum')}); +        $svc_acct{$svc_acct->getfield('svcnum')}= +          $svc_acct->cust_svc->part_svc->svc. ': '. $svc_acct->email; +      }   +    } +  } + + +  my($d_part_svc,@d_acct_svcparts); +  foreach $d_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_domain'}) ) { +    push @d_acct_svcparts,$d_part_svc->getfield('svcpart'); +  } + +  foreach $i_cust_pkg ( qsearch( 'cust_pkg', { 'custnum' => $custnum } ) ) { +    my $cust_pkgnum = $i_cust_pkg->pkgnum; + +    foreach my $acct_svcpart (@d_acct_svcparts) { + +      foreach my $i_cust_svc ( +        qsearch( 'cust_svc', { 'pkgnum'  => $cust_pkgnum, +                               'svcpart' => $acct_svcpart } ) +      ) { +        my $svc_domain = +          qsearchs( 'svc_domain', { 'svcnum' => $i_cust_svc->svcnum } ); + +        my $extra_sql = "AND ( rectype = 'A' OR rectype = 'CNAME' )"; +        unless ( $conf->exists('svc_www-enable_subdomains') ) { +          $extra_sql .= " AND ( reczone = '\@' OR reczone = '". +                        $svc_domain->domain. ".' )"; +        } + +        foreach my $domain_rec ( +          qsearch( 'domain_record', +                   { +                     'svcnum' => $svc_domain->svcnum, +                   }, +                   '', +                   $extra_sql, +          ) +        ) { +          $arec{$domain_rec->recnum} = $domain_rec->zone; +        } + +        if ( $conf->exists('svc_www-enable_subdomains') ) { +          $arec{'www.'. $svc_domain->domain} = 'www.'. $svc_domain->domain +            unless    qsearchs( 'domain_record', { +                                  svcnum  => $svc_domain->svcnum, +                                  reczone => 'www', +                      } ) +                   || qsearchs( 'domain_record', { +                                  svcnum  => $svc_domain->svcnum, +                                  reczone => 'www.'.$svc_domain->domain.'.', +                    } ); +        } + +        $arec{'@.'. $svc_domain->domain} = $svc_domain->domain +          unless   qsearchs('domain_record', { +                              svcnum  => $svc_domain->svcnum, +                              reczone => '@', +                   } ) +                || qsearchs('domain_record', { +                              svcnum  => $svc_domain->svcnum, +                              reczone => $svc_domain->domain.'.', +                   } ); + +      } + +    } +  } + +} elsif ( $action eq 'Edit' ) { + +  my($domain_rec) = qsearchs('domain_record', { 'recnum'=>$svc_www->recnum }); +  $arec{$svc_www->recnum} = join '.', $domain_rec->recdata, $domain_rec->reczone; + +} else { +  die "\$action eq Add, but \$pkgnum is null!\n"; +} + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/tax_class.html b/httemplate/edit/tax_class.html new file mode 100644 index 000000000..d3e2e821f --- /dev/null +++ b/httemplate/edit/tax_class.html @@ -0,0 +1,36 @@ +<% include('/elements/header.html', "$action taxclass") %> + +<% include('/elements/error.html') %> + +<FORM ACTION="<% $p1 %>process/tax_class.html" METHOD=POST> + +<INPUT TYPE="hidden" NAME="taxclassnum" VALUE=""> +<INPUT TYPE="hidden" NAME="data_vendor" VALUE=""> + +Tax class <INPUT TYPE="text" NAME="taxclass" VALUE="<% $taxclass |h %>"><BR> +Description <INPUT TYPE="text" NAME="description" VALUE="<% $description |h %>"> + +<BR><BR> +<INPUT TYPE="submit" VALUE="<% $action %> taxclass"> + +</FORM> + +<% include('/elements/footer.html') %> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $taxclass = ''; +my $description = ''; +if ( $cgi->param('error') ) { +  $taxclass = $cgi->param('taxclass'); +  $description = $cgi->param('description'); +} + +my $action = 'Add'; + +my $p1 = popurl(1); + +</%init> diff --git a/httemplate/edit/tax_rate.html b/httemplate/edit/tax_rate.html new file mode 100644 index 000000000..bff699946 --- /dev/null +++ b/httemplate/edit/tax_rate.html @@ -0,0 +1,106 @@ +<% include('elements/edit.html', +     'popup'          => 1, +     'name'           => 'Tax rate', #Edit tax rate +     'table'          => 'tax_rate', +     'labels'         => $labels, +     'fields'         => \@fields, +     'value_callback' => $value_callback, +   ) +%> +<%once> + +my $conf = new FS::Conf; +my $value_callback = +  sub { my ( $field, $value ) = @_; +        ( $field =~ /^(tax|excessrate|usetax|useexcessrate)$/ ) +          ? $value*100 +          : $value; +      }; + +</%once> + +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $taxnum; +if ( $cgi->param('error') ) { +  $cgi->param('taxnum') =~ /^(\d+)$/ or die 'error, but no taxnum'; +  $taxnum = $1; +} else { +  my($query) = $cgi->keywords; +  $query =~ /^(\d+)$/ or die 'no taxnum'; +  $taxnum = $1; +} + +my $tax_rate = qsearchs('tax_rate', { 'taxnum' => $taxnum }) +  or die "unknown taxnum $1"; + +my $labels =  { 'taxnum'               => 'Tax', +                'data_vendor'          => 'Data vendor', +                'geocode'              => 'Vendor location code', +                'location'             => 'Tax auth loc code', +                'taxclass_description' => 'Tax class', +                'taxname'              => 'Tax name', +                'effective_date'       => 'Effective date', +                'tax'                  => 'Tax rate (1st bracket)', +                'excessrate'           => 'Tax rate (2nd bracket)', +                'taxbase'              => 'First bracket', +                'taxmax'               => 'Max tax', +                'usetax'               => 'Use tax rate (1st bracket)', +                'useexcessrate'        => 'Use tax rate (2nd bracket)', +                'unittype_name'        => 'Units', +                'fee'                  => 'Fee per unit (1st bracket)', +                'excessfee'            => 'Fee per unit (2st bracket)', +                'feebase'              => 'Units in first bracket', +                'feemax'               => 'Max Units', +                'maxtype_name'         => 'Threshold accumulation', +                'taxauth_name',        => 'Tax authority', +                'basetype_name'        => 'Basis', +                'passtype_name'        => 'Passthru', +                'passflag'             => 'Passable', +                'setuptax'             => 'This tax not applicable to setup fees', +                'recurtax'             => 'This tax not applicable to recurring fees', +              }; + +my @fields = ( +    { type=>'tablebreak-tr-title',   value=>'Location' }, +    { field=>'data_vendor',          type=>'hidden',}, +    { field=>'geocode',              type=>'fixed' }, +    { field=>'taxclassnum',          type=>'hidden' } , +    { field=>'taxclass_description', type=>'fixed' } , +    { field=>'taxname',              type=>'text' } , +    { field=>'effective_date',       type=>'fixed' } , +    { field=>'location',             type=>'text' }, +    { type=>'tablebreak-tr-title',   value=>'Money based rates' }, +    { field=>'tax',                  type=>'percentage' } , +    { field=>'excessrate',           type=>'percentage' } , +    { field=>'taxbase',              type=>'money' } , +    { field=>'taxmax',               type=>'money' } , +    { field=>'usetax',               type=>'percentage' } , +    { field=>'useexcessrate',        type=>'percentage' } , +    { type=>'tablebreak-tr-title',   value=>'Service based rates' }, +    { field=>'unittype',             type=>'hidden' } , +    { field=>'unittype_name',        type=>'fixed' } , +    { field=>'fee',                  type=>'money' } , +    { field=>'excessfee',            type=>'money' } , +    { field=>'feebase',              type=>'text' } , +    { field=>'feemax',               type=>'text' } , +    { type=>'tablebreak-tr-title',   value=>'Taxation rules' }, +    { field=>'maxtype',              type=>'hidden' } , +    { field=>'maxtype_name',         type=>'fixed' } , +    { field=>'taxauth',              type=>'hidden' } , +    { field=>'taxauth_name',         type=>'fixed' } , +    { field=>'basetype',             type=>'hidden' } , +    { field=>'basetype_name',        type=>'fixed' } , +    { field=>'passtype',             type=>'hidden' } , +    { field=>'passtype_name',        type=>'fixed' } , +    { field=>'passflag',             type=>'fixed' } , +    { field=>'setuptax',             type=>'checkbox', value=>'Y'  } , +    { field=>'recurtax',             type=>'checkbox', value=>'Y'  } , +    { field=>'disabled',             type=>'checkbox', value=>'Y'  } , +    { field=>'manual',               type=>'hidden', value=>'Y' } , +); + +</%init> diff --git a/httemplate/edit/usage_class.html b/httemplate/edit/usage_class.html new file mode 100644 index 000000000..be01d2e67 --- /dev/null +++ b/httemplate/edit/usage_class.html @@ -0,0 +1,42 @@ +<% include( 'elements/edit.html', +              'name_singular' => 'Usage Class', +              'table'         => 'usage_class', +              'fields'        => [ +                                   'classname', +                                   'weight', +                                   { field => 'format', +                                     type  => $useformat ? 'select' : 'hidden', +                                     ( $useformat +                                       ? ( 'options' => [ keys %labels ], +                                           'labels'  => \%labels, +                                         ) +                                       : () +                                     ), +                                   }, +                                   { field => 'disabled', +                                     type  => 'checkbox', +                                     value => 'Y', +                                   }, +                                 ], +              'labels'        => {  +                                   'classnum'  => 'Class number', +                                   'classname' => 'Class name', +                                   'weight'    => 'Weight', +                                   'format'    => 'Format', +                                   'disabled'  => 'Disable class', +                                 }, +              'viewall_dir'   => 'browse', +           ) +           +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $conf = new FS::Conf; +my $useformat = $conf->exists('usage_class_as_a_section'); + +my %labels = &FS::usage_class::summary_formats_labelhash(); + +</%init> | 
