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