summaryrefslogtreecommitdiff
path: root/httemplate/misc
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate/misc')
-rw-r--r--httemplate/misc/batch-cust_pay.html98
-rw-r--r--httemplate/misc/cancel_cust.html42
-rwxr-xr-xhttemplate/misc/change_pkg.cgi12
-rw-r--r--httemplate/misc/choose_tax_location.html90
-rwxr-xr-xhttemplate/misc/cust_main-cancel.cgi31
-rw-r--r--httemplate/misc/cust_main-import.cgi17
-rw-r--r--httemplate/misc/cust_main-import_charges.cgi63
-rwxr-xr-xhttemplate/misc/cust_main-merge.html40
-rw-r--r--httemplate/misc/cust_main_note-import.cgi8
-rw-r--r--httemplate/misc/cust_main_note-import.html12
-rw-r--r--httemplate/misc/cust_pkg-import.html150
-rw-r--r--httemplate/misc/custom_link_proxy.cgi24
-rwxr-xr-xhttemplate/misc/delete-domain_record.cgi2
-rw-r--r--httemplate/misc/email-customers.html109
-rw-r--r--httemplate/misc/merge_cust.html72
-rw-r--r--httemplate/misc/order_pkg.html60
-rw-r--r--httemplate/misc/payment.cgi5
-rw-r--r--httemplate/misc/process/batch-cust_pay.cgi34
-rw-r--r--httemplate/misc/process/cust_main-import_charges.cgi3
-rw-r--r--httemplate/misc/process/cust_main_note-import.cgi41
-rw-r--r--httemplate/misc/process/cust_pay-import.cgi2
-rw-r--r--httemplate/misc/process/cust_pkg-import.html10
-rwxr-xr-xhttemplate/misc/process/delete-customer.cgi2
-rw-r--r--httemplate/misc/process/email-customers.html2
-rw-r--r--httemplate/misc/process/payment.cgi26
-rwxr-xr-xhttemplate/misc/timeworked.html24
-rwxr-xr-xhttemplate/misc/unprovision.cgi32
-rw-r--r--httemplate/misc/xmlhttp-cust_main-address_standardize.html1
-rw-r--r--httemplate/misc/xmlhttp-cust_main-censustract.html21
-rw-r--r--httemplate/misc/xmlhttp-cust_main-discount_terms.cgi24
-rw-r--r--httemplate/misc/xmlhttp-cust_main-search.cgi8
31 files changed, 898 insertions, 167 deletions
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
index 505f2d0..610f6e1 100644
--- a/httemplate/misc/batch-cust_pay.html
+++ b/httemplate/misc/batch-cust_pay.html
@@ -13,23 +13,63 @@ function warnUnload() {
}
}
window.onbeforeunload = warnUnload;
+
+function select_discount_term(row, prefix) {
+ var custnum_obj = document.getElementById('custnum'+prefix+row);
+ var select_obj = document.getElementById('discount_term'+prefix+row);
+
+ var value = '';
+ if (select_obj.type == 'hidden') {
+ value = select_obj.value;
+ }
+
+ var term_select = document.createElement('SELECT');
+ term_select.setAttribute('name', 'discount_term'+row);
+ term_select.setAttribute('id', 'discount_term'+row);
+ term_select.setAttribute('rownum', row);
+ term_select.style.display = '';
+ select_obj.parentNode.replaceChild(term_select, select_obj);
+ opt(term_select, '', '1 month');
+
+ function select_discount_term_update(discount_terms) {
+
+ var termArray = eval('(' + discount_terms + ')');
+ for ( var t = 0; t < termArray.length; t++ ) {
+ opt(term_select, termArray[t][0], termArray[t][1]);
+ if (termArray[t][0] == value) {
+ term_select.selectedIndex = t+1;
+ }
+ }
+
+ }
+
+ discount_terms(custnum_obj.value, select_discount_term_update);
+
+}
</SCRIPT>
+<% include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-discount_terms.cgi',
+ 'subs' => [qw( discount_terms )],
+ )
+%>
+
<FORM ACTION="process/batch-cust_pay.cgi" NAME="OneTrueForm" METHOD="POST" onsubmit="document.OneTrueForm.submit.disabled=true;window.onbeforeunload = null;">
<!-- <B>Batch</B> <INPUT TYPE="text" NAME="paybatch"><BR><BR> -->
<% include( "/elements/customer-table.html",
name_singular => 'payment',
- header => [ '', 'Amount', 'Check #', '' ],
- fields => [ sub {'$'}, 'paid', 'payinfo', 'error', ],
- types => [ 'immutable', '', '', 'immutable', ],
- align => [ 'c', 'r', 'r', 'l' ],
- sizes => [ 0, 8, 10, 0, ],
- colors => [ '', '', '', '#ff0000' ],
- param => { () },
- footer => [ '$', '_TOTAL', '', '' ],
- footer_align => [ 'c', 'r', 'r', '' ],
+ header => \@header,
+ fields => \@fields,
+ types => \@types,
+ align => \@align,
+ sizes => \@sizes,
+ colors => \@colors,
+ param => \%param,
+ footer => \@footer,
+ footer_align => \@footer_align,
+ custnum_update_callback => $custnum_update_callback,
)
%>
@@ -41,6 +81,14 @@ window.onbeforeunload = warnUnload;
</FORM>
+%if ( $cgi->param('error') ) {
+<SCRIPT TYPE="text/javascript">
+% for ( my $row = 0; defined($cgi->param("custnum$row")); $row++ ) {
+ select_discount_term(<% $row %>, '');
+% }
+</SCRIPT>
+%}
+
<% include('/elements/footer.html') %>
<%init>
@@ -48,4 +96,36 @@ window.onbeforeunload = warnUnload;
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Post payment batch');
+my @header = ( '', 'Amount', 'Check #' );
+my @fields = ( sub {'$'}, 'paid', 'payinfo' );
+my @types = ( 'immutable', '', '' );
+my @align = ( 'c', 'r', 'r' );
+my @sizes = ( 0, 8, 10 );
+my @colors = ( '', '', '' );
+my %param = ();
+my @footer = ( '$', '_TOTAL', '' );
+my @footer_align = ( 'c', 'r', 'r' );
+my $custnum_update_callback = '';
+
+if ( FS::Record->scalar_sql('SELECT count(*) FROM part_pkg_discount') ) {
+ push @header, '';
+ push @fields, 'discount_term';
+ push @types, 'immutable';
+ push @align, 'r';
+ push @sizes, '0';
+ push @colors, '';
+ push @footer, '';
+ push @footer_align, '';
+ $custnum_update_callback = 'select_discount_term';
+}
+
+push @header, '';
+push @fields, 'error';
+push @types, 'immutable';
+push @align, 'l';
+push @sizes, '0';
+push @colors, '#ff0000';
+push @footer, '';
+push @footer_align, '';
+
</%init>
diff --git a/httemplate/misc/cancel_cust.html b/httemplate/misc/cancel_cust.html
index 12c37eb..b7ecccd 100644
--- a/httemplate/misc/cancel_cust.html
+++ b/httemplate/misc/cancel_cust.html
@@ -2,18 +2,46 @@
<% include('/elements/error.html') %>
+
<FORM NAME="cust_cancel_popup" ACTION="<% popurl(1) %>cust_main-cancel.cgi" METHOD=POST>
<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
<P ALIGN="center"><B>Permanently delete all services and cancel this customer?</B>
- <% $ban %>
-
-<BR><BR>
-
-<% ntable("#cccccc", 2) %>
+<TABLE BORDER="0" CELLSPACING="2"
+STYLE="margin-left:auto; margin-right:auto">
+<TR>
+ <TD ALIGN="right">
+ <INPUT TYPE="radio" NAME="now_or_later" VALUE="0" onclick="toggle(false)" CHECKED />
+ </TD>
+ <TD ALIGN="left">Cancel now</TD>
+</TR>
+<TR>
+ <TD ALIGN="right">
+ <INPUT TYPE="radio" NAME="now_or_later" VALUE="1" onclick="toggle(true)" />
+ </TD>
+ <TD ALIGN="left">Cancel on date:&nbsp;
+ <% include('/elements/input-date-field.html', {
+ 'name' => 'expire',
+ 'value' => time,
+ } ) %>
+ </TD>
+</TR>
+</TABLE>
+<SCRIPT type="text/javascript">
+function toggle(val) {
+ document.getElementById("expire_text").disabled = !val;
+ document.getElementById("ban").disabled = val;
+ document.getElementById("expire_button").style.visibility =
+ val ? 'visible' : 'hidden';
+}
+toggle(false);
+</SCRIPT>
+<% $ban %>
+<TABLE BGCOLOR="#cccccc", BORDER="0" CELLSPACING="2"
+STYLE="margin-left:auto; margin-right:auto">
<% include('/elements/tr-select-reason.html',
'field' => 'reasonnum',
'reason_class' => 'C',
@@ -50,8 +78,8 @@ die "No customer # $custnum" unless $cust_main;
my $ban = '';
if ( $cust_main->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/ ) {
- $ban = '<BR><P ALIGN="center">'.
- '<INPUT TYPE="checkbox" NAME="ban" VALUE="1"> Ban this customer\'s ';
+ $ban = '<P ALIGN="center">'.
+ '<INPUT TYPE="checkbox" NAME="ban" ID="ban" VALUE="1"> Ban this customer\'s ';
if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
$ban .= 'credit card';
} elsif ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
diff --git a/httemplate/misc/change_pkg.cgi b/httemplate/misc/change_pkg.cgi
index 16b7071..ec10b85 100755
--- a/httemplate/misc/change_pkg.cgi
+++ b/httemplate/misc/change_pkg.cgi
@@ -2,7 +2,7 @@
<% include('/elements/error.html') %>
-<FORM ACTION="<% $p %>edit/process/change-cust_pkg.html" METHOD=POST>
+<FORM NAME="OrderPkgForm" ACTION="<% $p %>edit/process/change-cust_pkg.html" METHOD=POST>
<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
<% ntable('#cccccc') %>
@@ -31,8 +31,16 @@
</TABLE>
+<% include( '/elements/standardize_locations.html',
+ 'form' => "OrderPkgForm",
+ 'onlyship' => 1,
+ 'no_company' => 1,
+ 'callback' => 'document.OrderPkgForm.submit();',
+ )
+%>
+
<BR>
-<INPUT TYPE="submit" VALUE="Change package">
+<INPUT NAME="submitButton" TYPE="button" VALUE="Change package" onClick="this.disabled=true; standardize_locations();">
</FORM>
</BODY>
diff --git a/httemplate/misc/choose_tax_location.html b/httemplate/misc/choose_tax_location.html
new file mode 100644
index 0000000..dce04c7
--- /dev/null
+++ b/httemplate/misc/choose_tax_location.html
@@ -0,0 +1,90 @@
+<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.<% $formname %>.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($formname) = $cgi->param('formname') =~ /^([\w]*)$/;
+$formname ||= 'CustomerForm';
+
+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/misc/cust_main-cancel.cgi b/httemplate/misc/cust_main-cancel.cgi
index 009a7d4..44be20c 100755
--- a/httemplate/misc/cust_main-cancel.cgi
+++ b/httemplate/misc/cust_main-cancel.cgi
@@ -1,4 +1,4 @@
-<% header("Customer cancelled") %>
+<% include('/elements/header.html', "Customer cancelled") %>
<SCRIPT TYPE="text/javascript">
window.top.location.reload();
</SCRIPT>
@@ -11,9 +11,11 @@ die "access denied"
my $custnum;
my $ban = '';
+my $expire = '';
if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
$custnum = $1;
$ban = $cgi->param('ban');
+ $expire = $cgi->param('expire');
} else {
my($query) = $cgi->keywords;
$query =~ /^(\d+)$/ || die "Illegal custnum";
@@ -42,11 +44,28 @@ my $cust_main = qsearchs( {
'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
} );
-warn "cancelling $cust_main";
-my @errors = $cust_main->cancel(
- 'ban' => $ban,
- 'reason' => $reasonnum,
-);
+my @errors;
+if($cgi->param('now_or_later')) {
+ $expire = parse_datetime($expire);
+ if($expire) {
+ #warn "setting expire dates on custnum#$custnum\n";
+ my @pkgs = $cust_main->ncancelled_pkgs;
+ @errors = grep {$_} map { $_->cancel(
+ 'reason' => $reasonnum,
+ 'date' => $expire,
+ ) } @pkgs;
+ }
+ else {
+ @errors = ("error parsing expire date: ".$cgi->param('expire'));
+ }
+}
+else {
+ warn "cancelling $cust_main";
+ @errors = $cust_main->cancel(
+ 'ban' => $ban,
+ 'reason' => $reasonnum,
+ );
+}
my $error = join(' / ', @errors) if scalar(@errors);
if ( $error ) {
diff --git a/httemplate/misc/cust_main-import.cgi b/httemplate/misc/cust_main-import.cgi
index 9c1f984..edf4665 100644
--- a/httemplate/misc/cust_main-import.cgi
+++ b/httemplate/misc/cust_main-import.cgi
@@ -30,7 +30,9 @@ Import a file containing customer records.
<SELECT NAME="format">
<!-- <OPTION VALUE="simple">Simple -->
<OPTION VALUE="extended" SELECTED>Extended
+ <OPTION VALUE="extended-plus_options">Extended + options
<OPTION VALUE="extended-plus_company">Extended plus company
+ <OPTION VALUE="extended-plus_company_and_options">Extended plus company and options
<OPTION VALUE="svc_external">External service
<OPTION VALUE="svc_external_svc_phone">External service and phone service
</SELECT>
@@ -89,7 +91,11 @@ Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets.
<b>Extended</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password</i>
<BR><BR>
+<b>Extended plus options</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password, options</i>
+
<b>Extended plus company</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password</i>
+
+<b>Extended plus company and options </b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password, options</i>
<BR><BR>
<b>External service</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, next_bill_date, id, title</i>
@@ -111,7 +117,7 @@ Field information:
of an integer, the string is searched for and if necessary auto-created in the
advertising source table.
- <li><i>payinfo</i>: Credit card number, or leave this, <i>paycvv</i> and <i>paydate</i> blank for email/paper invoicing.
+ <li><i>payinfo</i>: Credit card number, or leave this, <i>paycvv</i> and <i>paydate</i> blank for email/paper invoicing. You may optionally prepend an 'A' or 'D' to the credit card number for automatic or on demand of customer billing respectively
<li><i>paycvv</i>: CVV2 number (three digits on the back of the credit card)
@@ -119,7 +125,7 @@ advertising source table.
<li><i>invoicing_list</i>: Email address for invoices, or POST for postal invoices.
- <li><i>pkgpart</i>: Package definition. Configuration -&gt; Provisioning, services and packages -&gt; View/Edit package definitions
+ <li><i>pkgpart</i>: Package definition. Configuration -&gt; Packages -&gt; Package definitions
<li><i>username</i> and <i>_password</i> are required if <i>pkgpart</i> is specified. (Extended and Extended plus company formats)
@@ -127,6 +133,13 @@ advertising source table.
<li><i>title</i>: External service identifier, text
+ <li><i>options</i>: text containing one or more of
+
+ <ul>
+ <li>taxexempt: this customer does not pay taxes
+ <li>postalinvoice: ensure this customer receives a postal invoice
+ </ul>
+
</ul>
<BR>
diff --git a/httemplate/misc/cust_main-import_charges.cgi b/httemplate/misc/cust_main-import_charges.cgi
index 3801929..c844e0e 100644
--- a/httemplate/misc/cust_main-import_charges.cgi
+++ b/httemplate/misc/cust_main-import_charges.cgi
@@ -1,22 +1,69 @@
-<% include('/elements/header.html', 'Batch Customer Charge') %>
+<% include("/elements/header.html",'Batch Payment Charge') %>
+
+Import a CSV file containing customer payments.
+<BR><BR>
<FORM ACTION="process/cust_main-import_charges.cgi" METHOD="post" ENCTYPE="multipart/form-data">
-Import a CSV file containing customer charges.<BR><BR>
-Default file format is CSV, with the following field order: <i>custnum, amount, description</i><BR><BR>
-If <i>amount</i> is negative, a credit will be applied instead.<BR><BR>
-<BR><BR>
+<% &ntable("#cccccc", 2) %>
+
+<% include('/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+%>
+
+<TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <OPTION VALUE="simple">Simple
+<!-- <OPTION VALUE="extended" SELECTED>Extended -->
+ </SELECT>
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="csvfile"></TD>
+</TR>
-CSV Filename: <INPUT TYPE="file" NAME="csvfile"><BR><BR>
-<INPUT TYPE="submit" VALUE="Import">
+<TR><TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"><INPUT TYPE="submit" VALUE="Import CSV file"></TD></TR>
+
+</TABLE>
</FORM>
-<% include('/elements/footer.html') %>
+<BR>
+
+Simple file format is CSV, with the following field order: <i>custnum, agent_custid, amount, description</i>
+<BR><BR>
+
+<!-- Extended file format is not yet defined</i>
+<BR><BR> -->
+Field information:
+
+<ul>
+
+ <li><i>custnum</i>: This is the freeside customer number. It may be left blank. If specified, agent_custid must be blank.
+
+ <li><i>agent_custid</i>: This is the reseller's idea of the customer number or identifier. It may be left blank. If specified, custnum must be blank.
+
+ <li><i>amount</i>: A numeric value with at most two digits after the decimal point. If <i>amount</i> is negative, a credit will be applied instead.
+
+ <li><i>description</i>: Text describing the transaction.
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Import');
</%init>
+
diff --git a/httemplate/misc/cust_main-merge.html b/httemplate/misc/cust_main-merge.html
new file mode 100755
index 0000000..4decbef
--- /dev/null
+++ b/httemplate/misc/cust_main-merge.html
@@ -0,0 +1,40 @@
+% if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(1). "merge_cust.html?". $cgi->query_string ) %>
+% } else {
+<% include('/elements/header-popup.html', "Customer merged") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.href = '<% $p %>view/cust_main.cgi?<% $new_custnum %>';
+%# parent.nd(1) ?
+ </SCRIPT>
+ </BODY>
+</HTML>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Merge customer');
+
+my $error = '';
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die "illegal custnum";
+my $custnum = $1;
+
+my $new_custnum;
+if ( $cgi->param('new_custnum') =~ /^(\d+)$/ ) {
+ $new_custnum = $1;
+
+ my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+ } );
+ die "No customer # $custnum" unless $cust_main;
+
+ $error = $cust_main->merge($new_custnum);
+
+} else {
+ $error = 'Select a customer to merge into';
+}
+
+</%init>
diff --git a/httemplate/misc/cust_main_note-import.cgi b/httemplate/misc/cust_main_note-import.cgi
index b93c5c1..8a94ae4 100644
--- a/httemplate/misc/cust_main_note-import.cgi
+++ b/httemplate/misc/cust_main_note-import.cgi
@@ -108,6 +108,7 @@
% my $fh = $cgi->upload('csvfile');
% my $csv = new Text::CSV_XS;
% my $skip_fuzzies = $cgi->param('fuzzies') ? 0 : 1;
+% my $use_agent_custid = $cgi->param('use_agent_custid') ? 1 : 0;
%
% if ( defined($fh) ) {
<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
@@ -118,7 +119,7 @@
<TH>First</TH>
<TH>Note to be added</TH>
</TR>
-% my $agentnum => scalar($cgi->param('agentnum')),
+% my $agentnum = scalar($cgi->param('agentnum'));
% my $line;
% my $row = 0;
% while ( defined($line=<$fh>) ) {
@@ -138,7 +139,10 @@
% next unless ( $last || $first || $note );
% my @cust_main = ();
% warn "searching for: $last, $first" if ($first || $last);
-% if ($custnum) {
+% if ($agentnum && $custnum && $use_agent_custid) {
+% @cust_main = qsearch('cust_main', { 'agent' => $agentnum,
+% 'agent_custid' => $custnum } );
+% } elsif ($custnum) { # && !use_agent_custid
% @cust_main = qsearch('cust_main', { 'custnum' => $custnum });
% } else {
% @cust_main = FS::cust_main::smart_search(
diff --git a/httemplate/misc/cust_main_note-import.html b/httemplate/misc/cust_main_note-import.html
index d8fefa7..cc1645d 100644
--- a/httemplate/misc/cust_main_note-import.html
+++ b/httemplate/misc/cust_main_note-import.html
@@ -13,6 +13,13 @@ Anything after the character sequence #! is ignored.
<% &ntable("#cccccc") %>
+<% include('/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+%>
+
<TR>
<TH ALIGN="right">CSV filename</TH>
<TD><INPUT TYPE="file" NAME="csvfile"></TD>
@@ -22,6 +29,11 @@ Anything after the character sequence #! is ignored.
<TD><INPUT TYPE="checkbox" NAME="fuzzies"></TD>
</TR>
+<TR>
+ <TH ALIGN="right">custnum is reseller's customer number</TH>
+ <TD><INPUT TYPE="checkbox" NAME="use_agent_custid"></TD>
+</TR>
+
</TABLE>
<BR><BR>
diff --git a/httemplate/misc/cust_pkg-import.html b/httemplate/misc/cust_pkg-import.html
new file mode 100644
index 0000000..b29884d
--- /dev/null
+++ b/httemplate/misc/cust_pkg-import.html
@@ -0,0 +1,150 @@
+<% include("/elements/header.html",'Batch Package Import') %>
+
+Import a file containing package records.
+<BR><BR>
+
+<% include( '/elements/form-file_upload.html',
+ 'name' => 'PackageImportForm',
+ 'action' => 'process/cust_pkg-import.html',
+ 'num_files' => 1,
+ 'fields' => [ 'agentnum', 'pkgbatch', 'format' ],
+ 'message' => 'Package import successful',
+ 'url' => $p."search/cust_pkg.cgi?pkgbatch=$pkgbatch",
+ )
+%>
+
+<% &ntable("#cccccc", 2) %>
+
+ <% include( '/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+ %>
+
+ <INPUT TYPE="hidden" NAME="pkgbatch" VALUE="<% $pkgbatch %>"%>
+
+ <TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <OPTION VALUE="default" SELECTED>Default
+ <OPTION VALUE="default-agent_custid">Default with agent_custid
+ <OPTION VALUE="svc_acct">Account service
+ <OPTION VALUE="svc_acct-agent_custid">Account service with agent_custid
+ <OPTION VALUE="svc_phone">Phone service
+ <OPTION VALUE="svc_phone-agent_custid">Phone service with agent_custid
+ <OPTION VALUE="svc_external">External service
+ <OPTION VALUE="svc_external-agent_custid">External service with agent_custid
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/file-upload.html',
+ 'field' => 'file',
+ 'label' => 'Filename',
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ ID = "submit"
+ VALUE = "Import file"
+ onClick = "document.PackageImportForm.submit.disabled=true;"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets. The file should have a .CSV or .XLS extension.
+<BR><BR>
+
+<b>Default</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire</i>
+<BR><BR>
+
+<b>Default with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire</i>
+<BR><BR>
+
+<b>Account service</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, username, _password, domsvc</i>
+<BR><BR>
+
+<b>Account service with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, username, _password, domsvc</i>
+<BR><BR>
+
+<b>Phone sevice</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, countrycode, phonenum, sip_password, pin</i>
+<BR><BR>
+
+<b>Phone service with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, countrycode, phonenum, sip_password, pin</i>
+<BR><BR>
+
+<b>External sevice</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, id, title</i>
+<BR><BR>
+
+<b>External service with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, id, title</i>
+<BR><BR>
+
+<%$req%> Required fields
+<BR><BR>
+
+Field information:
+
+<ul>
+
+ <li><i>custnum</i>: This specifies an existing customer by custnum.
+
+ <li><i>agent_custid</i>: This specifies an existing customer record by agent_custid.
+
+ <li><i>pkgpart</i>: Package definition. Configuration -&gt; Packages -&gt; Package definitions
+
+ <li><i>discountnum</i>: Optional discount. Configuration -&gt; Packages -&gt; Discounts
+
+ <li><i>start_date</i>: Indicates a future start date; do not fill in for active packages
+
+ <li><i>setup</i>: Indicates setup fee has been charged and package setup on this date
+
+ <li><i>bill</i>: Next bill date
+
+ <li><i>last_bill</i>: Last bill date
+
+ <li><i>susp</i>: Indicates the package is suspended (on the given date).
+
+ <li><i>adjourn</i>: Indicates a future suspension on this date.
+
+ <li><i>cancel</i>: Indicates the package is cancelled (on the given date).
+
+ <li><i>expire</i>: Indicates a future cancellation on this date.
+
+<!--
+ <li><i>username</i> and <i>_password</i> are required if <i>pkgpart</i> is specified. (Extended and Extended plus company formats)
+-->
+
+ <li><i>domsvc</i>: Domain svcnum
+
+ <li><i>id</i>: External service id, integer
+
+ <li><i>title</i>: External service identifier, text
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+my $req = qq!<font color="#ff0000">*</font>!;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $pkgbatch = time2str('webimport-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+</%init>
diff --git a/httemplate/misc/custom_link_proxy.cgi b/httemplate/misc/custom_link_proxy.cgi
new file mode 100644
index 0000000..e5934e4
--- /dev/null
+++ b/httemplate/misc/custom_link_proxy.cgi
@@ -0,0 +1,24 @@
+% if( $response->is_success ) {
+<% $response->decoded_content %>
+% }
+% else {
+<% $response->error_as_HTML %>
+% }
+<%init>
+
+my( $custnum ) = $cgi->param('custnum');
+my $cust_main = qsearchs('cust_main', { custnum => $custnum } )
+ or die "custnum '$custnum' not found"; # just check for existence
+
+my $conf = new FS::Conf;
+my $url = $conf->config('cust_main-custom_link') . $cust_main->custnum;
+#warn $url;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('View customer');
+
+my $ua = new LWP::UserAgent;
+my $response = $ua->get($url);
+</%init>
diff --git a/httemplate/misc/delete-domain_record.cgi b/httemplate/misc/delete-domain_record.cgi
index 08eedde..200365d 100755
--- a/httemplate/misc/delete-domain_record.cgi
+++ b/httemplate/misc/delete-domain_record.cgi
@@ -1,7 +1,7 @@
% if ( $error ) {
% errorpage($error);
% } else {
-<% $cgi->redirect($p. "view/svc_domain.cgi?". $domain_record->svcnum) %>
+<% $cgi->redirect($p. "view/svc_domain.cgi?". $domain_record->svcnum. '#dns') %>
% }
<%init>
diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html
index 201aed4..759c8bf 100644
--- a/httemplate/misc/email-customers.html
+++ b/httemplate/misc/email-customers.html
@@ -1,69 +1,78 @@
<% include('/elements/header.html', $title) %>
<FORM NAME="OneTrueForm" ACTION="email-customers.html" METHOD="POST">
-% foreach my $key ( keys %search ) {
-% my @values = ref($search{$key}) ? @{$search{$key}} : ( $search{$key} );
-% foreach my $value ( @values ) {
- <INPUT TYPE="hidden" NAME="<% $key %>" VALUE="<% $value %>">
-% }
-% }
+<INPUT TYPE="hidden" NAME="table" VALUE="<% $table %>">
+%# Mixing search params with from address, subject, etc. required special-case
+%# handling of those, risked name conflicts, and caused massive problems with
+%# multi-valued search params. We are no longer in search context, so we
+%# pack the search into a Storable string for later use.
+<INPUT TYPE="hidden" NAME="search" VALUE="<% encode_base64(nfreeze(\%search)) %>">
-% if ( $cgi->param('magic') eq 'send' ) {
+% if ( $cgi->param('action') eq 'send' ) {
<FONT SIZE="+2">Sending notice</FONT>
<% include('/elements/progress-init.html',
'OneTrueForm',
- [ keys(%search), qw( from subject html_body text_body ) ],
+ [ qw( search table from subject html_body text_body msgnum ) ],
'process/email-customers.html',
{ 'message' => "Notice sent" }, #would be nice to show #, but..
)
%>
-% } elsif ( $cgi->param('magic') eq 'preview' ) {
+% } elsif ( $cgi->param('action') eq 'preview' ) {
<FONT SIZE="+2">Preview notice</FONT>
% }
-% if ( $cgi->param('magic') ) {
+% if ( $cgi->param('action') ) {
<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+ <INPUT TYPE="hidden" NAME="msgnum" VALUE="<% $cgi->param('msgnum') %>">
+
+% if ( $msg_template ) {
+ <% include('/elements/tr-fixed.html',
+ 'label' => 'Template:',
+ 'value' => $msg_template->msgname,
+ )
+ %>
+% }
<% include('/elements/tr-fixed.html',
'field' => 'from',
'label' => 'From:',
- 'value' => scalar( $cgi->param('from') ),
+ 'value' => scalar( $from ),
)
%>
<% include('/elements/tr-fixed.html',
'field' => 'subject',
'label' => 'Subject:',
- 'value' => scalar( $cgi->param('subject') ),
+ 'value' => scalar( $subject ),
)
%>
- <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $cgi->param('html_body') |h %>">
+ <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $html_body |h %>">
<TR>
<TD ALIGN="right" VALIGN="top">Message (HTML display): </TD>
- <TD CLASS="background" ALIGN="left"><% $cgi->param('html_body') %></TD>
+ <TD CLASS="background" ALIGN="left"><% $html_body %></TD>
</TR>
% my $text_body = HTML::FormatText->new(leftmargin=>0)->format(
% HTML::TreeBuilder->new_from_content(
-% $cgi->param('html_body')
+% $html_body
% )
% );
<INPUT TYPE="hidden" NAME="text_body" VALUE="<% $text_body |h %>">
<TR>
<TD ALIGN="right" VALIGN="top">Message (Text display): </TD>
- <TD CLASS="background" ALIGN="left"><PRE><% $text_body %></PRE></TD>
+ <TD CLASS="background" STYLE="background-color:white" ALIGN="left"><PRE><% $text_body %></PRE></TD>
</TR>
</TABLE>
-% if ( $cgi->param('magic') eq 'preview' ) {
+% if ( $cgi->param('action') eq 'preview' ) {
<SCRIPT>
function areyousure(href) {
@@ -72,15 +81,29 @@
</SCRIPT>
<BR>
- <INPUT TYPE="hidden" NAME="magic" VALUE="send">
+ <INPUT TYPE="hidden" NAME="action" VALUE="send">
<INPUT TYPE="submit" VALUE="Send notice" onClick="return areyousure()">
% }
% } else {
- <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%">
+<SCRIPT TYPE="text/javascript">
+function toggle(obj) {
+ document.getElementById('table_no_template').style.display = (obj.value == 0) ? '' : 'none';
+}
+</SCRIPT>
+Template:
+ <% include('/elements/select-table.html',
+ 'label' => 'Template:',
+ 'table' => 'msg_template',
+ 'name_col' => 'msgname',
+ 'empty_label' => '(none)',
+ 'onchange' => 'toggle(this)',
+ )
+ %><BR>
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%" id="table_no_template">
<% include('/elements/tr-input-text.html',
'field' => 'from',
'label' => 'From:',
@@ -102,15 +125,14 @@
%#Substitution vars:
- <BR><BR>
- <INPUT TYPE="hidden" NAME="magic" VALUE="preview">
+ <INPUT TYPE="hidden" NAME="action" VALUE="preview">
<INPUT TYPE="submit" VALUE="Preview notice">
% }
</FORM>
-% if ( $cgi->param('magic') eq 'send' ) {
+% if ( $cgi->param('action') eq 'send' ) {
<SCRIPT TYPE="text/javascript">
process();
</SCRIPT>
@@ -123,16 +145,32 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
-my %search = $cgi->Vars;
-delete $search{$_} for qw( magic from subject html_body text_body );
-$search{$_} = [ split(/\0/, $search{$_}) ]
- foreach grep { $_ eq 'payby' || $search{$_} =~ /\0/ } keys %search;
-
-my $title = 'Bulk send customer notices';
+my $table = $cgi->param('table') or die "'table' required";
+my %search;
+if ( $cgi->param('search') ) {
+ %search = %{ thaw(decode_base64($cgi->param('search'))) };
+}
+else {
+ %search = $cgi->Vars;
+ delete $search{$_} for qw( action table from subject html_body text_body );
+ # FS::$table->search is expected to know which parameters might be
+ # multi-valued, and to accept scalar values for them also. No good
+ # solution to this since CGI can't tell whether a parameter _might_
+ # have had multiple values, only whether it does.
+ @search{keys %search} = map { /\0/ ? [ split /\0/, $_ ] : $_ } values %search;
+}
+
+my $title = 'Send bulk customer notices';
my $num_cust;
-if ( $cgi->param('magic') eq 'preview' ) {
- my $sql_query = FS::cust_main->search(\%search);
+my $from = $cgi->param('from') || '';
+my $subject = $cgi->param('subject') || '';
+my $html_body = $cgi->param('html_body') || '';
+
+my $msg_template = '';
+
+if ( $cgi->param('action') eq 'preview' ) {
+ my $sql_query = "FS::$table"->search(\%search);
my $count_query = delete($sql_query->{'count_query'});
my $count_sth = dbh->prepare($count_query)
or die "Error preparing $count_query: ". dbh->errstr;
@@ -140,6 +178,17 @@ if ( $cgi->param('magic') eq 'preview' ) {
or die "Error executing $count_query: ". $count_sth->errstr;
my $count_arrayref = $count_sth->fetchrow_arrayref;
$num_cust = $count_arrayref->[0];
+
+ if ( $cgi->param('msgnum') ) {
+ $msg_template = qsearchs('msg_template',
+ { msgnum => $cgi->param('msgnum') } )
+ or die "template not found: ".$cgi->param('msgnum');
+ $sql_query->{'extra_sql'} .= ' LIMIT 1';
+ $sql_query->{'order_by'} = '';
+ my $cust = qsearchs($sql_query)->cust_main;
+ my %message = $msg_template->prepare( 'cust_main' => $cust );
+ ($from, $subject, $html_body) = @message{'from', 'subject', 'html_body'};
+ }
}
</%init>
diff --git a/httemplate/misc/merge_cust.html b/httemplate/misc/merge_cust.html
new file mode 100644
index 0000000..ad075be
--- /dev/null
+++ b/httemplate/misc/merge_cust.html
@@ -0,0 +1,72 @@
+<% include('/elements/header-popup.html', 'Merge customer' ) %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="cust_merge_popup" ID="cust_merge_popup" ACTION="<% popurl(1) %>cust_main-merge.html" METHOD=POST onSubmit="submit_merge(); return false;">
+
+<SCRIPT TYPE="text/javascript">
+
+var submit_interval_id;
+function submit_merge() {
+ document.getElementById('confirm_merge_cust_button').disabled = 'true';
+ smart_new_custnum_search(document.getElementById('new_custnum_search'));
+ submit_interval_id = setInterval( do_submit_merge, 100);
+}
+
+function do_submit_merge() {
+
+ if ( new_custnum_search_active )
+ return;
+
+ document.getElementById('confirm_merge_cust_button').disabled = '';
+
+ clearInterval(submit_interval_id);
+
+ if ( document.cust_merge_popup.new_custnum.value != '' ) {
+ document.cust_merge_popup.submit();
+ }
+
+}
+
+</SCRIPT>
+
+</SCRIPT>
+
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+<TABLE BORDER="0" CELLSPACING="2" STYLE="margin-left:auto; margin-right:auto">
+ <% include('/elements/tr-search-cust_main.html',
+ 'label' => 'Merge into: ',
+ 'field' => 'new_custnum',
+ 'find_button' => 1,
+ 'curr_value' => scalar($cgi->param('new_custnum')),
+ )
+ %>
+</TABLE>
+
+<P ALIGN="CENTER">
+%#have merge button start out disabled and enable after you select a target cust
+<INPUT TYPE="submit" NAME="confirm_merge_cust_button" ID="confirm_merge_cust_button" VALUE="Merge customer">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT TYPE="BUTTON" VALUE="Don't merge" onClick="parent.cClick();">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
+my $custnum = $1;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied" unless $curuser->access_right('Merge customer');
+
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+} );
+die "No customer # $custnum" unless $cust_main;
+
+</%init>
+
diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html
index 33b2bb3..b232deb 100644
--- a/httemplate/misc/order_pkg.html
+++ b/httemplate/misc/order_pkg.html
@@ -9,14 +9,14 @@
function enable_order_pkg () {
if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) {
- document.OrderPkgForm.submit.disabled = false;
+ document.OrderPkgForm.submitButton.disabled = false;
if ( document.OrderPkgForm.pkgpart.options[document.OrderPkgForm.pkgpart.selectedIndex].getAttribute('data-can_discount') == 1 ) {
document.OrderPkgForm.discountnum.disabled = false;
} else {
document.OrderPkgForm.discountnum.disabled = true;
}
} else {
- document.OrderPkgForm.submit.disabled = true;
+ document.OrderPkgForm.submitButton.disabled = true;
document.OrderPkgForm.discountnum.disabled = true;
}
}
@@ -38,34 +38,19 @@
)
%>
-%# false laziness w/edit/quick-charge.html
<TR>
<TH ALIGN="right">Start date </TD>
<TD COLSPAN=6>
- <INPUT TYPE = "text"
- NAME = "start_date"
- SIZE = 32
- ID = "start_date_text"
- VALUE = "<% $start_date %>"
- >
- <IMG SRC = "../images/calendar.png"
- ID = "start_date_button"
- STYLE = "cursor: pointer"
- TITLE = "Select date"
- >
+ <% include('/elements/input-date-field.html',{
+ 'name' => 'start_date',
+ 'format' => $date_format,
+ 'value' => $start_date,
+ 'noinit' => 1,
+ }) %>
<FONT SIZE=-1>(leave blank to start immediately)</FONT>
</TD>
</TR>
-<SCRIPT TYPE="text/javascript">
- Calendar.setup({
- inputField: "start_date_text",
- ifFormat: "<% $date_format %>",
- button: "start_date_button",
- align: "BR"
- });
-</SCRIPT>
-
% if ( $cust_main->payby =~ /^(CARD|CHEK)$/ ) {
% my $what = lc(FS::payby->shortname($cust_main->payby));
<TR>
@@ -99,10 +84,30 @@
)
%>
+<TR>
+ <TH ALIGN="right">Contract end date </TD>
+ <TD COLSPAN=6>
+ <% include('/elements/input-date-field.html',{
+ 'name' => 'contract_end',
+ 'format' => $date_format,
+ 'value' => '',
+ 'noinit' => 1,
+ }) %>
+ </TD>
+</TR>
+
</TABLE>
+<% include( '/elements/standardize_locations.html',
+ 'form' => "OrderPkgForm",
+ 'onlyship' => 1,
+ 'no_company' => 1,
+ 'callback' => 'document.OrderPkgForm.submit();',
+ )
+%>
+
<BR>
-<INPUT NAME="submit" TYPE="submit" VALUE="Order Package" <% $pkgpart ? '' : 'DISABLED' %>>
+<INPUT NAME="submitButton" TYPE="button" VALUE="Order Package" onClick = "this.disabled=true; standardize_locations();" <% $pkgpart ? '' : 'DISABLED' %>>
</FORM>
</BODY>
@@ -128,7 +133,10 @@ my $cust_main = qsearchs({
my $pkgpart = scalar($cgi->param('pkgpart'));
my $format = $date_format. ' %T %z (%Z)'; #false laziness w/REAL_cust_pkg.cgi?
-my $start_date = $cust_main->next_bill_date;
-$start_date = $start_date ? time2str($format, $start_date) : '';
+my $start_date = '';
+if( ! $conf->exists('order_pkg-no_start_date') ) {
+ $start_date = $cust_main->next_bill_date;
+ $start_date = $start_date ? time2str($format, $start_date) : '';
+}
</%init>
diff --git a/httemplate/misc/payment.cgi b/httemplate/misc/payment.cgi
index 813b560..bcab68a 100644
--- a/httemplate/misc/payment.cgi
+++ b/httemplate/misc/payment.cgi
@@ -67,6 +67,11 @@
% }
+<% include('/elements/tr-select-discount_term.html',
+ 'custnum' => $custnum,
+ 'cgi' => $cgi
+ )
+%>
% if ( $payby eq 'CARD' ) {
%
diff --git a/httemplate/misc/process/batch-cust_pay.cgi b/httemplate/misc/process/batch-cust_pay.cgi
index 058a225..e51b9e6 100644
--- a/httemplate/misc/process/batch-cust_pay.cgi
+++ b/httemplate/misc/process/batch-cust_pay.cgi
@@ -10,13 +10,33 @@
% #my $row = 0;
% #while ( exists($param->{"custnum$row"}) ) {
% for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+% my $custnum = $param->{"custnum$row"};
+% my $cust_main;
+% if ( $custnum =~ /^(\d+)$/ and $1 <= 2147483647 ) {
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'custnum' => $1 },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+% }
+% if ( !$cust_main ) { # not found, try agent_custid
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'agent_custid' => $custnum },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+% }
+% $custnum = $cust_main->custnum if $cust_main;
+% # if !$cust_main, then this will throw an error on batch_insert
+%
% push @cust_pay, new FS::cust_pay {
-% 'custnum' => $param->{"custnum$row"},
-% 'paid' => $param->{"paid$row"},
-% 'payby' => 'BILL',
-% 'payinfo' => $param->{"payinfo$row"},
-% 'paybatch' => $paybatch,
-% }
+% 'custnum' => $custnum,
+% 'paid' => $param->{"paid$row"},
+% 'payby' => 'BILL',
+% 'payinfo' => $param->{"payinfo$row"},
+% 'discount_term' => $param->{"discount_term$row"},
+% 'paybatch' => $paybatch,
+% }
% if $param->{"custnum$row"}
% || $param->{"paid$row"}
% || $param->{"payinfo$row"};
@@ -42,6 +62,6 @@
% } else {
%
%
-<% $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %>
+<% $cgi->redirect(popurl(3). "search/cust_pay.html?magic=paybatch;paybatch=$paybatch") %>
% }
diff --git a/httemplate/misc/process/cust_main-import_charges.cgi b/httemplate/misc/process/cust_main-import_charges.cgi
index 3ca6894..bda3e3b 100644
--- a/httemplate/misc/process/cust_main-import_charges.cgi
+++ b/httemplate/misc/process/cust_main-import_charges.cgi
@@ -16,7 +16,8 @@ my $fh = $cgi->upload('csvfile');
my $error = defined($fh)
? FS::cust_main::batch_charge( {
filehandle => $fh,
- 'fields' => [qw( custnum amount pkg )],
+ 'agentnum' => scalar($cgi->param('agentnum')),
+ 'format' => scalar($cgi->param('format')),
} )
: 'No file';
diff --git a/httemplate/misc/process/cust_main_note-import.cgi b/httemplate/misc/process/cust_main_note-import.cgi
index 6aa8b1d..6625e00 100644
--- a/httemplate/misc/process/cust_main_note-import.cgi
+++ b/httemplate/misc/process/cust_main_note-import.cgi
@@ -26,6 +26,7 @@ The following items <% $op eq 'Preview' ? 'would be' : 'were' %> imported. (See
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Import');
+$FS::cust_main::import=1; # the customer records are already in the database
my $date = time;
my $otaker = $FS::CurrentUser::CurrentUser->username;
my $csv = new Text::CSV_XS;
@@ -38,25 +39,27 @@ my @inserted = ();
my @uninserted = ();
for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
if ( $param->{"custnum$row"} ) {
-# my $cust_main_note = new FS::cust_main_note {
-# 'custnum' => $param->{"custnum$row"},
-# '_date' => $date,
-# 'otaker' => $otaker,
-# 'comments' => $param->{"note$row"},
-# };
-# my $error = '';
-# $error = $cust_main_note->insert unless ($op eq "Preview");
- my $cust_main = qsearchs('cust_main',
- { 'custnum' => $param->{"custnum$row"} }
- );
- my $error;
- if ($cust_main) {
- $cust_main->comments
- ? $cust_main->comments($cust_main->comments. " ". $param->{"note$row"})
- : $cust_main->comments($param->{"note$row"});
- $error = $cust_main->replace;
- }else{
- $error = "Can't find customer " . $param->{"custnum$row"};
+ my $error = '';
+ if ( $param->{use_comments} ) { # why? notes are sexier
+ my $cust_main = qsearchs('cust_main',
+ { 'custnum' => $param->{"custnum$row"} }
+ );
+ if ($cust_main) {
+ $cust_main->comments
+ ? $cust_main->comments($cust_main->comments. " ". $param->{"note$row"})
+ : $cust_main->comments($param->{"note$row"});
+ $error = $cust_main->replace;
+ }else{
+ $error = "Can't find customer " . $param->{"custnum$row"};
+ }
+ } else {
+ my $cust_main_note = new FS::cust_main_note {
+ 'custnum' => $param->{"custnum$row"},
+ '_date' => $date,
+ 'otaker' => $otaker,
+ 'comments' => $param->{"note$row"},
+ };
+ $error = $cust_main_note->insert unless ($op eq "Preview");
}
my $result = { 'custnum' => $param->{"custnum$row"},
'last' => $param->{"last$row"},
diff --git a/httemplate/misc/process/cust_pay-import.cgi b/httemplate/misc/process/cust_pay-import.cgi
index d4ff226..92b6e5a 100644
--- a/httemplate/misc/process/cust_pay-import.cgi
+++ b/httemplate/misc/process/cust_pay-import.cgi
@@ -1,4 +1,4 @@
-<% $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %>
+<% $cgi->redirect(popurl(3). "search/cust_pay.html?magic=paybatch;paybatch=$paybatch") %>
<%init>
my $fh = $cgi->upload('csvfile');
diff --git a/httemplate/misc/process/cust_pkg-import.html b/httemplate/misc/process/cust_pkg-import.html
new file mode 100644
index 0000000..1021817
--- /dev/null
+++ b/httemplate/misc/process/cust_pkg-import.html
@@ -0,0 +1,10 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $server =
+ new FS::UI::Web::JSRPC 'FS::cust_pkg::Import::process_batch_import', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/delete-customer.cgi b/httemplate/misc/process/delete-customer.cgi
index d509a5e..1201131 100755
--- a/httemplate/misc/process/delete-customer.cgi
+++ b/httemplate/misc/process/delete-customer.cgi
@@ -28,6 +28,6 @@ if ( $cgi->param('new_custnum') ) {
my $cust_main = qsearchs( 'cust_main', { 'custnum' => $custnum } )
or die "Customer not found: $custnum";
-my $error = $cust_main->delete($new_custnum);
+my $error = $cust_main->delete('new_custnum' => $new_custnum);
</%init>
diff --git a/httemplate/misc/process/email-customers.html b/httemplate/misc/process/email-customers.html
index c54bc6d..de2bb92 100644
--- a/httemplate/misc/process/email-customers.html
+++ b/httemplate/misc/process/email-customers.html
@@ -4,6 +4,6 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
-my $server = new FS::UI::Web::JSRPC 'FS::cust_main::process_email_search_result', $cgi;
+my $server = new FS::UI::Web::JSRPC 'FS::cust_main_Mixin::process_email_search_result', $cgi;
</%init>
diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi
index 665001e..c1c9071 100644
--- a/httemplate/misc/process/payment.cgi
+++ b/httemplate/misc/process/payment.cgi
@@ -119,19 +119,26 @@ if ( $payby eq 'CHEK' ) {
die "unknown payby $payby";
}
+$cgi->param('discount_term') =~ /^\d*$/
+ or errorpage("illegal discount_term");
+my $discount_term = $1;
+
my $error = '';
my $paynum = '';
if ( $cgi->param('batch') ) {
- $error = $cust_main->batch_card(
- 'payby' => $payby,
- 'amount' => $amount,
- 'payinfo' => $payinfo,
- 'paydate' => "$year-$month-01",
- 'payname' => $payname,
- map { $_ => $cgi->param($_) }
- @{$payby2fields{$payby}}
- );
+ $error = 'Prepayment discounts not supported with batched payments'
+ if $discount_term;
+
+ $error ||= $cust_main->batch_card(
+ 'payby' => $payby,
+ 'amount' => $amount,
+ 'payinfo' => $payinfo,
+ 'paydate' => "$year-$month-01",
+ 'payname' => $payname,
+ map { $_ => $cgi->param($_) }
+ @{$payby2fields{$payby}}
+ );
errorpage($error) if $error;
} else {
@@ -146,6 +153,7 @@ if ( $cgi->param('batch') ) {
'payunique' => $payunique,
'paycvv' => $paycvv,
'paynum_ref' => \$paynum,
+ 'discount_term' => $discount_term,
map { $_ => $cgi->param($_) } @{$payby2fields{$payby}}
);
errorpage($error) if $error;
diff --git a/httemplate/misc/timeworked.html b/httemplate/misc/timeworked.html
index 46063e8..672fad8 100755
--- a/httemplate/misc/timeworked.html
+++ b/httemplate/misc/timeworked.html
@@ -8,22 +8,11 @@
<THEAD>
<TR>
- <TH>Trans</TH>
<TH COLSPAN="2">Ticket</TH>
- <TH>Time</TH>
+ <TH>Hours</TH>
<TH COLSPAN="2">Customer</TH>
<TH>Multiplier</TH>
</TR>
-
- <TR>
- <TH>#</TH>
- <TH>#</TH>
- <TH>Subject</TH>
- <TH>hours</TH>
- <TH>#</TH>
- <TH>Name</TH>
- <TH></TH>
- </TR>
</THEAD>
<TBODY>
@@ -35,9 +24,9 @@
% my ($custnum, $name) = split(':', pop @customers, 2);
% my $link = $p. 'rt/Ticket/Display.html?id='. $ticketmap{$tr_id}.
% '#txn-'. $tr_id;
+% my $clink = $p. 'view/cust_main.cgi?'. $custnum;
<TR>
- <TD><a href="<% $link %>"><% $tr_id %></a></TD>
<TD><a href="<% $link %>"><% $ticketmap{$tr_id} %></a></TD>
<TD><a href="<% $link %>"><% $ticket{$ticketmap{$tr_id}} |h %></a></TD>
@@ -47,8 +36,8 @@
% }
<TD><% sprintf("%0.2f", $seconds/3600) %></TD>
- <TD ALIGN="right"><% $custnum %></TD>
- <TD ALIGN="right"><% $name %></TD>
+ <TD ALIGN="right"><a href="<% $clink %>"><% $custnum %></a></TD>
+ <TD ALIGN="right"><a href="<% $clink %>"><% $name %></a></TD>
<TD>
<INPUT TYPE="hidden" NAME="transactionid<%$tr_id%>" VALUE="1" >
<INPUT TYPE="hidden" NAME="seconds<%$tr_id%>" VALUE="<% $seconds %>" >
@@ -65,10 +54,11 @@
% foreach ( @customers ) {
% ($custnum, $name) = split(':', $_, 2);
+% $clink = $p. 'view/cust_main.cgi?'. $custnum;
<TR>
- <TD ALIGN="right" COLSPAN="5" ><% $custnum %></TD>
- <TD ALIGN="right"><% $name %></TD>
+ <TD ALIGN="right" COLSPAN="4" ><a href="<% $clink %>"><% $custnum %></a></TD>
+ <TD ALIGN="right"><a href="<% $clink %>"><% $name %></a></TD>
<TD>
% $multiplier = $default_multiplier;
diff --git a/httemplate/misc/unprovision.cgi b/httemplate/misc/unprovision.cgi
index 4ab15fd..6f2c238 100755
--- a/httemplate/misc/unprovision.cgi
+++ b/httemplate/misc/unprovision.cgi
@@ -1,6 +1,8 @@
%if ( $error ) {
% errorpage($error);
-%} else {
+%} elsif ( $pkgnum ) {
+<% $cgi->redirect(popurl(2)."search/cust_pkg_svc.html?svcpart=$svcpart;pkgnum=$pkgnum") %>
+%} else { # $custnum should always exist
<% $cgi->redirect(popurl(2)."view/cust_main.cgi?$custnum") %>
%}
<%init>
@@ -9,18 +11,28 @@ die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Unprovision customer service');
#untaint svcnum
-my($query) = $cgi->keywords;
-$query =~ /^(\d+)$/;
-my $svcnum = $1;
+my @svcnums;
+my ($pkgnum, $svcpart, $custnum);
+if( $cgi->param('svcnum') ) {
+ @svcnums = grep { $_ } map { /^(\d+)$/ && $1 } $cgi->param('svcnum');
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $custnum = $cgi->param('custnum');
+}
+else {
+ @svcnums = map { /^(\d+)$/ && $1 } $cgi->keywords;
+}
-#my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$svcnum});
-#die "Unknown svcnum!" unless $svc_acct;
+my $error = '';
+foreach my $svcnum (@svcnums) {
-my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
-die "Unknown svcnum!" unless $cust_svc;
+ my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+ die "Unknown svcnum!" unless $cust_svc;
-my $custnum = $cust_svc->cust_pkg->custnum;
+ $custnum ||= $cust_svc->cust_pkg->custnum;
-my $error = $cust_svc->cancel;
+ $error .= $cust_svc->cancel;
+
+}
</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-address_standardize.html b/httemplate/misc/xmlhttp-cust_main-address_standardize.html
index 3b9e142..d0627cd 100644
--- a/httemplate/misc/xmlhttp-cust_main-address_standardize.html
+++ b/httemplate/misc/xmlhttp-cust_main-address_standardize.html
@@ -28,6 +28,7 @@ if ( $sub eq 'address_standardize' ) {
} );
foreach my $pre ( '', 'ship_' ) {
+ next unless ($pre || !$arg{onlyship});
my($zip5, $zip4) = split('-',$arg{$pre.'zip'});
diff --git a/httemplate/misc/xmlhttp-cust_main-censustract.html b/httemplate/misc/xmlhttp-cust_main-censustract.html
index 9d588d7..3ba68af 100644
--- a/httemplate/misc/xmlhttp-cust_main-censustract.html
+++ b/httemplate/misc/xmlhttp-cust_main-censustract.html
@@ -36,23 +36,32 @@ if ( $sub eq 'censustract' ) {
my $content = $res->content;
my $p = new HTML::TokeParser \$content;
my $viewstate;
+ my $eventvalidation;
while (my $token = $p->get_tag('input') ) {
- next unless $token->[1]->{name} eq '__VIEWSTATE';
- $viewstate = $token->[1]->{value};
- last;
+ if ($token->[1]->{name} eq '__VIEWSTATE') {
+ $viewstate = $token->[1]->{value};
+ }
+ if ($token->[1]->{name} eq '__EVENTVALIDATION') {
+ $eventvalidation = $token->[1]->{value};
+ }
+ last if $viewstate && $eventvalidation;
}
- unless ($viewstate) {
+ unless ($viewstate && $eventvalidation ) {
- $error = "no __VIEWSTATE found";
+ $error = "either no __VIEWSTATE or __EVENTVALIDATION found";
} else {
my($zip5, $zip4) = split('-',$arg{zip});
+ #ugh workaround a mess at ffiec
+ $arg{year} = " $arg{year}" unless $arg{year} = "2010";
my @ffiec_args = (
__VIEWSTATE => $viewstate,
+ __EVENTVALIDATION => $eventvalidation,
ddlbYear => $arg{year},
+ ddlbYear => ' 2009',
txtAddress => $arg{address},
txtCity => $arg{city},
ddlbState => $arg{state},
@@ -62,6 +71,7 @@ if ( $sub eq 'censustract' ) {
warn join("\n", @ffiec_args )
if $DEBUG;
+ push @{ $ua->requests_redirectable }, 'POST';
$res = $ua->request( POST( $url, \@ffiec_args ) );
warn $res->as_string
if $DEBUG > 1;
@@ -74,6 +84,7 @@ if ( $sub eq 'censustract' ) {
my @id = qw( MSACode StateCode CountyCode TractCode );
$content = $res->content;
+ warn $res->content if $DEBUG > 1;
$p = new HTML::TokeParser \$content;
my $prefix = 'UcGeoResult11_lb';
my $compare =
diff --git a/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi
new file mode 100644
index 0000000..71e2da5
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi
@@ -0,0 +1,24 @@
+% if ( $sub eq 'discount_terms' ) {
+%
+% my $return = [];
+% my $custnum = $cgi->param('arg');
+% my $cust_main = '';
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'custnum' => $custnum },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+%
+% if ($cust_main) {
+% $return = [ map [ $_, "$_ months" ], $cust_main->discount_terms ];
+% }
+%
+<% objToJson($return) %>
+% }
+<%init>
+
+my $conf = new FS::Conf;
+
+my $sub = $cgi->param('sub');
+
+</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-search.cgi b/httemplate/misc/xmlhttp-cust_main-search.cgi
index 26e68b5..481bea2 100644
--- a/httemplate/misc/xmlhttp-cust_main-search.cgi
+++ b/httemplate/misc/xmlhttp-cust_main-search.cgi
@@ -2,10 +2,10 @@
%
% my $custnum = $cgi->param('arg');
% my $cust_main = '';
-% if ( $custnum <= 2147483647 ) {
+% if ( $custnum =~ /^(\d+)$/ and $1 <= 2147483647 ) {
% $cust_main = qsearchs({
% 'table' => 'cust_main',
-% 'hashref' => { 'custnum' => $custnum },
+% 'hashref' => { 'custnum' => $1 },
% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
% });
% }
@@ -22,7 +22,9 @@
% } elsif ( $sub eq 'smart_search' ) {
%
% my $string = $cgi->param('arg');
-% my @cust_main = smart_search( 'search' => $string );
+% my @cust_main = smart_search( 'search' => $string,
+% 'no_fuzzy_on_exact' => 1, #pref?
+% );
% my $return = [ map [ $_->custnum, $_->name ], @cust_main ];
%
<% objToJson($return) %>