summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2012-07-08 22:45:58 -0700
committerIvan Kohler <ivan@freeside.biz>2012-07-08 22:45:58 -0700
commita6fe07e49e3fc12169e801b1ed6874c3a5bd8500 (patch)
treeb87a7e6f37da5c8e13eb4d4653cfc8ce9239d8f0 /httemplate
parente27244386c346f459d1569db26344407a0372a05 (diff)
parent005424d0c899aa899f43f583a6c74deb13ea4be1 (diff)
Merge branch 'master' of git.freeside.biz:/home/git/freeside
Conflicts: httemplate/misc/process/cancel_pkg.html
Diffstat (limited to 'httemplate')
-rw-r--r--httemplate/browse/cust_class.html7
-rw-r--r--httemplate/browse/ftp_target.html56
-rw-r--r--httemplate/browse/msg_template.html33
-rw-r--r--httemplate/browse/part_event.html2
-rwxr-xr-xhttemplate/browse/part_pkg.cgi2
-rwxr-xr-xhttemplate/browse/part_referral.html35
-rw-r--r--httemplate/browse/part_svc_class.html34
-rw-r--r--httemplate/browse/payment_gateway.html4
-rwxr-xr-xhttemplate/browse/sales.cgi100
-rw-r--r--httemplate/browse/tower.html16
-rw-r--r--httemplate/config/config-process.cgi4
-rw-r--r--httemplate/config/config-view.cgi2
-rw-r--r--httemplate/config/config.cgi39
-rw-r--r--httemplate/docs/about.html52
-rw-r--r--httemplate/docs/credits.html7
-rw-r--r--httemplate/docs/part_svc-table.html63
-rw-r--r--httemplate/edit/cust_class.html23
-rwxr-xr-xhttemplate/edit/cust_main.cgi211
-rw-r--r--httemplate/edit/cust_main/after_bill_location.html12
-rw-r--r--httemplate/edit/cust_main/before_bill_location.html10
-rw-r--r--httemplate/edit/cust_main/billing.html84
-rw-r--r--httemplate/edit/cust_main/birthdate.html31
-rw-r--r--httemplate/edit/cust_main/bottomfixup.js28
-rw-r--r--httemplate/edit/cust_main/company.html7
-rw-r--r--httemplate/edit/cust_main/fax.html5
-rw-r--r--httemplate/edit/cust_main/name.html53
-rw-r--r--httemplate/edit/cust_main/phones.html29
-rw-r--r--httemplate/edit/cust_main/stateid.html39
-rw-r--r--httemplate/edit/cust_main/top_misc.html27
-rwxr-xr-xhttemplate/edit/cust_pay.cgi36
-rw-r--r--httemplate/edit/elements/class_Common.html9
-rw-r--r--httemplate/edit/elements/edit.html22
-rw-r--r--httemplate/edit/elements/svc_Common.html14
-rwxr-xr-xhttemplate/edit/ftp_target.html46
-rw-r--r--httemplate/edit/invoice_template.html3
-rw-r--r--httemplate/edit/msg_template.html129
-rw-r--r--httemplate/edit/part_export.cgi3
-rwxr-xr-xhttemplate/edit/part_referral.html5
-rwxr-xr-xhttemplate/edit/part_svc.cgi131
-rw-r--r--httemplate/edit/part_svc_class.html6
-rw-r--r--httemplate/edit/payment_gateway.html56
-rw-r--r--httemplate/edit/pkg_class.html2
-rw-r--r--httemplate/edit/process/cust_location.cgi6
-rwxr-xr-xhttemplate/edit/process/cust_main.cgi56
-rwxr-xr-xhttemplate/edit/process/cust_pay.cgi13
-rw-r--r--httemplate/edit/process/elements/process.html2
-rw-r--r--httemplate/edit/process/ftp_target.html12
-rw-r--r--httemplate/edit/process/msg_template.html4
-rw-r--r--httemplate/edit/process/part_svc_class.html11
-rw-r--r--httemplate/edit/process/quick-cust_pkg.cgi4
-rw-r--r--httemplate/edit/process/quotation.html11
-rw-r--r--httemplate/edit/process/sales.cgi23
-rw-r--r--httemplate/edit/process/svc_broadband.cgi6
-rw-r--r--httemplate/edit/prospect_main.html3
-rw-r--r--httemplate/edit/quick-charge.html106
-rw-r--r--httemplate/edit/quotation.html30
-rw-r--r--httemplate/edit/radius_group.html1
-rwxr-xr-xhttemplate/edit/sales.cgi79
-rw-r--r--httemplate/edit/svc_broadband.cgi16
-rw-r--r--httemplate/edit/tower.html4
-rw-r--r--httemplate/elements/city.html6
-rw-r--r--httemplate/elements/coord-links.html12
-rw-r--r--httemplate/elements/customer-table.html375
-rw-r--r--httemplate/elements/form-create_ticket.html38
-rw-r--r--httemplate/elements/freeside.css48
-rw-r--r--httemplate/elements/header.html6
-rw-r--r--httemplate/elements/init_overlib.html11
-rw-r--r--httemplate/elements/location.html99
-rw-r--r--httemplate/elements/menu.html150
-rw-r--r--httemplate/elements/order_pkg_link.html26
-rw-r--r--httemplate/elements/popup_link-cust_main.html23
-rw-r--r--httemplate/elements/progress-init.html6
-rw-r--r--httemplate/elements/searchbar-ticket.html2
-rw-r--r--httemplate/elements/select-cust-part_pkg.html6
-rw-r--r--httemplate/elements/select-part_svc_class.html22
-rw-r--r--httemplate/elements/select-rt-customfield.html34
-rw-r--r--httemplate/elements/select-table.html13
-rw-r--r--httemplate/elements/standardize_locations.js5
-rw-r--r--httemplate/elements/table-grid.html13
-rw-r--r--httemplate/elements/table-tickets.html159
-rw-r--r--httemplate/elements/tr-cust_svc.html2
-rw-r--r--httemplate/elements/tr-fixed-cust_main.html15
-rw-r--r--httemplate/elements/tr-fixed-date.html13
-rw-r--r--httemplate/elements/tr-fixed-prospect_main.html15
-rw-r--r--httemplate/elements/tr-fixed.html16
-rw-r--r--httemplate/elements/tr-select-agent.html19
-rw-r--r--httemplate/elements/tr-select-cust-part_pkg.html27
-rw-r--r--httemplate/elements/tr-select-cust_location.html52
-rw-r--r--httemplate/elements/tr-select-cust_tag.html2
-rw-r--r--httemplate/elements/tr-select-from_to.html2
-rw-r--r--httemplate/elements/tr-select-part_svc_class.html27
-rw-r--r--httemplate/elements/tr-select-router_block_ip.html33
-rw-r--r--httemplate/elements/xmenu.css2
-rw-r--r--httemplate/elements/xmenu.top.css2
-rw-r--r--httemplate/graph/cust_bill_pkg.cgi92
-rw-r--r--httemplate/graph/cust_signup.html29
-rw-r--r--httemplate/graph/elements/monthly.html11
-rw-r--r--httemplate/graph/money_time.cgi31
-rw-r--r--httemplate/graph/report_cust_bill_pkg.html16
-rw-r--r--httemplate/graph/report_cust_signup.html6
-rw-r--r--httemplate/graph/report_money_time.html7
-rw-r--r--httemplate/loginout/logout.html4
-rw-r--r--httemplate/misc/batch-cust_pay.html375
-rwxr-xr-xhttemplate/misc/cancel_pkg.html79
-rw-r--r--httemplate/misc/cust-part_pkg.cgi18
-rwxr-xr-xhttemplate/misc/cust_main-cancel.cgi2
-rw-r--r--httemplate/misc/cust_main-import.cgi4
-rwxr-xr-xhttemplate/misc/cust_main-suspend.cgi75
-rwxr-xr-xhttemplate/misc/cust_main-unsuspend.cgi56
-rw-r--r--httemplate/misc/delete-ftp_target.html18
-rw-r--r--httemplate/misc/download-batch.cgi13
-rw-r--r--httemplate/misc/order_pkg.html14
-rw-r--r--httemplate/misc/process/batch-cust_pay.cgi115
-rwxr-xr-xhttemplate/misc/process/cancel_pkg.html40
-rw-r--r--httemplate/misc/suspend_cust.html79
-rw-r--r--httemplate/misc/unsuspend_cust.html68
-rw-r--r--httemplate/misc/xmlhttp-cust_bill-search.html18
-rw-r--r--httemplate/misc/xmlhttp-cust_main-discount_terms.cgi19
-rw-r--r--httemplate/misc/xmlhttp-cust_main-search.cgi90
-rw-r--r--httemplate/pref/pref-process.html2
-rw-r--r--httemplate/pref/pref.html9
-rwxr-xr-xhttemplate/search/cust_bill.html4
-rw-r--r--httemplate/search/cust_bill_pay.html7
-rw-r--r--httemplate/search/cust_bill_pkg.cgi158
-rw-r--r--httemplate/search/cust_bill_pkg_referral.html287
-rwxr-xr-xhttemplate/search/cust_credit.html7
-rw-r--r--httemplate/search/cust_credit_bill.html7
-rw-r--r--httemplate/search/cust_credit_bill_pkg.html4
-rw-r--r--httemplate/search/cust_credit_refund.html7
-rwxr-xr-xhttemplate/search/cust_main.cgi2
-rwxr-xr-xhttemplate/search/cust_main.html21
-rwxr-xr-xhttemplate/search/cust_pkg.cgi11
-rw-r--r--httemplate/search/cust_pkg_summary.cgi2
-rw-r--r--httemplate/search/cust_pkg_summary.html4
-rw-r--r--httemplate/search/cust_pkg_susp.cgi2
-rw-r--r--httemplate/search/cust_pkg_susp.html4
-rw-r--r--httemplate/search/cust_svc.html123
-rw-r--r--httemplate/search/customer_accounting_summary.html14
-rw-r--r--httemplate/search/elements/cust_main_dayranges.html26
-rw-r--r--httemplate/search/elements/cust_pay_batch_top.html35
-rwxr-xr-xhttemplate/search/elements/cust_pay_or_refund.html7
-rw-r--r--httemplate/search/elements/search-html.html6
-rw-r--r--httemplate/search/elements/search-xls.html22
-rw-r--r--httemplate/search/elements/search.html10
-rwxr-xr-xhttemplate/search/pay_batch.cgi20
-rw-r--r--httemplate/search/prepaid_income.html (renamed from httemplate/search/report_prepaid_income.cgi)129
-rw-r--r--httemplate/search/report_cust_bill_pkg_referral.html55
-rwxr-xr-xhttemplate/search/report_cust_main.html49
-rwxr-xr-xhttemplate/search/report_cust_pkg.html43
-rwxr-xr-xhttemplate/search/report_customer_accounting_summary.html8
-rw-r--r--httemplate/search/report_prepaid_income.html19
-rwxr-xr-xhttemplate/search/report_receivables.html2
-rw-r--r--httemplate/search/report_rt_ticket.html32
-rw-r--r--httemplate/search/report_rt_transaction.html13
-rwxr-xr-xhttemplate/search/report_svc_acct.html2
-rwxr-xr-xhttemplate/search/report_svc_broadband.html2
-rwxr-xr-xhttemplate/search/report_svc_hardware.html2
-rwxr-xr-xhttemplate/search/report_tax.cgi165
-rw-r--r--httemplate/search/rt_ticket.html125
-rw-r--r--httemplate/search/rt_transaction.html125
-rw-r--r--httemplate/search/unearned_detail.html257
-rwxr-xr-xhttemplate/view/cust_main.cgi49
-rw-r--r--httemplate/view/cust_main/billing.html40
-rw-r--r--httemplate/view/cust_main/contacts.html148
-rw-r--r--httemplate/view/cust_main/custom_content.html31
-rw-r--r--httemplate/view/cust_main/custom_content/.birthdate.html.swpbin0 -> 12288 bytes
-rw-r--r--httemplate/view/cust_main/custom_content/.small_custview.html.swpbin0 -> 12288 bytes
-rw-r--r--httemplate/view/cust_main/custom_content/.spouse_birthdate.html.swpbin0 -> 12288 bytes
-rw-r--r--httemplate/view/cust_main/custom_content/.svc_Common.html.swpbin0 -> 12288 bytes
-rw-r--r--httemplate/view/cust_main/custom_content/.svc_acct.html.swpbin0 -> 12288 bytes
-rw-r--r--httemplate/view/cust_main/custom_content/.svc_hardware.html.swpbin0 -> 12288 bytes
-rw-r--r--httemplate/view/cust_main/custom_content/.svc_phone.html.swpbin0 -> 12288 bytes
-rw-r--r--httemplate/view/cust_main/custom_content/birthdate.html15
-rw-r--r--httemplate/view/cust_main/custom_content/small_custview.html4
-rw-r--r--httemplate/view/cust_main/custom_content/spouse_birthdate.html15
-rw-r--r--httemplate/view/cust_main/custom_content/svc_Common.html40
-rw-r--r--httemplate/view/cust_main/custom_content/svc_acct.html7
-rw-r--r--httemplate/view/cust_main/custom_content/svc_hardware.html7
-rw-r--r--httemplate/view/cust_main/custom_content/svc_phone.html7
-rwxr-xr-xhttemplate/view/cust_main/locations.html22
-rw-r--r--httemplate/view/cust_main/misc.html20
-rw-r--r--httemplate/view/cust_main/order_pkg_link.html22
-rwxr-xr-xhttemplate/view/cust_main/packages.html2
-rw-r--r--httemplate/view/cust_main/packages/services.html2
-rw-r--r--httemplate/view/cust_main/packages/status.html34
-rw-r--r--httemplate/view/cust_main/payment_history.html12
-rw-r--r--httemplate/view/cust_main/tickets.html165
-rwxr-xr-xhttemplate/view/cust_msg.html2
-rw-r--r--httemplate/view/cust_pay.html22
-rw-r--r--httemplate/view/directions.html1
-rw-r--r--httemplate/view/elements/svc_Common.html4
-rwxr-xr-xhttemplate/view/logo-agent.cgi10
-rw-r--r--httemplate/view/prospect_main.html38
-rw-r--r--httemplate/view/qual.cgi6
-rwxr-xr-xhttemplate/view/quotation.html93
-rwxr-xr-xhttemplate/view/svc_acct.cgi6
-rw-r--r--httemplate/view/svc_acct/basics.html3
-rwxr-xr-xhttemplate/view/svc_domain.cgi4
-rw-r--r--httemplate/view/svc_external.cgi5
-rwxr-xr-xhttemplate/view/svc_forward.cgi4
-rw-r--r--httemplate/view/svc_www.cgi4
201 files changed, 5354 insertions, 1743 deletions
diff --git a/httemplate/browse/cust_class.html b/httemplate/browse/cust_class.html
index d7c622837..70a279a05 100644
--- a/httemplate/browse/cust_class.html
+++ b/httemplate/browse/cust_class.html
@@ -42,4 +42,11 @@ if ($sth->fetchrow_arrayref->[0]) {
push @$links, $link;
}
+my $conf = new FS::Conf;
+if ( $conf->exists('cust_class-tax_exempt') ) {
+ push @$header, 'Tax exempt';
+ push @$fields, 'tax';
+ push @$links, '';
+}
+
</%init>
diff --git a/httemplate/browse/ftp_target.html b/httemplate/browse/ftp_target.html
new file mode 100644
index 000000000..4a5782058
--- /dev/null
+++ b/httemplate/browse/ftp_target.html
@@ -0,0 +1,56 @@
+<& elements/browse.html,
+ 'title' => 'FTP targets',
+ 'menubar' => [ 'Add a target' => $p.'edit/ftp_target.html', ],
+ 'name' => 'FTP targets',
+ 'query' => { 'table' => 'ftp_target',
+ 'hashref' => {},
+ },
+ 'count_query' => $count_query,
+ 'header' => [ '#',
+ 'Server',
+ 'Username',
+ 'Password',
+ 'Path',
+ 'Protocol',
+ '', #handling
+ ],
+ 'fields' => [ 'targetnum',
+ 'hostname',
+ 'username',
+ 'password',
+ 'path',
+ sub {
+ my $ftp_target = shift;
+ my $label;
+ if ($ftp_target->secure) {
+ $label = 'SFTP';
+ $label .= ' (port '.$ftp_target->port.')'
+ if $ftp_target->port != 22;
+ }
+ else {
+ $label = 'FTP';
+ $label .= ' (port '.$ftp_target->port.')'
+ if $ftp_target->port != 21;
+ }
+ $label;
+ },
+ 'handling',
+ ],
+ 'links' => [ $link, $link ],
+&>
+</TABLE>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+my $count_query = 'SELECT COUNT(*) FROM ftp_target';
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $link = [ $p.'edit/ftp_target.html?', 'targetnum' ];
+</%init>
diff --git a/httemplate/browse/msg_template.html b/httemplate/browse/msg_template.html
index 8a6ccf741..50afc283e 100644
--- a/httemplate/browse/msg_template.html
+++ b/httemplate/browse/msg_template.html
@@ -1,29 +1,32 @@
<% include( 'elements/browse.html',
'title' => 'Message templates',
'name_singular' => 'template',
- 'menubar' => [ 'Add a new template' =>
- $p.'edit/msg_template.html',
- ],
- 'query' => { 'table' => 'msg_template', },
- 'count_query' => 'SELECT COUNT(*) FROM msg_template',
- 'disableable' => 1,
+ 'menubar' => \@menubar,
+ 'query' => { 'table' => 'msg_template', },
+ 'count_query' => 'SELECT COUNT(*) FROM msg_template',
+ 'disableable' => 1,
'disabled_statuspos' => 2,
'agent_virt' => 1,
- 'agent_null_right' => ['Edit global templates','Configuration'],
+ 'agent_null_right' => ['View global templates','Edit global templates'],
'agent_pos' => 1,
- 'header' => [ 'Name', '', ('' x scalar @locales) ],
- 'fields' => [ 'msgname', @locales ],
- 'links' => [ $link, @locale_links ],
- 'cell_style' =>
- [ '', '', ($locale_style) x (scalar @locales) ],
+ 'header' => [ 'Name', '', map '', @locales ],
+ 'fields' => [ 'msgname', @locales ],
+ 'links' => [ $link, @locale_links ],
+ 'cell_style' => [ '', '', map $locale_style, @locales ],
)
%>
<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Edit templates')
- || $FS::CurrentUser::CurrentUser->access_right('Edit global templates')
- || $FS::CurrentUser::CurrentUser->access_right('Configuration');
+ unless $curuser->access_right([ 'View templates', 'View global templates',
+ 'Edit templates', 'Edit global templates', ]);
+
+my @menubar = ();
+if ( $curuser->access_right(['Edit templates', 'Edit global templates']) ) {
+ push @menubar, 'Add a new template' => $p.'edit/msg_template.html';
+}
my $link = [ "${p}edit/msg_template.html?msgnum=", 'msgnum' ];
diff --git a/httemplate/browse/part_event.html b/httemplate/browse/part_event.html
index 03996435e..c06a14fe7 100644
--- a/httemplate/browse/part_event.html
+++ b/httemplate/browse/part_event.html
@@ -174,7 +174,7 @@ my $html_init =
qq!<A HREF="${p}edit/part_event.html"><I>Add a new event</I></A>!.
'&nbsp;or&nbsp;<SELECT NAME="clone"><OPTION></OPTION>';
-foreach my $part_event ( qsearch('part_event', {'diabled'=>''}) ) {
+foreach my $part_event ( qsearch('part_event', {'disabled'=>''}) ) {
$html_init .= '<OPTION VALUE="'. $part_event->eventpart. '">'.
$part_event->eventpart. ': '. $part_event->event. '</OPTION>';
}
diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi
index 4ca78d718..e3d9de13a 100755
--- a/httemplate/browse/part_pkg.cgi
+++ b/httemplate/browse/part_pkg.cgi
@@ -436,7 +436,7 @@ push @fields,
[ map {
[
- { 'data' => $_,
+ { 'data' => "$_: ",
'align' => 'right',
},
{ 'data' => $part_pkg->format($_,$options{$_}),
diff --git a/httemplate/browse/part_referral.html b/httemplate/browse/part_referral.html
index 9cc32c459..c7374673f 100755
--- a/httemplate/browse/part_referral.html
+++ b/httemplate/browse/part_referral.html
@@ -6,6 +6,13 @@ Where a customer heard about your service. Tracked for informational purposes.
<A HREF="<% $p %>edit/part_referral.html"><I>Add a new advertising source</I></A>
<BR><BR>
+<% $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled advertising sources</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled advertising sources</a> )'; }
+%>
+
<% include('/elements/table-grid.html') %>
% my $bgcolor1 = '#eeeeee';
% my $bgcolor2 = '#ffffff';
@@ -13,8 +20,12 @@ Where a customer heard about your service. Tracked for informational purposes.
<TR>
<TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=2 ROWSPAN=2>Advertising source</TH>
-% if ( $show_agentnums ) {
+% if ( ! $cgi->param('showdisabled') ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc" ALIGN="center" ROWSPAN=2></TH>
+% }
+
+% if ( $show_agentnums ) {
<TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2>Agent</TH>
% }
@@ -27,7 +38,7 @@ Where a customer heard about your service. Tracked for informational purposes.
</TR>
-%foreach my $part_referral ( FS::part_referral->all_part_referral(1) ) {
+%foreach my $part_referral ( FS::part_referral->all_part_referral(1,!scalar($cgi->param('showdisabled'))) ) {
%
% if ( $bgcolor eq $bgcolor1 ) {
% $bgcolor = $bgcolor2;
@@ -55,6 +66,16 @@ Where a customer heard about your service. Tracked for informational purposes.
% }
<% $part_referral->referral %><% $a ? '</A>' : '' %></TD>
+
+% if ( ! $cgi->param('showdisabled') ) {
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="center">
+ <% $part_referral->disabled
+ ? '<FONT COLOR="#FF0000"><B>DISABLED</B></FONT>'
+ : '<FONT COLOR="#00CC00"><B>Active</B></FONT>'
+ %>
+ </TD>
+% }
+
% if ( $show_agentnums ) {
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $part_referral->agentnum ? $part_referral->agent->agent : '(global)' %></TD>
@@ -73,11 +94,11 @@ Where a customer heard about your service. Tracked for informational purposes.
<TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
<TR>
<TD ALIGN="right"><B><% $num_cust %></B></TD>
- <TD ALIGN="left">customers</TD>
+ <TD ALIGN="left">&nbsp;customers&nbsp;</TD>
</TR>
<TR>
<TD ALIGN="right"><B><% $num_pkg %></B></TD>
- <TD ALIGN="left">packages</TD>
+ <TD ALIGN="left">&nbsp;packages&nbsp;</TD>
</TR>
</TABLE>
</TD>
@@ -94,7 +115,7 @@ Where a customer heard about your service. Tracked for informational purposes.
% or die dbh->errstr;
<TR>
- <TD BGCOLOR="#dddddd" ALIGN="center" COLSPAN=3><B>Total</B></TD>
+ <TD BGCOLOR="#dddddd" ALIGN="center" COLSPAN=<% 2 + $show_agentnums + ! $cgi->param('showdisabled') %><B>Total</B></TD>
% for my $period ( keys %after ) {
% my @param = ( $today-$after{$period},
% $today+$before{$period},
@@ -108,11 +129,11 @@ Where a customer heard about your service. Tracked for informational purposes.
<TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>
<TR>
<TD ALIGN="right"><B><% $num_cust %></B></TD>
- <TD ALIGN="left">customers</TD>
+ <TD ALIGN="left">&nbsp;customers&nbsp;</TD>
</TR>
<TR>
<TD ALIGN="right"><B><% $num_pkg %></B></TD>
- <TD ALIGN="left">packages</TD>
+ <TD ALIGN="left">&nbsp;packages&nbsp;</TD>
</TR>
</TABLE>
</TD>
diff --git a/httemplate/browse/part_svc_class.html b/httemplate/browse/part_svc_class.html
new file mode 100644
index 000000000..73bd60305
--- /dev/null
+++ b/httemplate/browse/part_svc_class.html
@@ -0,0 +1,34 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Service classes',
+ 'html_init' => $html_init,
+ 'name' => 'service classes',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 1,
+ 'query' => { 'table' => 'part_svc_class',
+ 'hashref' => {},
+ 'order_by' => 'ORDER BY classnum',
+ },
+ 'count_query' => $count_query,
+ 'header' => $header,
+ 'fields' => $fields,
+ 'links' => $links,
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init =
+ 'Service classes are user-defined, informational types for services.<BR><BR>'.
+ qq!<A HREF="${p}edit/part_svc_class.html"><I>Add a service class</I></A><BR><BR>!;
+
+my $count_query = 'SELECT COUNT(*) FROM part_svc_class';
+
+my $link = [ $p.'edit/part_svc_class.html?', 'classnum' ];
+
+my $header = [ '#', 'Class' ];
+my $fields = [ 'classnum', 'classname' ];
+my $links = [ $link, $link ];
+
+</%init>
diff --git a/httemplate/browse/payment_gateway.html b/httemplate/browse/payment_gateway.html
index a06e5cf7c..7a8a668d7 100644
--- a/httemplate/browse/payment_gateway.html
+++ b/httemplate/browse/payment_gateway.html
@@ -77,9 +77,9 @@ my $options_sub = sub {
my $html = '<TABLE CELLSPACING=0 CELLPADDING=0>';
- my %options = $payment_gateway->options;
+ tie my %options, 'Tie::IxHash', $payment_gateway->options;
foreach my $option ( keys %options ) {
- $html .= '<TR><TH>'. $option. ':</TH>'.
+ $html .= '<TR><TH ALIGN="right">'. $option. ':</TH>'.
'<TD>'. $options{$option}. '</TD></TR>';
}
$html .= '</TABLE>';
diff --git a/httemplate/browse/sales.cgi b/httemplate/browse/sales.cgi
new file mode 100755
index 000000000..af098121d
--- /dev/null
+++ b/httemplate/browse/sales.cgi
@@ -0,0 +1,100 @@
+<% include("/elements/header.html",'Sales Listing', menubar(
+ 'Add new sales person' => '../edit/sales.cgi'
+)) %>
+Sales people bring in business.<BR><BR>
+% if ( dbdef->table('sales')->column('disabled') ) {
+
+ <% $cgi->param('showdisabled')
+ ? do { $cgi->param('showdisabled', 0);
+ '( <a href="'. $cgi->self_url. '">hide disabled sales people</a> )'; }
+ : do { $cgi->param('showdisabled', 1);
+ '( <a href="'. $cgi->self_url. '">show disabled sales people</a> )'; }
+ %>
+% }
+
+
+<% include('/elements/table-grid.html') %>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=<% ( $cgi->param('showdisabled') || !dbdef->table('sales')->column('disabled') ) ? 2 : 3 %>>Sales person</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Agent</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc">Access Groups</TH>
+</TR>
+
+%foreach my $sales ( sort {
+% $a->getfield('salesnum') cmp $b->getfield('salesnum')
+%} qsearch('sales', \%search ) ) {
+%
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/sales.cgi?<% $sales->salesnum %>"><% $sales->salesnum %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/sales.cgi?<% $sales->salesnum %>"><% $sales->salesperson %></A>
+ </TD>
+
+% if ( ! $cgi->param('showdisabled') ) {
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>" ALIGN="center">
+ <% $sales->disabled ? '<FONT COLOR="#FF0000"><B>DISABLED</B></FONT>'
+ : '<FONT COLOR="#00CC00"><B>Active</B></FONT>'
+ %>
+ </TD>
+% }
+
+% my ($agent) = qsearch('agent', { 'agentnum' => $sales->agentnum });
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<%$p%>edit/sales.cgi?<% $sales->agentnum %>"><% $sales->agentnum %></A>
+ <A HREF="<%$p%>edit/agent.cgi?<% $agent->agentnum %>">(<% $agent->agent %>)<BR>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% foreach my $access_group (
+% map $_->access_group,
+% qsearch('access_groupsales', { 'salesnum' => $sales->salesnum })
+% ) {
+ <A HREF="<%$p%>edit/access_group.html?<% $access_group->groupnum %>"><% $access_group->groupname |h %><BR>
+% }
+ </TD>
+
+ </TR>
+% }
+
+ </TABLE>
+
+<SCRIPT TYPE="text/javascript">
+ function areyousure(what, href) {
+ if ( confirm("Are you sure you want to " + what + "?") == true )
+ window.location.href = href;
+ }
+</SCRIPT>
+
+ </BODY>
+</HTML>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my %search;
+if ( $cgi->param('showdisabled')
+ || !dbdef->table('agent')->column('disabled') ) {
+ %search = ();
+} else {
+ %search = ( 'disabled' => '' );
+}
+
+my $conf = new FS::Conf;
+
+</%init>
diff --git a/httemplate/browse/tower.html b/httemplate/browse/tower.html
index 7767a3c36..7f096a748 100644
--- a/httemplate/browse/tower.html
+++ b/httemplate/browse/tower.html
@@ -8,9 +8,10 @@
'count_query' => 'SELECT COUNT(*) FROM tower',
'disableable' => 1,
'disabled_statuspos' => 1,
- 'header' => [ 'Name', 'Sectors', ],
+ 'header' => [ 'Name', 'Sectors', 'Coordinates'],
'fields' => [ $tower_sub,
$sector_sub,
+ $coord_sub,
],
'links' => [ ],
)
@@ -49,6 +50,19 @@ my $num_svc_links = sub {
},
};
+my $coord_sub = sub {
+ my $tower = shift;
+
+ my $coords = $m->scomp("/elements/coord-links.html", $tower->latitude, $tower->longitude, $tower->towername);
+
+ [
+ [
+ { 'data' => "Latitude: " . $tower->latitude . "<br>Longitude: " . $tower->longitude, },
+ { 'data' => $coords, 'link' => "Coordinates", },
+ ],
+ ]
+};
+
my $tower_sub = sub {
my $tower = shift;
my $sectors = join(',',
diff --git a/httemplate/config/config-process.cgi b/httemplate/config/config-process.cgi
index 3dcb1d3ef..f1cbb1831 100644
--- a/httemplate/config/config-process.cgi
+++ b/httemplate/config/config-process.cgi
@@ -47,7 +47,7 @@
'</pre></font>';
% } elsif ( $type eq 'checkbox' ) {
-% if ( $conf->exists($i->key, $agentnum) ) {
+% if ( $conf->config_bool($i->key, $agentnum) ) {
configCell.style.backgroundColor = '#00ff00';
configCell.innerHTML = 'YES';
% } else {
@@ -184,7 +184,7 @@ foreach my $type ( ref($i->type) ? @{$i->type} : $i->type ) {
}
# warn @touch;
$conf->touch($_, $agentnum) foreach @touch;
-$conf->delete($_, $agentnum) foreach @delete;
+$conf->delete_bool($_, $agentnum) foreach @delete;
if (scalar(@error)) {
$cgi->param('error', join(' ', @error));
diff --git a/httemplate/config/config-view.cgi b/httemplate/config/config-view.cgi
index 02acd5853..02a24adbf 100644
--- a/httemplate/config/config-view.cgi
+++ b/httemplate/config/config-view.cgi
@@ -211,7 +211,7 @@ invoice language options:
% } elsif ( $type eq 'checkbox' ) {
<tr>
- <td id="<% $agentnum.$i->key.$n %>" bgcolor="#<% $conf->exists($i->key, $agentnum) ? '00ff00">YES' : 'ff0000">NO' %></td>
+ <td id="<% $agentnum.$i->key.$n %>" bgcolor="#<% $conf->config_bool($i->key, $agentnum) ? '00ff00">YES' : 'ff0000">NO' %></td>
</tr>
% } elsif ( $type eq 'select' && $i->select_hash ) {
diff --git a/httemplate/config/config.cgi b/httemplate/config/config.cgi
index 6a1eaecf7..a4f9890a5 100644
--- a/httemplate/config/config.cgi
+++ b/httemplate/config/config.cgi
@@ -40,12 +40,14 @@ Setting <b><% $key %></b>
<table><tr><td>
% my $n = 0;
+% my $submit = 0;
% foreach my $type (@types) {
% if ( $type eq '' ) {
<font color="#ff0000">no type</font>
% } elsif ( $type eq 'image' ) {
+% $submit++;
<% $conf->exists($key, $agentnum)
? 'Current image<br>'.
@@ -59,24 +61,37 @@ Setting <b><% $key %></b>
New image filename <input type="file" name="<% "$key$n" %>">
% } elsif ( $type eq 'binary' ) {
+% $submit++;
Filename <input type="file" name="<% "$key$n" %>">
% } elsif ( $type eq 'textarea' ) {
+% $submit++;
<textarea name="<% "$key$n" %>" rows=12 cols=78 wrap="off"><% join("\n", $conf->config($key, $agentnum)) |h %></textarea>
% } elsif ( $type eq 'checkbox' ) {
+%
+% if ( $agentnum && $conf->exists($key) && ! $agent_bool ) {
- <input name="<% "$key$n" %>" type="checkbox" value="1"
- <% $conf->exists($key, $agentnum) ? 'CHECKED' : '' %> >
+ <input name="<% "$key$n" %>" type="checkbox" value="1" CHECKED DISABLED>
+ <FONT SIZE="-1"><I>(global setting cannot yet be overridden)</I></FONT>
-% } elsif ( $type eq 'text' ) {
+% } else {
+% $submit++;
- <input name="<% "$key$n" %>" type="text" value="<% $conf->exists($key, $agentnum) ? $conf->config($key, $agentnum) : '' |h %>">
+ <input name="<% "$key$n" %>" type="checkbox" value="1"
+ <% $conf->config_bool($key, $agentnum) ? 'CHECKED' : '' %> >
+% }
-% } elsif ( $type eq 'select' || $type eq 'selectmultiple' ) {
+% } elsif ( $type eq 'text' ) {
+% $submit++;
+ <input name="<% "$key$n" %>" type="text" value="<% $conf->exists($key, $agentnum) ? $conf->config($key, $agentnum) : '' |h %>">
+
+% } elsif ( $type eq 'select' || $type eq 'selectmultiple' ) {
+% $submit++;
+
<select name="<% "$key$n" %>" <% $type eq 'selectmultiple' ? 'MULTIPLE' : '' %>>
%
@@ -131,7 +146,8 @@ Setting <b><% $key %></b>
</select>
-% } elsif ( $type eq 'select-sub' ) {
+% } elsif ( $type eq 'select-sub' ) {
+% $submit++;
<select name="<% "$key$n" %>" <% $config_item->multiple ? 'MULTIPLE' : '' %>>
@@ -167,8 +183,8 @@ Setting <b><% $key %></b>
</select>
-% } elsif ( $type eq 'editlist' ) {
-%
+% } elsif ( $type eq 'editlist' ) {
+% $submit++;
<script>
function doremove<% "$key$n" %>() {
fromObject = document.OneTrueForm.<% "$key$n" %>;
@@ -284,6 +300,7 @@ Setting <b><% $key %></b>
</tr></table>
% } elsif ( $element_types{$type} ) {
+% $submit++;
%
% my %opt = ( 'element_name' => "$key$n",
% 'empty_label' => ' ',
@@ -313,7 +330,10 @@ Setting <b><% $key %></b>
% }
</tr>
</table>
-<INPUT TYPE="submit" VALUE="<% $title %>">
+
+% if ( $submit ) {
+ <INPUT TYPE="submit" VALUE="<% $title %>">
+% }
</FORM>
</BODY>
@@ -365,5 +385,6 @@ my $config_item = $confitems{$key};
my $description = $config_item->description;
my $config_type = $config_item->type;
my @types = ref($config_type) ? @$config_type : ($config_type);
+my $agent_bool = $config_item->agent_bool;
</%init>
diff --git a/httemplate/docs/about.html b/httemplate/docs/about.html
index f0994e506..33b21a3ad 100644
--- a/httemplate/docs/about.html
+++ b/httemplate/docs/about.html
@@ -1,14 +1,34 @@
-<% include('/elements/header-popup.html', { title=>'Freeside', nobr=>1 } ) %>
+<% include('/elements/header-popup.html', { title=>$title, nobr=>1 } ) %>
<% include('/elements/init_overlib.html') %>
<CENTER>
-<IMG SRC="<%$fsurl%>images/small-logo.png" BORDER="0"><BR>
-<H3>version <% $FS::VERSION %></H3>
+% if ( $agentnum ) {
+
+ <IMG SRC="<%$fsurl%>view/logo-agent.cgi?agentnum=<%$agentnum%>" BORDER="0"><BR>
+
+% my $url = $conf->config('company_url', $agentnum);
+% if ( $url ) {
+ <BR><BR>
+ <FONT SIZE="+1"><A HREF="<% $conf->config('company_url', $agentnum) %>" TARGET="_top"><%$title%> homepage</A></FONT>
+% }
+
+% } else {
+
+ <IMG SRC="<%$fsurl%>images/small-logo.png" BORDER="0"><BR>
+ <H3>version <% $FS::VERSION %></H3>
+
+% }
</CENTER>
<CENTER>
-<FONT SIZE="-1">&copy; 2011 Freeside Internet Services, Inc.<BR>
+% if ( $agentnum ) {
+ <BR><BR>
+ <FONT SIZE="-2">Based on Freeside version <% $FS::VERSION %><BR>
+% } else {
+ <FONT SIZE="-1">
+% }
+&copy; 2012 Freeside Internet Services, Inc.<BR>
All rights reserved.<BR>
Licensed under the terms of the<BR>
GNU <b>Affero</b> General Public License.<BR>
@@ -17,20 +37,28 @@ GNU <b>Affero</b> General Public License.<BR>
<BR>
<CENTER>
+% if ( $agentnum ) {
+ <FONT SIZE="-2">
+% }
<A HREF="credits.html">Credits</A>
&nbsp;&nbsp;&nbsp;&nbsp;
<A HREF="javascript:void(0)" onClick="openLicense()">License</A>
<BR><BR>
-<A HREF="http://www.freeside.biz/freeside" TARGET="_blank">Freeside homepage</A>
+<A HREF="http://www.freeside.biz/freeside" TARGET="_top">Freeside homepage</A>
+% if ( $agentnum ) {
+ </FONT>
+% }
</CENTER>
<BR>
-<CENTER>
-<FONT SIZE="-3">"The sky was yellow and the sun was blue" -R. Hunter</FONT>
-</CENTER>
+% unless ( $agentnum ) {
+ <CENTER>
+<!-- <FONT SIZE="-3">"" -R. Hunter</FONT> -->
+ </CENTER>
+% }
<SCRIPT TYPE="text/javascript">
@@ -51,3 +79,11 @@ function openLicense() {
</BODY>
</HTML>
+<%init>
+
+my $conf = new FS::Conf;
+my $agentnum = $conf->config('brand-agent');
+
+my $title = $agentnum ? $conf->config('company_name', $agentnum) : 'Freeside';
+
+</%init>
diff --git a/httemplate/docs/credits.html b/httemplate/docs/credits.html
index 9bb1decea..c1d0d8705 100644
--- a/httemplate/docs/credits.html
+++ b/httemplate/docs/credits.html
@@ -120,6 +120,9 @@ Tim Yardley<BR>
<BR>
<BR>
<BR>
+<BR>
+<BR>
+<BR>
<SCRIPT TYPE="text/javascript">
@@ -154,12 +157,12 @@ function myHeight() {
return document.body.document.height;
else
*/
- return 1850; // approx height (add more per contributors)
+ return 1900; // approx height (add more per contributors)
}
document.body.style.overflow = 'hidden';
-var startingPosition = 360;
+var startingPosition = 340;
//huh, adjust for firefox
var ua = navigator.userAgent;
diff --git a/httemplate/docs/part_svc-table.html b/httemplate/docs/part_svc-table.html
new file mode 100644
index 000000000..48841f550
--- /dev/null
+++ b/httemplate/docs/part_svc-table.html
@@ -0,0 +1,63 @@
+<& /elements/header-popup.html &>
+
+<TABLE>
+ <TR>
+ <TH ALIGN="left">Generic</TH>
+ <TH ALIGN="left">Access</TH>
+ <TH ALIGN="left">Telephony</TH>
+<!-- <TH>Hosting</TH>
+ <TH>Colocation</TH>
+-->
+ </TR>
+ <TR>
+ <TD VALIGN="top">
+ <UL STYLE="margin:0">
+ <LI><B>svc_acct</B>: Accounts - anything with a username (mailbox, shell, RADIUS, etc.)
+ <LI><B>svc_hardware</B>: Equipment supplied to customers
+ <LI><B>svc_external</B>: Externally-tracked service
+ </UL>
+ </TD>
+ <TD VALIGN="top">
+ <UL STYLE="margin:0">
+ <LI><B>svc_dsl</B>: DSL
+ <LI><B>svc_broadband</B>: Wireless broadband
+ <LI><B>svc_dish</B>: DISH Network
+ </UL>
+ </TD>
+ <TD VALIGN="top">
+ <UL STYLE="margin:0">
+ <LI><B>svc_phone</B>: Customer phone number
+ <LI><B>svc_pbx</B>: Customer PBX
+ </UL>
+ </TD>
+ </TR>
+</TABLE>
+<BR>
+<TABLE>
+ <TR>
+ <TH ALIGN="left">Hosting</TH>
+ <TH ALIGN="left">Colocation</TH>
+ </TR>
+ <TD VALIGN="top">
+ <UL STYLE="margin:0">
+ <LI><B>svc_domain</B>: Domain
+ <LI><B>svc_cert</B>: Certificate
+ <LI><B>svc_forward</B>: Mail forwarding
+ <LI><B>svc_mailinglist</B>: Mailing list
+ <LI><B>svc_www</B>: Virtual domain website
+ </UL>
+ </TD>
+ <TD VALIGN="top">
+ <UL STYLE="margin:0">
+ <LI><B>svc_port</B>: Customer router/switch port
+ </UL>
+ </TD>
+ </TR>
+<TABLE>
+<!-- <LI>svc_charge - One-time charges (Partially unimplemented)
+ <LI>svc_wo - Work orders (Partially unimplemented)
+-->
+
+</BODY>
+</HTML>
+
diff --git a/httemplate/edit/cust_class.html b/httemplate/edit/cust_class.html
index fdb58e687..8fce90588 100644
--- a/httemplate/edit/cust_class.html
+++ b/httemplate/edit/cust_class.html
@@ -1,5 +1,24 @@
<% include( 'elements/class_Common.html',
- 'name' => 'Customer Class',
- 'table' => 'cust_class',
+ 'name' => 'Customer Class',
+ 'table' => 'cust_class',
+ 'addl_fields' => \@addl_fields,
+ 'addl_labels' => { 'tax' => 'Tax Exempt' },
)
%>
+<%init>
+
+my $conf = new FS::Conf;
+
+my @addl_fields = ();
+if ( $conf->exists('cust_class-tax_exempt') ) {
+ push @addl_fields, { 'field' => 'tax',
+ 'type' => 'checkbox',
+ 'value' => 'Y',
+ };
+} else {
+ push @addl_fields, { 'field' => 'tax',
+ 'type' => 'hidden',
+ };
+}
+
+</%init>
diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi
index 399431311..ef81ebab1 100755
--- a/httemplate/edit/cust_main.cgi
+++ b/httemplate/edit/cust_main.cgi
@@ -23,120 +23,89 @@
% }
%# agent, agent_custid, refnum (advertising source), referral_custnum
+%# better section title for this?
+<FONT CLASS="fsinnerbox-title"><% mt('Basics') |h %></FONT>
<& cust_main/top_misc.html, $cust_main, 'custnum' => $custnum &>
%# birthdate
-% if ( $conf->exists('cust_main-enable_birthdate') ) {
+% if ( $conf->exists('cust_main-enable_birthdate')
+% || $conf->exists('cust_main-enable_spouse_birthdate')
+% )
+% {
<BR>
<& cust_main/birthdate.html, $cust_main &>
% }
-
-%# contact info
-
-% my $same_checked = '';
-% my $ship_disabled = '';
-% my @ship_style = ();
-% unless ( $cust_main->ship_last && $same ne 'Y' ) {
-% $same_checked = 'CHECKED';
-% $ship_disabled = 'DISABLED';
-% push @ship_style, 'background-color:#dddddd';
-% foreach (
-% qw( last first company address1 address2 city county state zip country
-% latitude longitude coord_auto
-% daytime night fax mobile )
-% ) {
-% $cust_main->set("ship_$_", $cust_main->get($_) );
-% }
-% }
-
+% my $has_ship_address = '';
+% if ( $cgi->param('error') ) {
+% $has_ship_address = !$same;
+% } elsif ( $cust_main->custnum ) {
+% $has_ship_address = $cust_main->has_ship_address;
+% }
<BR>
-<FONT CLASS="fsinnerbox-title"><% mt('Billing address') |h %></FONT>
-
-<& cust_main/contact.html,
- 'cust_main' => $cust_main,
- 'pre' => '',
- 'onchange' => 'bill_changed(this)',
- 'disabled' => '',
- 'ss' => $ss,
- 'stateid' => $stateid,
- 'same_checked' => $same_checked, #for address2 "Unit #" labeling
-&>
+<TABLE> <TR>
+ <TD STYLE="width:650px">
+%#; padding-right:2px; vertical-align:top">
+ <FONT CLASS="fsinnerbox-title"><% mt('Billing address') |h %></FONT>
+ <TABLE CLASS="fsinnerbox">
+ <& cust_main/before_bill_location.html, $cust_main &>
+ <& /elements/location.html,
+ object => $cust_main->bill_location,
+ prefix => 'bill_',
+ &>
+ <& cust_main/after_bill_location.html, $cust_main &>
+ </TABLE>
+ </TD>
+</TR>
+<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"
+ ID="same"
+ onclick="samechanged(this)"
+ onkeyup="samechanged(this)"
+ 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,
+ &>
+ </TABLE>
+ <TABLE CLASS="fsinnerbox" ID="table_ship_location_blank"
+ STYLE="display:none">
+ <TR><TD></TD></TR>
+ </TABLE>
+ </TD>
+</TR></TABLE>
<SCRIPT>
-function bill_changed(what) {
- if ( what.form.same.checked ) {
-% for (qw( last first company address1 address2 city zip latitude longitude coord_auto daytime night fax mobile )) {
- what.form.ship_<%$_%>.value = what.form.<%$_%>.value;
-% }
-
- what.form.ship_country.selectedIndex = what.form.country.selectedIndex;
-
- function fix_ship_city() {
- what.form.ship_city_select.selectedIndex = what.form.city_select.selectedIndex;
- what.form.ship_city.style.display = what.form.city.style.display;
- what.form.ship_city_select.style.display = what.form.city_select.style.display;
- }
-
- function fix_ship_county() {
- what.form.ship_county.selectedIndex = what.form.county.selectedIndex;
- ship_county_changed(what.form.ship_county, fix_ship_city );
- }
-
- function fix_ship_state() {
- what.form.ship_state.selectedIndex = what.form.state.selectedIndex;
- ship_state_changed(what.form.ship_state, fix_ship_county );
- }
-
- ship_country_changed(what.form.ship_country, fix_ship_state );
-
- }
-}
function samechanged(what) {
+%# not display = 'none', because we still want it to take up space
+%# 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 ) {
- bill_changed(what);
-
-% my @fields = qw( last first company address1 address2 city city_select county state zip country latitude longitude daytime night fax mobile );
-% for (@fields) {
- what.form.ship_<%$_%>.disabled = true;
- what.form.ship_<%$_%>.style.backgroundColor = '#dddddd';
-% }
-
-% if ( $conf->exists('cust_main-require_address2') ) {
- document.getElementById('address2_required').style.visibility = '';
- document.getElementById('address2_label').style.visibility = '';
- document.getElementById('ship_address2_required').style.visibility = 'hidden';
- document.getElementById('ship_address2_label').style.visibility = 'hidden';
-% }
-
- } else {
-
-% for (@fields) {
- what.form.ship_<%$_%>.disabled = false;
- what.form.ship_<%$_%>.style.backgroundColor = '#ffffff';
-% }
-
-% if ( $conf->exists('cust_main-require_address2') ) {
- document.getElementById('address2_required').style.visibility = 'hidden';
- document.getElementById('address2_label').style.visibility = 'hidden';
- document.getElementById('ship_address2_required').style.visibility = '';
- document.getElementById('ship_address2_label').style.visibility = '';
-% }
-
+ t2.style.width = t1.clientWidth + 'px';
+ t2.style.height = t1.clientHeight + 'px';
+ t1.style.display = 'none';
+ t2.style.display = '';
+ }
+ else {
+ t2.style.display = 'none';
+ t1.style.display = '';
}
}
+samechanged(document.getElementById('same'));
</SCRIPT>
<BR>
-<FONT CLASS="fsinnerbox-title"><% mt('Service address') |h %></FONT>
-
-<INPUT TYPE="checkbox" NAME="same" VALUE="Y" onClick="samechanged(this)" <%$same_checked%>><% mt('same as billing address') |h %>
-<& cust_main/contact.html,
- 'cust_main' => $cust_main,
- 'pre' => 'ship_',
- 'onchange' => '',
- 'disabled' => $ship_disabled,
- 'style' => \@ship_style
-&>
<& cust_main/contacts_new.html,
'cust_main' => $cust_main,
@@ -229,27 +198,49 @@ my $conf = new FS::Conf;
#get record
my($custnum, $cust_main, $ss, $stateid, $payinfo, @invoicing_list);
-my $same = '';
my $pkgpart_svcpart = ''; #first_pkg
my($username, $password, $popnum, $saved_domsvc) = ( '', '', 0, 0 ); #svc_acct
my %svc_phone = ();
my %svc_dsl = ();
my $prospectnum = '';
my $locationnum = '';
+my $same = '';
+
if ( $cgi->param('error') ) {
+ $same = ($cgi->param('same') || '') eq 'Y';
+ # false laziness w/ edit/process/cust_main.cgi
+ my %locations;
+ for my $pre (qw(bill ship)) {
+ my %hash;
+ foreach ( FS::cust_main->location_fields ) {
+ $hash{$_} = scalar($cgi->param($pre.'_'.$_));
+ }
+ $hash{'custnum'} = $cgi->param('custnum');
+ $locations{$pre} = qsearchs('cust_location', \%hash)
+ || FS::cust_location->new( \%hash );
+ }
+ if ( $same ) {
+ $locations{ship} = $locations{bill};
+ }
+
$cust_main = new FS::cust_main ( {
- map { $_, scalar($cgi->param($_)) } fields('cust_main')
+ map { ( $_, scalar($cgi->param($_)) ) } (fields('cust_main')),
+ map { ( "ship_$_", '' ) } (FS::cust_main->location_fields)
} );
+ for my $pre (qw(bill ship)) {
+ $cust_main->set($pre.'_location', $locations{$pre});
+ $cust_main->set($pre.'_locationnum', $locations{$pre}->locationnum);
+ }
+
$custnum = $cust_main->custnum;
die "access denied"
unless $curuser->access_right($custnum ? 'Edit customer' : 'New customer');
@invoicing_list = split( /\s*,\s*/, $cgi->param('invoicing_list') );
- $same = $cgi->param('same');
$cust_main->setfield('paid' => $cgi->param('paid')) if $cgi->param('paid');
$ss = $cust_main->ss; # don't mask an entered value on errors
$stateid = $cust_main->stateid; # don't mask an entered value on errors
@@ -296,7 +287,7 @@ if ( $cgi->param('error') ) {
$cust_main->paycvv($paycvv);
}
@invoicing_list = $cust_main->invoicing_list;
- $ss = $cust_main->masked('ss');
+ $ss = $conf->exists('unmask_ss') ? $cust_main->ss : $cust_main->masked('ss');
$stateid = $cust_main->masked('stateid');
$payinfo = $cust_main->paymask;
@@ -352,6 +343,20 @@ if ( $cgi->param('error') ) {
$svc_dsl{$_} = $qual->$_
foreach qw( phonenum vendor_qual_id );
}
+ else {
+ 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 }
+ )
+ );
+ $cust_main->set('ship_location',
+ FS::cust_location->new(
+ { country => $countrydefault, state => $statedefault }
+ )
+ );
+ }
if ( $cgi->param('lock_pkgpart') =~ /^(\d+)$/ ) {
my $pkgpart = $1;
@@ -364,7 +369,7 @@ if ( $cgi->param('error') ) {
}
my %keep = map { $_=>1 } qw( error tagnum lock_agentnum lock_pkgpart );
-$cgi->delete( grep !$keep{$_}, $cgi->param );
+$cgi->delete( grep { !$keep{$_} && $_ !~ /^tax_/ } $cgi->param );
my $title = $custnum ? 'Edit Customer' : 'Add Customer';
$title = mt($title);
diff --git a/httemplate/edit/cust_main/after_bill_location.html b/httemplate/edit/cust_main/after_bill_location.html
new file mode 100644
index 000000000..2f4c3b51c
--- /dev/null
+++ b/httemplate/edit/cust_main/after_bill_location.html
@@ -0,0 +1,12 @@
+% if ( ! $conf->exists('cust-edit-alt-field-order') ) {
+ <& phones.html, $cust_main &>
+ <& fax.html, $cust_main &>
+% } else {
+ <& fax.html, $cust_main &>
+ <& company.html, $cust_main &>
+% }
+<& stateid.html, $cust_main &>
+<%init>
+my $cust_main = shift;
+my $conf = FS::Conf->new;
+</%init>
diff --git a/httemplate/edit/cust_main/before_bill_location.html b/httemplate/edit/cust_main/before_bill_location.html
new file mode 100644
index 000000000..973201ecb
--- /dev/null
+++ b/httemplate/edit/cust_main/before_bill_location.html
@@ -0,0 +1,10 @@
+<& name.html, $cust_main &>
+% if ( ! $conf->exists('cust-edit-alt-field-order') ) {
+ <& company.html, $cust_main &>
+% } else {
+ <& phones.html, $cust_main &>
+% }
+<%init>
+my $cust_main = shift;
+my $conf = FS::Conf->new;
+</%init>
diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html
index 18c7ae9a6..d7082f23a 100644
--- a/httemplate/edit/cust_main/billing.html
+++ b/httemplate/edit/cust_main/billing.html
@@ -117,6 +117,15 @@
//why? select.selectedIndex = 0;
}
}
+
+ function tax_changed(what) {
+ var num = document.getElementById(what.id + '_num');
+ if ( what.checked ) {
+ num.disabled = false;
+ } else {
+ num.disabled = true;
+ }
+ }
</SCRIPT>
@@ -437,14 +446,28 @@
% my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups');
- <TR>
- <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="tax" VALUE="Y" <% $cust_main->tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt<% @exempt_groups ? ' (all taxes)' : '' %></TD>
- </TR>
+% if ( $conf->exists('cust_class-tax_exempt')
+% || $conf->exists('tax-cust_exempt-groups-require_individual_nums')
+% )
+% {
+
+ <INPUT TYPE="hidden" NAME="tax" VALUE="<% $cust_main->tax eq 'Y' ? 'Y' : '' %>">
+
+% } else {
+
+ <TR>
+ <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="tax" VALUE="Y" <% $cust_main->tax eq "Y" ? 'CHECKED' : '' %>> Tax Exempt<% @exempt_groups ? ' (all taxes)' : '' %></TD>
+ </TR>
+
+% }
% foreach my $exempt_group ( @exempt_groups ) {
-% #escape $exempt_group for NAME
+% 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 WIDTH="608" COLSPAN="2">&nbsp;&nbsp;<INPUT TYPE="checkbox" NAME="tax_<% $exempt_group %>" VALUE="Y" <% $cust_main->tax_exemption($exempt_group) ? 'CHECKED' : '' %>> Tax Exempt (<% $exempt_group %> taxes)<TD>
+ <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>
% }
@@ -458,7 +481,7 @@
? 'CHECKED'
: ''
- %>> <% mt('Postal mail invoice') |h %>
+ %>> <% mt('Postal mail invoices') |h %>
</TD>
</TR>
@@ -470,15 +493,27 @@
? 'CHECKED'
: ''
- %>> <% mt('Fax invoice') |h %>
+ %>> <% mt('Fax invoices') |h %>
</TD>
</TR>
% }
-% unless ( $conf->exists('cust-email-high-visibility')) {
<TR>
+ <TD WIDTH="608" COLSPAN="2"><INPUT TYPE="checkbox" NAME="invoice_email" VALUE="Y" <%
+
+ ( $cust_main->invoice_noemail eq 'Y' )
+ ? ''
+ : 'CHECKED'
+
+ %>> <% mt('Email invoices') |h %>
+
+ </TD>
+ </TR>
+
+% unless ( $conf->exists('cust-email-high-visibility')) {
+ <TR>
<TD ALIGN="right" WIDTH="200">
<% $conf->exists('cust_main-require_invoicing_list_email', $agentnum)
? $r : '' %>Email address(es)
@@ -562,20 +597,25 @@ function toggle(obj) {
%my @available_locales = $conf->config('available-locales');
%if ( scalar(@available_locales) ) {
-% push @available_locales, '';
-% my %locale_labels = map {
-% my %ll;
-% my %info = FS::Locales->locale_info($_);
-% $ll{$_} = $info{name} . " (" . $info{country} . ")";
-% %ll;
-% } FS::Locales->locales;
- <& /elements/tr-select.html,
- 'label' => emt('Invoicing locale'),
- 'field' => 'locale',
- 'options' => \@available_locales,
- 'labels' => \%locale_labels,
- 'curr_value' => $cust_main->locale,
- &>
+% push @available_locales, ''
+% unless $cust_main->locale && $conf->exists('cust_main-require_locale');
+% my %locale_labels = map {
+% my %ll;
+% my %info = FS::Locales->locale_info($_);
+% $ll{$_} = $info{name} . " (" . $info{country} . ")";
+% %ll;
+% } FS::Locales->locales;
+%
+% my $label = ( $conf->exists('cust_main-require_locale') ? $r : '' ).
+% emt('Invoicing locale');
+
+ <& /elements/tr-select.html,
+ 'label' => $label,
+ 'field' => 'locale',
+ 'options' => \@available_locales,
+ 'labels' => \%locale_labels,
+ 'curr_value' => $cust_main->locale,
+ &>
% }
</TABLE>
diff --git a/httemplate/edit/cust_main/birthdate.html b/httemplate/edit/cust_main/birthdate.html
index b4e78e3b9..5d6a123b1 100644
--- a/httemplate/edit/cust_main/birthdate.html
+++ b/httemplate/edit/cust_main/birthdate.html
@@ -1,16 +1,33 @@
<% ntable("#cccccc", 2) %>
- <% include( '/elements/tr-input-date-field.html',
- 'birthdate',
- $cust_main->birthdate,
- 'Date of Birth',
- ( $conf->config('date_format') || "%m/%d/%Y" ),
- 1
- )
+% # maybe put after the contact names?
+% if ( $conf->exists('cust_main-enable_birthdate') ) {
+ <% include( '/elements/tr-input-date-field.html', {
+ 'name' => 'birthdate',
+ 'value' => $cust_main->birthdate,
+ 'label' => 'Date of Birth',
+ 'format' => ( $conf->config('date_format') || "%m/%d/%Y" ),
+ 'usedatetime' => 1,
+ 'noinit' => $noinit++,
+ })
%>
+% }
+% if ( $conf->exists('cust_main-enable_spouse_birthdate') ) {
+ <% include( '/elements/tr-input-date-field.html', {
+ 'name' => 'spouse_birthdate',
+ 'value' => $cust_main->spouse_birthdate,
+ 'label' => 'Spouse Date of Birth',
+ 'format' => ( $conf->config('date_format') || "%m/%d/%Y" ),
+ 'usedatetime' => 1,
+ 'noinit' => $noinit++,
+ })
+ %>
+% }
</TABLE>
<%init>
my( $cust_main, %opt ) = @_;
my $conf = new FS::Conf;
+my $noinit = 0;
+
</%init>
diff --git a/httemplate/edit/cust_main/bottomfixup.js b/httemplate/edit/cust_main/bottomfixup.js
index 800864bc8..77d4294a6 100644
--- a/httemplate/edit/cust_main/bottomfixup.js
+++ b/httemplate/edit/cust_main/bottomfixup.js
@@ -66,21 +66,25 @@ function copy_payby_fields() {
%# call submit_continue() on completion...
%# otherwise not touching standardize_locations for now
<% include( '/elements/standardize_locations.js',
- 'callback' => 'submit_continue();'
+ '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['ship_state'];
+ var state_el = cf.elements[prefix + 'state'];
var census_data = new Array(
'year', <% $conf->config('census_year') || '2012' %>,
- 'address1', cf.elements['ship_address1'].value,
- 'city', cf.elements['ship_city'].value,
+ 'address1', cf.elements[prefix + 'address1'].value,
+ 'city', cf.elements[prefix + 'city'].value,
'state', state_el.options[ state_el.selectedIndex ].value,
- 'zip', cf.elements['ship_zip'].value
+ 'zip', cf.elements[prefix + 'zip'].value
);
censustract( census_data, update_censustract );
@@ -109,19 +113,21 @@ function update_censustract(arg) {
set_censustract = function () {
- cf.elements['censustract'].value = newcensus;
+ cf.elements[prefix + 'censustract'].value = newcensus;
submit_continue();
}
- if (error || cf.elements['censustract'].value != newcensus) {
+ 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['latitude' ].value || '<% $company_latitude %>';
- var longitude= cf.elements['longitude'].value || '<% $company_longitude %>';
+ 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>' +
@@ -132,14 +138,14 @@ function update_censustract(arg) {
'" 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['ship_zip'].value +
+ '&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['censustract'].value +
+ '<TR><TD>' + cf.elements[prefix + 'censustract'].value +
'</TD><TD>' + newcensus + '</TD></TR>' +
'<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
diff --git a/httemplate/edit/cust_main/company.html b/httemplate/edit/cust_main/company.html
new file mode 100644
index 000000000..8a6ed0bbf
--- /dev/null
+++ b/httemplate/edit/cust_main/company.html
@@ -0,0 +1,7 @@
+% my $cust_main = shift;
+<TR ID="company_row" <% $cust_main->company ? '' : 'STYLE="display:none"' %>>
+ <TD ALIGN="right"><% mt('Company') |h %></TD>
+ <TD COLSPAN=6><INPUT TYPE="text" NAME="company" ID="company" SIZE=60
+ VALUE="<% $cust_main->company |h %>">
+ </TD>
+</TR>
diff --git a/httemplate/edit/cust_main/fax.html b/httemplate/edit/cust_main/fax.html
new file mode 100644
index 000000000..237d4be44
--- /dev/null
+++ b/httemplate/edit/cust_main/fax.html
@@ -0,0 +1,5 @@
+% my $cust_main = shift;
+<TR>
+ <TD ALIGN="right"><% mt('Fax') |h %></TD>
+ <TD><INPUT TYPE="text" NAME="fax" VALUE="<% $cust_main->fax %>" SIZE=18></TD>
+</TR>
diff --git a/httemplate/edit/cust_main/name.html b/httemplate/edit/cust_main/name.html
new file mode 100644
index 000000000..2641ec930
--- /dev/null
+++ b/httemplate/edit/cust_main/name.html
@@ -0,0 +1,53 @@
+<%def .namepart>
+% my ($field, $value, $label, $extra) = @_;
+<TD>
+ <INPUT TYPE="text" NAME="<% $field %>" VALUE="<% $value |h %>" <%$extra%>>
+ <BR><FONT SIZE=-1><% mt($label) %></FONT>
+</TD>
+</%def>
+
+<TR>
+ <TH VALIGN="top" ALIGN="right"><%$r%><% mt('Contact name') |h %></TH>
+ <TD COLSPAN=6>
+ <TABLE CELLSPACING=0 CELLPADDING=0>
+ <TR>
+ <& .namepart, 'last', $cust_main->last, 'Last' &>
+ <TD VALIGN="top"> , </TD>
+ <& .namepart, 'first', $cust_main->first, 'First' &>
+% if ( $conf->exists('show_ss') ) {
+ <TD>&nbsp;</TD>
+ <& .namepart, 'ss', $ss, 'SS#', "SIZE=11" &>
+% } else {
+ <INPUT TYPE="hidden" NAME="ss" VALUE="<% $ss %>">
+% }
+ </TR>
+ </TABLE>
+ </TD>
+</TR>
+
+% if ( $conf->exists('cust-email-high-visibility') ) {
+<TR>
+ <TD ALIGN="right">
+ <% $conf->exists('cust_main-require_invoicing_list_email', $agentnum)
+ ? $r
+ : '' %>Email address(es)
+ </TD>
+ <TD BGCOLOR="#FFFF00">
+ <INPUT TYPE="text" NAME="invoicing_list"
+ VALUE=<% $cust_main->invoicing_list_emailonly_scalar %>>
+ </TD>
+</TR>
+% }
+<%init>
+my $cust_main = shift;
+my $agentnum = $cust_main->agentnum if $cust_main->custnum;
+my $conf = FS::Conf->new;
+my $r = '<font color="#ff0000">*</font>&nbsp;';
+my $ss;
+
+if ( $cgi->param('error') or $conf->exists('unmask_ss') ) {
+ $ss = $cust_main->ss;
+} else {
+ $ss = $cust_main->masked('ss');
+}
+</%init>
diff --git a/httemplate/edit/cust_main/phones.html b/httemplate/edit/cust_main/phones.html
new file mode 100644
index 000000000..9b23e0716
--- /dev/null
+++ b/httemplate/edit/cust_main/phones.html
@@ -0,0 +1,29 @@
+<TR>
+ <TD VALIGN="top" ALIGN="right"><% mt('Phones') |h %></TD>
+ <TD COLSPAN=6>
+ <TABLE CELLSPACING=0 CELLPADDING=0>
+ <TR>
+% foreach my $phone (qw(daytime night mobile)) {
+ <TD>
+ <INPUT TYPE="text"
+ NAME="<% $phone %>"
+ VALUE="<% $cust_main->get($phone) %>"
+ SIZE=18
+ >
+ <BR><FONT SIZE=-1><% mt($phone_label{$phone}) |h %></FONT>
+ </TD>
+ <TD>&nbsp;</TD>
+% }
+ </TR>
+ </TABLE>
+ </TD>
+</TR>
+<%init>
+my $cust_main = shift;
+my $conf = FS::Conf->new;
+my %phone_label = (
+ daytime => 'Day Phone',
+ night => 'Night Phone',
+ mobile => 'Mobile',
+);
+</%init>
diff --git a/httemplate/edit/cust_main/stateid.html b/httemplate/edit/cust_main/stateid.html
new file mode 100644
index 000000000..2655f5142
--- /dev/null
+++ b/httemplate/edit/cust_main/stateid.html
@@ -0,0 +1,39 @@
+% if ( $conf->exists('show_stateid') ) {
+<TR>
+ <TD ALIGN="right"><% $stateid_label %></TD>
+ <TD><INPUT TYPE="text" NAME="stateid" VALUE="<% $stateid %>" SIZE=12></TD>
+ <TD><& /elements/select-state.html,
+ state => $cust_main->stateid_state,
+ country => $cust_main->country, # how does this work on new customer?
+ prefix => 'stateid_',
+ disable_countyupdate => 1,
+ &></TD>
+</TR>
+% } else {
+<INPUT TYPE="hidden" NAME="stateid" VALUE="<% $stateid %>">
+<INPUT TYPE="hidden" NAME="stateid_state" VALUE="<% $cust_main->stateid_state %>">
+% }
+
+<%init>
+my $cust_main = shift;
+my $conf = FS::Conf->new;
+my $stateid;
+if ( $cgi->param('error') ) {
+ $stateid = $cust_main->stateid;
+} elsif ( $cust_main->custnum ) {
+ $stateid = $cust_main->masked('stateid');
+} else {
+ $stateid = '';
+}
+$cust_main->set('stateid_state' => $cust_main->state)
+ unless $cust_main->stateid_state;
+
+my $stateid_label = FS::Msgcat::_gettext('stateid') =~ /^(stateid)?$/
+ ? 'Driver&rsquo;s License'
+ : FS::Msgcat::_gettext('stateid') || 'Driver&rsquo;s License';
+
+my $stateid_state_label =
+ FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_state)?$/
+ ? 'Driver&rsquo;s License State'
+ : FS::Msgcat::_gettext('stateid') || 'Driver&rsquo;s License State';
+</%init>
diff --git a/httemplate/edit/cust_main/top_misc.html b/httemplate/edit/cust_main/top_misc.html
index 7ba167b7f..7ce283c6c 100644
--- a/httemplate/edit/cust_main/top_misc.html
+++ b/httemplate/edit/cust_main/top_misc.html
@@ -20,27 +20,16 @@
<% $cust_main->residential_commercial eq 'Commercial' ? 'CHECKED' : '' %>
></TD>
</TR>
-
<SCRIPT TYPE="text/javascript">
- function rescom_changed() {
- var f = document.CustomerForm;
-
- if ( f.residential_commercial_Residential.checked ) {
- document.getElementById('contacts_div').style.display = 'none';
- } else { // if ( f.residential_commercial_Commercial.checked ) {
- document.getElementById('contacts_div').style.display = '';
- }
-
- if ( f.residential_commercial_Residential.checked && ! f.company.value.length ) {
- document.getElementById('company_row').style.display = 'none'
- } else { // if ( f.residential_commercial_Commercial.checked ) {
+ function rescom_changed(what) {
+ if ( what.checked == (what.value == 'Commercial' ) ) {
document.getElementById('company_row').style.display = '';
- }
-
- if ( f.residential_commercial_Residential.checked && ! f.ship_company.value.length ) {
- document.getElementById('ship_company_row').style.display = 'none'
- } else { // if ( f.residential_commercial_Commercial.checked ) {
- document.getElementById('ship_company_row').style.display = '';
+ document.getElementById('contacts_div').style.display = '';
+ } else {
+ if ( document.getElementById('company').value.length == 0 ) {
+ document.getElementById('company_row').style.display = 'none';
+ }
+ document.getElementById('contacts_div').style.display = 'none';
}
}
</SCRIPT>
diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi
index 3fd9c79eb..d4414e44e 100755
--- a/httemplate/edit/cust_pay.cgi
+++ b/httemplate/edit/cust_pay.cgi
@@ -18,11 +18,12 @@
<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>">
<INPUT TYPE="hidden" NAME="paybatch" VALUE="<% $paybatch %>">
-<BR><BR>
+<BR>
<% mt('Payment') |h %>
<% ntable("#cccccc", 2) %>
+% if ( $FS::CurrentUser::CurrentUser->access_right('Backdate payment') ) {
<TR>
<TD ALIGN="right"><% mt('Date') |h %></TD>
<TD COLSPAN=2>
@@ -39,6 +40,15 @@
align: "BR"
});
</SCRIPT>
+% }
+% else {
+<TR>
+ <TD ALIGN="right"><% mt('Date') |h %></TD>
+ <TD COLSPAN=2>
+ <% time2str($date_format.' %r',$_date) %>
+ </TD>
+</TR>
+% }
<TR>
<TD ALIGN="right"><% mt('Amount') |h %></TD>
@@ -56,7 +66,29 @@
<TD ALIGN="right"><% mt('Check #') |h %></TD>
<TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
</TR>
-% }
+% }
+% elsif ( $payby eq 'CASH' and $conf->exists('require_cash_deposit_info') ) {
+ <TR>
+ <TD ALIGN="right"><% mt('Bank') |h %></TD>
+ <TD COLSPAN=3><INPUT TYPE="text" NAME="bank" VALUE="<% $cgi->param('bank') %>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Check #') |h %></TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="payinfo" VALUE="<% $payinfo %>" SIZE=10></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Teller #') |h %></TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="teller" VALUE="<% $cgi->param('teller') %>" SIZE=10></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Depositor') |h %></TD>
+ <TD COLSPAN=3><INPUT TYPE="text" NAME="depositor" VALUE="<% $cgi->param('depositor') %>"></TD>
+ </TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Account #') |h %></TD>
+ <TD COLSPAN=2><INPUT TYPE="text" NAME="account" VALUE="<% $cgi->param('account') %>" SIZE=18></TD>
+ </TR>
+% }
<TR>
% if ( $link eq 'custnum' || $link eq 'popup' ) {
diff --git a/httemplate/edit/elements/class_Common.html b/httemplate/edit/elements/class_Common.html
index 69da4db31..0a0916ebc 100644
--- a/httemplate/edit/elements/class_Common.html
+++ b/httemplate/edit/elements/class_Common.html
@@ -22,21 +22,22 @@ my %opt = @_;
my $table = $opt{'table'};
my @category;
+my $category_table;
unless ( $opt{'nocat'} ) {
- ( my $category_table = $table ) =~ s/class/category/ or die;
+ ( $category_table = $table ) =~ s/class/category/ or die;
@category = qsearch($category_table, { 'disabled' => '' });
}
my $fields = [ 'classname',
(scalar(@category)
- ? { field=>'categorynum', type=>'select-table', 'empty_label'=>'(none)', 'table'=>'pkg_category', 'name_col'=>'categoryname' }
+ ? { field=>'categorynum', type=>'select-table', 'empty_label'=>'(none)', 'table'=>$category_table, 'name_col'=>'categoryname' }
: { field=>'categorynum', type=>'hidden' }
),
{ field=>'disabled', type=>'checkbox', value=>'Y', },
];
-push @$fields, $opt{'addl_fields'} if $opt{'addl_fields'};
+push @$fields, @{ $opt{'addl_fields'} } if $opt{'addl_fields'};
my %addl_labels = ();
-%addl_labels = %{$opt{'addl_labels'}} if $opt{'addl_labels'};
+%addl_labels = %{ $opt{'addl_labels'} } if $opt{'addl_labels'};
</%init>
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index 73faad4c0..a24f23805 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -292,6 +292,9 @@ Example:
% #& deprecated weird value hashref used only by reason.html
% 'value' => $f->{'value'},
%
+% #fixed
+% 'noescape' => $f->{'noescape'},
+%
% #select(-*)
% 'options' => $f->{'options'},
% 'labels' => $f->{'labels'},
@@ -308,6 +311,7 @@ Example:
%
% #umm. for select-agent_types at least
% 'disabled' => $f->{'disabled'},
+% 'fixed' => $f->{'fixed'},
%
% #any?
% 'colspan' => $f->{'colspan'},
@@ -317,7 +321,7 @@ Example:
% $include_common{$_} = $f->{$_} foreach grep exists($f->{$_}),
% qw( js_only html_only select_only layers_only cell_style ),#selectlayers,?
% qw( empty_label ), # select-*
-% qw( value_col ), # select-table
+% qw( value_col compare_sub ), # select-table
% qw( table name_col ), #(select,checkboxes)-table
% qw( target_table link_table ), #checkboxes-table
% qw( hashref agent_virt agent_null agent_null_right ),#*-table
@@ -751,13 +755,15 @@ Example:
<BR>
- <INPUT TYPE = "submit"
- ID = "submit"
- VALUE = "<% ( !$clone && $object->$pkey() )
- ? "Apply changes"
- : "Add ". ( $opt{'name'} || $opt{'name_singular'} )
- %>"
- >
+% unless ($opt{'no_submit'}) {
+ <INPUT TYPE = "submit"
+ ID = "submit"
+ VALUE = "<% ( !$clone && $object->$pkey() )
+ ? "Apply changes"
+ : "Add ". ($opt{'name'} || $opt{'name_singular'})
+ %>"
+ >
+% }
</FORM>
diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html
index 38716f06e..0d9d36c07 100644
--- a/httemplate/edit/elements/svc_Common.html
+++ b/httemplate/edit/elements/svc_Common.html
@@ -103,7 +103,15 @@
$f->{'extra_sql'} .= ' OR svcnum = '. $object->svcnum
if $object->svcnum;
$f->{'extra_sql'} .= ' ) ';
- $f->{'disable_empty'} = $object->svcnum ? 1 : 0,
+ $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'} = {
@@ -127,6 +135,10 @@
$object->set('custnum', $cust_pkg->custnum);
}
+ if ( my $cb = $opt{'svc_field_callback'} ) {
+ &{ $cb }( $cgi, $object, $f);
+ }
+
},
'html_init' => sub {
diff --git a/httemplate/edit/ftp_target.html b/httemplate/edit/ftp_target.html
new file mode 100755
index 000000000..aebf9aaed
--- /dev/null
+++ b/httemplate/edit/ftp_target.html
@@ -0,0 +1,46 @@
+<& 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/invoice_template.html b/httemplate/edit/invoice_template.html
index 9cec62c86..3553c617b 100644
--- a/httemplate/edit/invoice_template.html
+++ b/httemplate/edit/invoice_template.html
@@ -8,7 +8,8 @@
<FORM ACTION="process/invoice_template.html" METHOD="POST">
<INPUT TYPE="hidden" NAME="confname" VALUE="<% $confname %>">
-% if ( $type eq 'html' ) {
+% #if ( $type eq 'html' ) {
+% if ( 0 ) { #this seems to broken, using a text editor for everything for now
<% include('/elements/htmlarea.html',
'field' => 'value',
diff --git a/httemplate/edit/msg_template.html b/httemplate/edit/msg_template.html
index 941554532..115032a07 100644
--- a/httemplate/edit/msg_template.html
+++ b/httemplate/edit/msg_template.html
@@ -1,14 +1,57 @@
-<% include( 'elements/edit.html',
- 'html_init' => '<TABLE id="outerTable"><TR><TD>',
- 'body_etc' => $body_etc,
- 'name_singular' => 'template',
- 'table' => 'msg_template',
- 'viewall_dir' => 'browse',
- 'agent_virt' => 1,
- 'agent_null' => 1,
- 'agent_null_right' => ['Edit global templates', 'Configuration'],
+<& elements/edit.html,
+ 'html_init' => '<TABLE id="outerTable"><TR><TD>',
+ 'body_etc' => $body_etc,
+ 'name_singular' => 'template',
+ 'table' => 'msg_template',
+ 'viewall_dir' => 'browse',
+ 'agent_virt' => 1,
+ 'agent_null' => 1,
+ 'agent_null_right' => [ 'View global templates', 'Edit global templates' ],
- 'fields' => [
+ 'fields' => \@fields,
+ 'labels' => {
+ 'msgnum' => 'Template',
+ 'agentnum' => 'Agent',
+ 'msgname' => 'Template name',
+ 'from_addr' => 'From: ',
+ 'bcc_addr' => 'Bcc: ',
+ 'locale' => 'Language',
+ 'subject' => 'Subject: ',
+ 'body' => 'Message body',
+ },
+ 'edit_callback' => \&edit_callback,
+ 'error_callback' => \&edit_callback,
+ 'html_bottom' => '</DIV>',
+ 'html_foot' => ( $no_submit ? '' : "</TD>$sidebar</TR></TABLE>" ),
+ 'no_submit' => $no_submit,
+&>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right([ 'View templates', 'View global templates',
+ 'Edit templates', 'Edit global templates',
+ ]);
+
+my $body_etc = '';
+$body_etc = q!onload="document.getElementById('locale').onchange()"!
+ if $cgi->param('locale') eq 'new';
+
+my $msgnum = $cgi->param('msgnum');
+my $msg_template = $msgnum ? qsearchs('msg_template', {msgnum=>$msgnum}) : '';
+
+my $no_submit = 0;
+my @fields = ();
+if ( $curuser->access_right('Edit global templates')
+ || ( $curuser->access_right('Edit templates')
+ && $msg_template
+ && $msg_template->agentnum
+ && $curuser->agentnums_href->{$msg_template->agentnum}
+ )
+ )
+{
+ push @fields,
{ field => 'agentnum',
type => 'select-agent',
},
@@ -25,33 +68,32 @@
type => 'htmlarea',
width => 763
},
- ],
- 'labels' => {
- 'msgnum' => 'Template',
- 'agentnum' => 'Agent',
- 'msgname' => 'Template name',
- 'from_addr' => 'From: ',
- 'bcc_addr' => 'Bcc: ',
- 'locale' => 'Language',
- 'subject' => 'Subject: ',
- 'body' => 'Message body',
- },
- 'edit_callback' => \&edit_callback,
- 'error_callback' => \&edit_callback,
- 'html_bottom' => '</DIV>',
- 'html_foot' => "</TD>$sidebar</TR></TABLE>",
- )
- %>
-<%init>
+ ;
+} else { #readonly
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Edit templates')
- || $FS::CurrentUser::CurrentUser->access_right('Edit global templates')
- || $FS::CurrentUser::CurrentUser->access_right('Configuration');
+ $no_submit = 1;
-my $body_etc = '';
-$body_etc = q!onload="document.getElementById('locale').onchange()"!
- if $cgi->param('locale') eq 'new';
+ push @fields,
+ { field => 'agentnum',
+ type => 'select-agent',
+ fixed => 1,
+ },
+ { field => 'msgname', type => 'fixed', },
+ { field => 'from_addr', type => 'fixed', },
+ { field => 'bcc_addr', type => 'fixed', },
+ { type => 'tablebreak-tabs',
+ include_opt_callback => \&menubar_opt_callback,
+ },
+ # template_content fields
+ { field => 'locale', type => 'hidden' },
+ { field => 'subject', type => 'fixed', },
+ { field => 'body',
+ type => 'fixed',
+ noescape => 1,
+ },
+ ;
+
+}
sub new_callback {
my ($cgi, $object, $fields_listref, $opt_hashref) = @_;
@@ -182,8 +224,18 @@ my %substitutions = (
'$country' => 'Country',
'$daytime' => 'Day phone',
'$night' => 'Night phone',
+ '$mobile' => 'Mobile phone',
'$fax' => 'Fax',
],
+ 'service' => [
+ '$ship_address1' => 'Address line 1',
+ '$ship_address2' => 'Address line 2',
+ '$ship_city' => 'City',
+ '$ship_county' => 'County',
+ '$ship_state' => 'State',
+ '$ship_zip' => 'Zip',
+ '$ship_country' => 'Country',
+ ],
'cust_bill' => [
'$invnum' => 'Invoice#',
],
@@ -238,15 +290,10 @@ my %substitutions = (
'$error' => 'Decline reason',
],
);
-my @c = @{ $substitutions{'contact'} };
-for (my $i=0; $i<scalar(@c); $i += 2) {
- $c[$i] =~ s/\$(.*)/\$ship_$1/;
-}
-$substitutions{'shipping'} = \@c;
tie my %sections, 'Tie::IxHash', (
'contact' => 'Name and contact info (billing)',
-'shipping' => 'Name and contact info (shipping)',
+'service' => 'Service address',
'cust_main' => 'Customer status and payment info',
'cust_pkg' => 'Package fields',
'cust_bill' => 'Invoice fields',
diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi
index 1450ac3b3..d7219b74a 100644
--- a/httemplate/edit/part_export.cgi
+++ b/httemplate/edit/part_export.cgi
@@ -106,7 +106,8 @@ my $widget = new HTML::Widgets::SelectLayers(
}
if ( $type eq 'select' ) {
my $size = defined($optinfo->{size}) ? " SIZE=" . $optinfo->{size} : '';
- my $multi = defined($optinfo->{multi}) ? ' MULTIPLE' : '';
+ my $multi = ($optinfo->{multi} || $optinfo->{multiple})
+ ? ' MULTIPLE' : '';
$html .= qq!<SELECT NAME="$option"$multi$size>!;
my @values = split '\s+', $value if $multi;
my @options;
diff --git a/httemplate/edit/part_referral.html b/httemplate/edit/part_referral.html
index daf8773f0..e9fd79452 100755
--- a/httemplate/edit/part_referral.html
+++ b/httemplate/edit/part_referral.html
@@ -3,9 +3,12 @@
'table' => 'part_referral',
'fields' => [ 'referral',
{ field=>'agentnum', type=>'select-agent', },
+ { field=>'disabled', type=>'checkbox', value=>'Y' } ,
],
- 'labels' => { 'referral' => 'Advertising source',
+ 'labels' => { 'refnum' => 'Ad Source',
+ 'referral' => 'Advertising source',
'agentnum' => 'Agent',
+ 'disabled' => 'Disabled',
},
'viewall_dir' => 'browse',
)
diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi
index fae896154..4bd083798 100755
--- a/httemplate/edit/part_svc.cgi
+++ b/httemplate/edit/part_svc.cgi
@@ -1,17 +1,27 @@
-<% include('/elements/header.html', "$action Service Definition",
+<& /elements/header.html, "$action Service Definition",
menubar('View all service definitions' => "${p}browse/part_svc.cgi"),
#" onLoad=\"visualize()\""
- )
-%>
+&>
+
+<& /elements/init_overlib.html &>
+
+<BR>
<FORM NAME="dummy">
- Service Part #<% $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %>
-<BR><BR>
-Service <INPUT TYPE="text" NAME="svc" VALUE="<% $hashref->{svc} %>"><BR>
+<FONT CLASS="fsinnerbox-title">Service Part #<% $part_svc->svcpart ? $part_svc->svcpart : "(NEW)" %></FONT>
+<TABLE CLASS="fsinnerbox">
+<TR>
+ <TD ALIGN="right">Service</TD>
+ <TD><INPUT TYPE="text" NAME="svc" VALUE="<% $hashref->{svc} %>"></TD>
+<TR>
+
+<& /elements/tr-select-part_svc_class.html, curr_value=>$hashref->{classnum} &>
-Self-service access:
-<SELECT NAME="selfservice_access">
+<TR>
+ <TD ALIGN="right">Self-service access</TD>
+ <TD>
+ <SELECT NAME="selfservice_access">
% tie my %selfservice_access, 'Tie::IxHash', #false laziness w/browse/part_svc
% '' => 'Yes',
% 'hidden' => 'Hidden',
@@ -22,12 +32,22 @@ Self-service access:
<% $_ eq $hashref->{'selfservice_access'} ? 'SELECTED' : '' %>
><% $selfservice_access{$_} %>
% }
-</SELECT><BR>
+ </SELECT>
+ </TD>
+</TR>
-<INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>>&nbsp;Disable new orders<BR>
-<INPUT TYPE="checkbox" NAME="preserve" VALUE="Y"<% $hashref->{'preserve'} eq 'Y' ? ' CHECKED' : '' %>>&nbsp;Preserve this service on package cancellation<BR>
+<TR>
+ <TD ALIGN="right">Disable new orders</TD>
+ <TD><INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $hashref->{disabled} eq 'Y' ? ' CHECKED' : '' %>></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">Preserve this service on package cancellation</TD>
+ <TD><INPUT TYPE="checkbox" NAME="preserve" VALUE="Y"<% $hashref->{'preserve'} eq 'Y' ? ' CHECKED' : '' %>>&nbsp;</TD>
+</TR>
+
+</TABLE>
<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $hashref->{svcpart} %>">
@@ -76,6 +96,18 @@ Self-service access:
% ? ( $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,
@@ -84,15 +116,16 @@ Self-service access:
% 'form_name' => 'dummy',
% #'form_action' => 'process/part_svc.cgi',
% 'form_action' => 'part_svc.cgi', #self
-% 'form_text' => [ qw( svc svcpart ) ],
-% 'form_select' => [ 'selfservice_access' ],
-% 'form_checkbox' => [ 'disabled', 'preserve' ],
+% '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;
+% #$html .= $svcdb_info;
%
% my $columns = 3;
% my $count = 0;
@@ -267,6 +300,7 @@ Self-service access:
%
% $html .= include('/elements/select-table.html',
% 'element_name' => "${layer}__${field}_classnum",
+% 'id' => "${layer}__${field}_classnum",
% 'element_etc' => ( $is_inv
% ? $disabled
% : $nodisplay
@@ -349,6 +383,7 @@ Self-service access:
% $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',
% );
@@ -382,7 +417,8 @@ Self-service access:
%
% $html .= include('/elements/progress-init.html',
% $layer, #form name
-% [ qw(svc svcpart selfservice_access disabled preserve
+% [ qw(svc svcpart classnum selfservice_access
+% disabled preserve
% exportnum),
% @fields ],
% 'process/part_svc.cgi',
@@ -401,9 +437,8 @@ Self-service access:
%
% },
% );
-%
-%
+<BR>
Table <% $widget->html %>
<% include('/elements/footer.html') %>
@@ -451,66 +486,6 @@ my %communigate_fields = (
#'svc_cert' => { map { $_=>1 } qw( ) },
);
-my $svcdb_info = '
-<TABLE>
- <TR>
- <TH ALIGN="left">Generic</TH>
- <TH ALIGN="left">Access</TH>
- <TH ALIGN="left">Telephony</TH>
-<!-- <TH>Hosting</TH>
- <TH>Colocation</TH>
--->
- </TR>
- <TR>
- <TD VALIGN="top">
- <UL STYLE="margin:0">
- <LI><B>svc_acct</B>: Accounts - anything with a username (mailbox, shell, RADIUS, etc.)
- <LI><B>svc_hardware</B>: Equipment supplied to customers
- <LI><B>svc_external</B>: Externally-tracked service
- </UL>
- </TD>
- <TD VALIGN="top">
- <UL STYLE="margin:0">
- <LI><B>svc_dsl</B>: DSL
- <LI><B>svc_broadband</B>: Wireless broadband
- <LI><B>svc_dish</B>: DISH Network
- </UL>
- </TD>
- <TD VALIGN="top">
- <UL STYLE="margin:0">
- <LI><B>svc_phone</B>: Customer phone number
- <LI><B>svc_pbx</B>: Customer PBX
- </UL>
- </TD>
- </TR>
-</TABLE>
-<BR>
-<TABLE>
- <TR>
- <TH ALIGN="left">Hosting</TH>
- <TH ALIGN="left">Colocation</TH>
- </TR>
- <TD VALIGN="top">
- <UL STYLE="margin:0">
- <LI><B>svc_domain</B>: Domain
- <LI><B>svc_cert</B>: Certificate
- <LI><B>svc_forward</B>: Mail forwarding
- <LI><B>svc_mailinglist</B>: Mailing list
- <LI><B>svc_www</B>: Virtual domain website
- </UL>
- </TD>
- <TD VALIGN="top">
- <UL STYLE="margin:0">
- <LI><B>svc_port</B>: Customer router/switch port
- </UL>
- </TD>
- </TR>
-<TABLE>
-<!-- <LI>svc_charge - One-time charges (Partially unimplemented)
- <LI>svc_wo - Work orders (Partially unimplemented)
--->
-';
-
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
diff --git a/httemplate/edit/part_svc_class.html b/httemplate/edit/part_svc_class.html
new file mode 100644
index 000000000..0d9a00727
--- /dev/null
+++ b/httemplate/edit/part_svc_class.html
@@ -0,0 +1,6 @@
+<% include( 'elements/class_Common.html',
+ 'name' => 'Service class',
+ 'table' => 'part_svc_class',
+ 'nocat' => 1,
+ )
+%>
diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html
index cfb86048c..2840df35b 100644
--- a/httemplate/edit/payment_gateway.html
+++ b/httemplate/edit/payment_gateway.html
@@ -6,11 +6,12 @@
'field_callback' => $field_callback,
'labels' => {
'gatewaynum' => 'Gateway',
+ 'gateway_namespace' => 'Gateway type',
'gateway_module' => 'Gateway',
'gateway_username' => 'Username',
'gateway_password' => 'Password',
'gateway_action' => 'Action',
- 'gateway_options' => 'Options: (Name/Value pairs, one element per line)',
+ 'gateway_options' => 'Options (Name/Value pairs, <BR>one element per line)',
'gateway_callback_url' => 'Callback URL',
},
)
@@ -18,18 +19,18 @@
<SCRIPT TYPE="text/javascript">
- var gatewayNamespace = new Array;
-
-% foreach my $module ( sort { lc($a) cmp lc ($b) } keys %modules ) {
- gatewayNamespace.push('<% $modules{$module} %>')
-% }
-
- // document.getElementById('gateway_namespace').value = gatewayNamespace[0];
- function setNamespace(what) {
- document.getElementById('gateway_namespace').value =
- gatewayNamespace[what.selectedIndex];
+% my $json = JSON->new->canonical;
+ var modulesForNamespace = <% $json->encode(\%modules_for_namespace) %>;
+ function changeNamespace(what) {
+ var ns = what.value;
+ var select_module = document.getElementById('gateway_module');
+ select_module.options.length = 0;
+ for (var x in modulesForNamespace[ns]) {
+ var o = document.createElement('option');
+ o.value = o.text = modulesForNamespace[ns][x];
+ select_module.add(o, null);
+ }
}
-
</SCRIPT>
<%init>
@@ -67,6 +68,7 @@ my %modules = (
'OpenECHO' => 'Business::OnlinePayment',
'PayConnect' => 'Business::OnlinePayment',
'PayflowPro' => 'Business::OnlinePayment',
+ 'Paymentech' => 'Business::BatchPayment',
'PaymenTech' => 'Business::OnlinePayment',
'PaymentsGateway' => 'Business::OnlinePayment',
'PayPal' => 'Business::OnlinePayment',
@@ -88,7 +90,13 @@ my %modules = (
'VirtualNet' => 'Business::OnlinePayment',
'WesternACH' => 'Business::OnlinePayment',
'WorldPay' => 'Business::OnlinePayment',
-);
+);
+
+my %modules_for_namespace;
+for (keys %modules) {
+ $modules_for_namespace{$modules{$_}} ||= [];
+ push @{ $modules_for_namespace{$modules{$_}} }, $_;
+}
my @actions = (
'Normal Authorization',
@@ -99,17 +107,23 @@ my @actions = (
my $fields = [
{
field => 'gateway_namespace',
- type => 'hidden',
- curr_value_callback => sub { my($cgi, $object, $fref) = @_;
- $modules{$object->gateway_module}
- || 'Business::OnlinePayment'
- },
+ type => 'select',
+ options => [ qw(
+ Business::OnlinePayment
+ Business::BatchPayment
+ Business::OnlineThirdPartyPayment
+ ) ],
+ labels => {
+ 'Business::OnlinePayment' => 'Direct',
+ 'Business::BatchPayment' => 'Batch',
+ 'Business::OnlineThirdPartyPayment' => 'Hosted',
+ },
+ onchange => 'changeNamespace',
},
{
field => 'gateway_module',
type => 'select',
options => [ sort { lc($a) cmp lc ($b) } keys %modules ],
- onchange => 'setNamespace',
},
'gateway_username',
'gateway_password',
@@ -126,6 +140,8 @@ my $fields = [
{
field => 'gateway_options',
type => 'textarea',
+ rows => '8',
+ cols => '40',
curr_value_callback => sub { my($cgi, $object, $fref) = @_;
join("\r", $object->options );
},
@@ -135,7 +151,7 @@ my $fields = [
my $field_callback = sub {
my ($cgi, $object, $field_hashref ) = @_;
if ($object->gatewaynum) {
- if ( $field_hashref->{field} eq 'gateway_module' ) {
+ if ( $field_hashref->{field} =~ /gateway_(module|namespace)/ ) {
$field_hashref->{type} = 'fixed';
}
}
diff --git a/httemplate/edit/pkg_class.html b/httemplate/edit/pkg_class.html
index 1bc100e36..c4e3d8ac5 100644
--- a/httemplate/edit/pkg_class.html
+++ b/httemplate/edit/pkg_class.html
@@ -10,7 +10,7 @@ my $conf = new FS::Conf;
my %opt = ();
if($conf->exists('cust_main-require_censustract')) {
- $opt{'addl_fields'} = qw( fcc_ds0s );
+ $opt{'addl_fields'} = [ 'fcc_ds0s' ];
$opt{'addl_labels'} = { 'fcc_ds0s' => 'FCC form 477 voice-grade equivalents' };
}
</%init>
diff --git a/httemplate/edit/process/cust_location.cgi b/httemplate/edit/process/cust_location.cgi
index 790fc8ea4..b9f93db8b 100644
--- a/httemplate/edit/process/cust_location.cgi
+++ b/httemplate/edit/process/cust_location.cgi
@@ -28,10 +28,12 @@ my $cust_location = qsearchs({
});
die "unknown locationnum $locationnum" unless $cust_location;
-my $new = {
+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 )
-};
+});
my $error = $cust_location->move_to($new);
diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi
index 44fbb4f10..5ee553b32 100755
--- a/httemplate/edit/process/cust_main.cgi
+++ b/httemplate/edit/process/cust_main.cgi
@@ -57,17 +57,40 @@ push @invoicing_list, 'POST' if $cgi->param('invoicing_list_POST');
push @invoicing_list, 'FAX' if $cgi->param('invoicing_list_FAX');
$cgi->param('invoicing_list', join(',', @invoicing_list) );
+# is this actually used? if so, we need to clone locations...
+# but I can't find anything that sets this parameter to a non-empty value
+$cgi->param('duplicate_of_custnum') =~ /^(\d+)$/;
+my $duplicate_of = $1;
+
+my %locations;
+for my $pre (qw(bill ship)) {
+
+ my %hash;
+ foreach ( FS::cust_main->location_fields ) {
+ $hash{$_} = scalar($cgi->param($pre.'_'.$_));
+ }
+ $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 );
+
+}
+
+if ( ($cgi->param('same') || '') eq 'Y' ) {
+ $locations{ship} = $locations{bill};
+}
#create new record object
+# but explicitly avoid setting ship_ fields
my $new = new FS::cust_main ( {
- map {
- $_, scalar($cgi->param($_))
- } fields('cust_main')
+ map { ( $_, scalar($cgi->param($_)) ) } (fields('cust_main')),
+ map { ( "ship_$_", '' ) } (FS::cust_main->location_fields)
} );
-$cgi->param('duplicate_of_custnum') =~ /^(\d+)$/;
-my $duplicate_of = $1;
+$new->invoice_noemail( ($cgi->param('invoice_email') eq 'Y') ? '' : 'Y' );
+
if ( $duplicate_of ) {
# then negate all changes to the customer; the only change we should
# make is to order a package, if requested
@@ -76,11 +99,9 @@ if ( $duplicate_of ) {
or die "nonexistent existing customer (custnum $duplicate_of)";
}
-if ( defined($cgi->param('same')) && $cgi->param('same') eq "Y" ) {
- $new->setfield("ship_$_", '') foreach qw(
- last first company address1 address2 city county state zip
- country daytime night fax
- );
+for my $pre (qw(bill ship)) {
+ $new->set($pre.'_location', $locations{$pre});
+ $new->set($pre.'_locationnum', $locations{$pre}->locationnum);
}
if ( $cgi->param('no_credit_limit') ) {
@@ -89,9 +110,11 @@ if ( $cgi->param('no_credit_limit') ) {
$new->tagnum( [ $cgi->param('tagnum') ] );
-my %usedatetime = ( 'birthdate' => 1 );
+my %usedatetime = ( 'birthdate' => 1,
+ 'spouse_birthdate' => 1,
+ );
-foreach my $dfield (qw( birthdate signupdate )) {
+foreach my $dfield (qw( birthdate spouse_birthdate signupdate )) {
if ( $cgi->param($dfield) && $cgi->param($dfield) =~ /^([ 0-9\-\/]{0,10})$/) {
@@ -130,6 +153,7 @@ $new->setfield('paid', $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;
#perhaps this stuff should go to cust_main.pm
if ( $new->custnum eq '' or $duplicate_of ) {
@@ -237,7 +261,7 @@ if ( $new->custnum eq '' or $duplicate_of ) {
else {
# create the customer
$error ||= $new->insert( \%hash, \@invoicing_list,
- 'tax_exemption'=> \@tax_exempt,
+ 'tax_exemption'=> \%tax_exempt,
'prospectnum' => scalar($cgi->param('prospectnum')),
);
@@ -256,6 +280,7 @@ if ( $new->custnum eq '' or $duplicate_of ) {
my $old = qsearchs( 'cust_main', { 'custnum' => $new->custnum } );
$error ||= "Old record not found!" unless $old;
+
if ( length($old->paycvv) && $new->paycvv =~ /^\s*\*+\s*$/ ) {
$new->paycvv($old->paycvv);
}
@@ -294,8 +319,11 @@ if ( $new->custnum eq '' or $duplicate_of ) {
local($FS::cust_main::DEBUG) = $DEBUG if $DEBUG;
local($FS::Record::DEBUG) = $DEBUG if $DEBUG;
+ local($Data::Dumper::Sortkeys) = 1;
+ warn Dumper({ new => $new, old => $old }) if $DEBUG;
+
$error ||= $new->replace( $old, \@invoicing_list,
- 'tax_exemption' => \@tax_exempt,
+ 'tax_exemption' => \%tax_exempt,
);
warn "$me returned from replace" if $DEBUG;
diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi
index e74f9022f..ce0ec3212 100755
--- a/httemplate/edit/process/cust_pay.cgi
+++ b/httemplate/edit/process/cust_pay.cgi
@@ -28,6 +28,8 @@
%}
<%init>
+my $conf = FS::Conf->new;
+
$cgi->param('linknum') =~ /^(\d+)$/
or die "Illegal linknum: ". $cgi->param('linknum');
my $linknum = $1;
@@ -37,7 +39,13 @@ $cgi->param('link') =~ /^(custnum|invnum|popup)$/
my $field = my $link = $1;
$field = 'custnum' if $field eq 'popup';
-my $_date = parse_datetime($cgi->param('_date'));
+my $_date;
+if ( $FS::CurrentUser::CurrentUser->access_right('Backdate payment') ) {
+ $_date = parse_datetime($cgi->param('_date'));
+}
+else {
+ $_date = time;
+}
my $new = new FS::cust_pay ( {
$field => $linknum,
@@ -46,6 +54,7 @@ my $new = new FS::cust_pay ( {
$_, scalar($cgi->param($_));
} qw( paid payby payinfo paybatch
pkgnum discount_term
+ bank depositor account teller
)
#} fields('cust_pay')
} );
@@ -57,6 +66,6 @@ push @rights, 'Post cash payment' if $new->payby eq 'CASH';
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right(\@rights);
-my $error = $new->insert( 'manual' => 1 );
+my $error ||= $new->insert( 'manual' => 1 );
</%init>
diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html
index 12b3bd94b..2d39e9dce 100644
--- a/httemplate/edit/process/elements/process.html
+++ b/httemplate/edit/process/elements/process.html
@@ -250,8 +250,6 @@ foreach my $value ( @values ) {
}
- $error ||= $new->check;
-
my @args = ();
if ( !$error && $opt{'args_callback'} ) {
@args = &{ $opt{'args_callback'} }( $cgi, $new );
diff --git a/httemplate/edit/process/ftp_target.html b/httemplate/edit/process/ftp_target.html
new file mode 100644
index 000000000..35f56c490
--- /dev/null
+++ b/httemplate/edit/process/ftp_target.html
@@ -0,0 +1,12 @@
+<& 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/msg_template.html b/httemplate/edit/process/msg_template.html
index 47fe978a8..b19f5c542 100644
--- a/httemplate/edit/process/msg_template.html
+++ b/httemplate/edit/process/msg_template.html
@@ -9,9 +9,7 @@
%>
<%init>
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Edit templates')
- || $FS::CurrentUser::CurrentUser->access_right('Edit global templates')
- || $FS::CurrentUser::CurrentUser->access_right('Configuration');
+ unless $FS::CurrentUser::CurrentUser->access_right(['Edit templates','Edit global templates']);
sub precheck_callback {
my $cgi = shift;
diff --git a/httemplate/edit/process/part_svc_class.html b/httemplate/edit/process/part_svc_class.html
new file mode 100644
index 000000000..16165dd18
--- /dev/null
+++ b/httemplate/edit/process/part_svc_class.html
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+ 'table' => 'part_svc_class',
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi
index fab85252b..ba4c5b1b6 100644
--- a/httemplate/edit/process/quick-cust_pkg.cgi
+++ b/httemplate/edit/process/quick-cust_pkg.cgi
@@ -48,6 +48,9 @@ die 'unknown custnum' unless $cust_main;
$cgi->param('pkgpart') =~ /^(\d+)$/
or die 'illegal pkgpart '. $cgi->param('pkgpart');
my $pkgpart = $1;
+$cgi->param('quantity') =~ /^(\d+)$/
+ or die 'illegal quantity '. $cgi->param('quantity');
+my $quantity = $1;
$cgi->param('refnum') =~ /^(\d*)$/
or die 'illegal refnum '. $cgi->param('refnum');
my $refnum = $1;
@@ -78,6 +81,7 @@ if ( $cgi->param('qualnum') ) {
my $cust_pkg = new FS::cust_pkg {
'custnum' => $custnum,
'pkgpart' => $pkgpart,
+ 'quantity' => $quantity,
'start_date' => ( scalar($cgi->param('start_date'))
? parse_datetime($cgi->param('start_date'))
: ''
diff --git a/httemplate/edit/process/quotation.html b/httemplate/edit/process/quotation.html
new file mode 100644
index 000000000..a69566581
--- /dev/null
+++ b/httemplate/edit/process/quotation.html
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+ 'table' => 'quotation',
+ 'redirect' => popurl(3).'view/quotation.html?',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Generate quotation');
+
+</%init>
diff --git a/httemplate/edit/process/sales.cgi b/httemplate/edit/process/sales.cgi
new file mode 100644
index 000000000..edef4d65c
--- /dev/null
+++ b/httemplate/edit/process/sales.cgi
@@ -0,0 +1,23 @@
+<% include( 'elements/process.html',
+ 'table' => 'sales',
+ 'viewall_dir' => 'browse',
+ 'viewall_ext' => 'cgi',
+ 'debug' => '1',
+ 'process_m2m' => { 'link_table' => 'access_groupsales',
+ 'target_table' => 'access_group',
+ },
+ 'edit_ext' => 'cgi',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+if ( FS::Conf->new->exists('disable_acl_changes') ) {
+ errorpage('ACL changes disabled in public demo.');
+ die "shouldn't be reached";
+}
+
+</%init>
+
diff --git a/httemplate/edit/process/svc_broadband.cgi b/httemplate/edit/process/svc_broadband.cgi
index 31def255c..90eab4aad 100644
--- a/httemplate/edit/process/svc_broadband.cgi
+++ b/httemplate/edit/process/svc_broadband.cgi
@@ -13,9 +13,9 @@ die "access denied"
sub precheck {
my $cgi = shift;
- if ( !defined($cgi->param('ip_addr')) ) {
- $cgi->param('ip_addr', $cgi->param('prev_ip_addr') || '');
- }
+ my $ip_addr = $cgi->param('ip_addr');
+ $ip_addr =~ s/[^\d\.]//g; # converts '(automatic)' to null
+ $cgi->param('ip_addr', $ip_addr);
$cgi->param("usergroup", [ $cgi->param('usergroup') ]);
''
}
diff --git a/httemplate/edit/prospect_main.html b/httemplate/edit/prospect_main.html
index c8c8e98e1..d3985410b 100644
--- a/httemplate/edit/prospect_main.html
+++ b/httemplate/edit/prospect_main.html
@@ -36,6 +36,9 @@
'empty_label' => 'No address',
'disable_empty' => $conf->exists('prospect_main-location_required'),
'alt_format' => $conf->exists('prospect_main-alt_address_format'),
+ 'include_opt_callback' => sub {
+ 'prospect_main' => shift
+ },
},
],
'new_callback' => $new_callback,
diff --git a/httemplate/edit/quick-charge.html b/httemplate/edit/quick-charge.html
index 27841063f..1d9647f2f 100644
--- a/httemplate/edit/quick-charge.html
+++ b/httemplate/edit/quick-charge.html
@@ -11,13 +11,30 @@
<SCRIPT TYPE="text/javascript">
-function enable_quick_charge () {
+function enable_quick_charge (e) {
+
if ( document.QuickChargeForm.amount.value
&& document.QuickChargeForm.pkg.value ) {
document.QuickChargeForm.submit.disabled = false;
} else {
document.QuickChargeForm.submit.disabled = true;
}
+
+% if ( $curuser->option('disable_enter_submit_onetimecharge') ) {
+
+ var key;
+ if (window.event)
+ key = window.event.keyCode; //IE
+ else
+
+ key = e.which; //firefox, others
+
+ return (key != 13);
+
+% } else {
+ return true;
+% }
+
}
function validate_quick_charge () {
@@ -76,7 +93,12 @@ function bill_now_changed (what) {
</SCRIPT>
-<FORM ACTION="process/quick-charge.cgi" NAME="QuickChargeForm" ID="QuickChargeForm" METHOD="POST" onsubmit="document.QuickChargeForm.submit.disabled=true;return validate_quick_charge();">
+<FORM ACTION = "process/quick-charge.cgi"
+ NAME = "QuickChargeForm"
+ ID = "QuickChargeForm"
+ METHOD = "POST"
+ onSubmit = "document.QuickChargeForm.submit.disabled=true; return validate_quick_charge();"
+>
<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
@@ -85,7 +107,13 @@ function bill_now_changed (what) {
<TR>
<TD ALIGN="right"><% mt('Amount') |h %> </TD>
<TD>
- <% $money_char %><INPUT TYPE="text" NAME="amount" SIZE=6 VALUE="<% $amount %>" onChange="enable_quick_charge()" onKeyPress="enable_quick_charge()">
+ <% $money_char %><INPUT TYPE = "text"
+ NAME = "amount"
+ SIZE = 6
+ VALUE = "<% $amount %>"
+ onChange = "return enable_quick_charge(event)"
+ onKeyPress = "return enable_quick_charge(event)"
+ >
</TD>
</TR>
@@ -93,7 +121,11 @@ function bill_now_changed (what) {
<TR>
<TD ALIGN="right"><% mt('Quantity') |h %> </TD>
<TD>
- <INPUT TYPE="text" NAME="quantity" SIZE=4 VALUE="<% $quantity %>">
+ <INPUT TYPE = "text"
+ NAME = "quantity"
+ SIZE = 4
+ VALUE = "<% $quantity %>"
+ onKeyPress = "return enable_quick_charge(event)">
</TD>
</TR>
% }
@@ -107,6 +139,7 @@ function bill_now_changed (what) {
NAME = "bill_now"
VALUE = "1"
<% $cgi->param('bill_now') ? 'CHECKED' : '' %>
+ onClick = "bill_now_changed(this);"
onChange = "bill_now_changed(this);"
>
<% mt('with terms') |h %>
@@ -127,7 +160,11 @@ function bill_now_changed (what) {
SIZE = 32
ID = "start_date_text"
VALUE = "<% $start_date %>"
- <% $cgi->param('bill_now') ? 'STYLE = "background-color:#dddddd" DISABLED' : '' %>
+ onKeyPress="return enable_quick_charge(event)"
+ <% $cgi->param('bill_now')
+ ? 'STYLE = "background-color:#dddddd" DISABLED'
+ : ''
+ %>
>
<IMG SRC = "<%$fsurl%>images/calendar.png"
ID = "start_date_button"
@@ -173,7 +210,14 @@ function bill_now_changed (what) {
<TR>
<TD ALIGN="right"><% mt('Description') |h %> </TD>
<TD>
- <INPUT TYPE="text" NAME="pkg" SIZE="50" MAXLENGTH="50" VALUE="<% $pkg %>" onChange="enable_quick_charge()" onKeyPress="enable_quick_charge()">
+ <INPUT TYPE = "text"
+ NAME = "pkg"
+ SIZE = "50"
+ MAXLENGTH = "50"
+ VALUE = "<% $pkg %>"
+ onChange = "return enable_quick_charge(event)"
+ onKeyPress = "return enable_quick_charge(event)"
+ >
</TD>
</TR>
@@ -191,7 +235,15 @@ function bill_now_changed (what) {
<TR>
<TD></TD>
<TD>
- <INPUT TYPE="text" NAME="description<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $param->{"description$row"} |h %>" rownum="<% $row %>" onkeyup = "possiblyAddRow;" >
+ <INPUT TYPE = "text"
+ NAME = "description<% $row %>"
+ SIZE = "60"
+ MAXLENGTH = "65"
+ VALUE = "<% $param->{"description$row"} |h %>"
+ rownum = "<% $row %>"
+ onKeyPress = "return enable_quick_charge(event)"
+ onKeyUp = "return possiblyAddRow(event)"
+ >
</TD>
</TR>
% }
@@ -210,10 +262,26 @@ function bill_now_changed (what) {
var rownum = <% $row %>;
- function possiblyAddRow() {
+ function possiblyAddRow(e) {
+
if ( ( rownum - this.getAttribute('rownum') ) == 1 ) {
addRow();
}
+
+% if ( $curuser->option('disable_enter_submit_onetimecharge') ) {
+
+ var key;
+ if (window.event)
+ key = window.event.keyCode; //IE
+ else
+ key = e.which; //firefox, others
+
+ return (key != 13);
+
+% } else {
+ return true;
+% }
+
}
function addRow() {
@@ -228,14 +296,16 @@ function bill_now_changed (what) {
var description_cell = document.createElement('TD');
- var description_input = document.createElement('INPUT');
- description_input.setAttribute('name', 'description'+rownum);
- description_input.setAttribute('id', 'description'+rownum);
- description_input.setAttribute('size', 60);
- description_input.setAttribute('maxLength', 65);
- description_input.setAttribute('rownum', rownum);
- description_input.onkeyup = possiblyAddRow;
- description_cell.appendChild(description_input);
+ //var description_input = document.createElement('INPUT');
+ var di = document.createElement('INPUT');
+ di.setAttribute('name', 'description'+rownum);
+ di.setAttribute('id', 'description'+rownum);
+ di.setAttribute('size', 60);
+ di.setAttribute('maxLength', 65);
+ di.setAttribute('rownum', rownum);
+ di.onkeyup = possiblyAddRow;
+ di.onkeypress = enable_quick_charge;
+ description_cell.appendChild(di);
row.appendChild(description_cell);
@@ -251,8 +321,10 @@ function bill_now_changed (what) {
</HTML>
<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('One-time charge');
+ unless $curuser->access_right('One-time charge');
my $conf = new FS::Conf;
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
diff --git a/httemplate/edit/quotation.html b/httemplate/edit/quotation.html
new file mode 100644
index 000000000..8b6062355
--- /dev/null
+++ b/httemplate/edit/quotation.html
@@ -0,0 +1,30 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Quotation',
+ 'table' => 'quotation',
+ 'labels' => {
+ 'quotationnum' => 'Quotation number',
+ 'prospectnum' => 'Prospect',
+ 'custnum' => 'Customer',
+ '_date' => 'Date',
+ 'disabled' => 'Disabled',
+ },
+ 'fields' => [
+ { field=>'prospectnum', type=>'fixed-prospect_main' },
+ { field=>'custnum', type=>'fixed-cust_main' },
+ { field=>'_date', type=>'fixed-date' },
+ { field=>'disabled', type=>'checkbox', value=>'Y'},
+ ],
+ #XXX some way to disable the "view all"
+ 'new_callback' => sub { my( $cgi, $quotation) = @_;
+ $quotation->$_( $cgi->param($_) )
+ foreach qw( prospectnum custnum );
+ $quotation->_date(time);
+ },
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Generate quotation');
+
+</%init>
diff --git a/httemplate/edit/radius_group.html b/httemplate/edit/radius_group.html
index 025561159..0c99b4c4c 100644
--- a/httemplate/edit/radius_group.html
+++ b/httemplate/edit/radius_group.html
@@ -8,6 +8,7 @@
'attrnum' => 'Attribute',
'priority' => 'Priority',
},
+ 'viewall_dir' => 'browse',
'menubar' => \@menubar,
'edit_callback' => $edit_callback,
'error_callback' => $edit_callback,
diff --git a/httemplate/edit/sales.cgi b/httemplate/edit/sales.cgi
new file mode 100755
index 000000000..3497de505
--- /dev/null
+++ b/httemplate/edit/sales.cgi
@@ -0,0 +1,79 @@
+<% include("/elements/header.html","$action Sales Person", menubar(
+ 'View all sales people' => $p. 'browse/sales.cgi',
+)) %>
+
+<% include('/elements/error.html') %>
+
+<FORM METHOD = POST
+ ACTION = "<%popurl(1)%>process/sales.cgi"
+>
+
+<INPUT TYPE="hidden" NAME="salesnum" VALUE="<% $sales->salesnum %>">
+Sales #<% $sales->salesnum ? $sales->salesnum : "(NEW)" %>
+
+<% &ntable("#cccccc", 2, '') %>
+
+ <TR>
+ <TH ALIGN="right">Sales</TH>
+ <TD><INPUT TYPE="text" NAME="salesperson" SIZE=32 VALUE="<% $sales->salesperson %>"></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% emt('Agent') %></TD>
+ <TD>
+ <& /elements/select-agent.html,
+ 'curr_value' => $sales->salesnum,
+ 'disable_empty' => 1,
+ &>
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Disable</TD>
+ <TD><INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"<% $sales->disabled eq 'Y' ? ' CHECKED' : '' %>></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Access Groups</TD>
+ <TD><% include('/elements/checkboxes-table.html',
+ 'source_obj' => $sales,
+ 'link_table' => 'access_groupsales',
+ 'target_table' => 'access_group',
+ 'name_col' => 'groupname',
+ 'target_link' => $p. 'edit/access_group.html?',
+ )
+ %>
+ </TD>
+ </TR>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="<% $sales->salesnum ? "Apply changes" : "Add sales" %>">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $sales;
+if ( $cgi->param('error') ) {
+ $sales = new FS::sales ( {
+ map { $_, scalar($cgi->param($_)) } fields('sales')
+ } );
+} elsif ( $cgi->keywords ) {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/;
+ $sales = qsearchs( 'sales', { 'salesnum' => $1 } );
+} else { #adding
+ $sales = new FS::sales {};
+}
+my $action = $sales->salesnum ? 'Edit' : 'Add';
+
+my $conf = new FS::Conf;
+
+</%init>
diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi
index b266928a1..0d4b9897b 100644
--- a/httemplate/edit/svc_broadband.cgi
+++ b/httemplate/edit/svc_broadband.cgi
@@ -3,7 +3,7 @@
'name' => 'broadband service',
'table' => 'svc_broadband',
'fields' => \@fields,
- 'field_callback' => $field_callback,
+ 'svc_field_callback' => $svc_field_callback,
'svc_new_callback' => $svc_edit_callback,
'svc_edit_callback' => $svc_edit_callback,
'svc_error_callback' => $svc_edit_callback,
@@ -161,20 +161,14 @@ my $svc_edit_callback = sub {
}
};
-my $field_callback = sub {
+my $svc_field_callback = sub {
my ($cgi, $object, $fieldref) = @_;
my $columndef = $part_svc->part_svc_column($fieldref->{'field'});
- if ($columndef->columnflag eq 'F') {
- $fieldref->{'type'} = length($columndef->columnvalue)
- ? 'fixed'
- : 'hidden';
- $fieldref->{'value'} = $columndef->columnvalue;
+ if ($fieldref->{field} eq 'usergroup' && $columndef->columnflag eq 'F') {
- if ( $fieldref->{field} eq 'usergroup' ) {
- $fieldref->{'formatted_value'} =
- [ $object->radius_groups('long_description') ];
- }
+ $fieldref->{'formatted_value'} =
+ [ $object->radius_groups('long_description') ];
}
};
diff --git a/httemplate/edit/tower.html b/httemplate/edit/tower.html
index 5a0f2a8ce..03b488e86 100644
--- a/httemplate/edit/tower.html
+++ b/httemplate/edit/tower.html
@@ -12,12 +12,16 @@
m2_label => 'Sector',
m2_error_callback => $m2_error_callback,
},
+ 'latitude',
+ 'longitude',
],
labels => { 'towernum' => 'Tower',
'towername' => 'Name',
'sectornum' => 'Sector',
'disabled' => 'Disabled',
'default_ip_addr' => 'Tower IP address',
+ 'latitude' => 'Latitude',
+ 'longitude' => 'Longitude',
},
&>
<%init>
diff --git a/httemplate/elements/city.html b/httemplate/elements/city.html
index f6d2b4bad..6a2142f29 100644
--- a/httemplate/elements/city.html
+++ b/httemplate/elements/city.html
@@ -107,7 +107,11 @@ function <% $pre %>county_changed(what, callback) {}
<% $text_style %>
>
-% if ( !$disable_select ) {
+% if ( $disable_select ) {
+%# avoid JS errors
+<INPUT TYPE="hidden" ID="city_select">
+% }
+% else {
<SELECT NAME = "<%$pre%>city_select"
ID = "<%$pre%>city_select"
diff --git a/httemplate/elements/coord-links.html b/httemplate/elements/coord-links.html
index 3fd3ff696..02a224a00 100644
--- a/httemplate/elements/coord-links.html
+++ b/httemplate/elements/coord-links.html
@@ -3,7 +3,7 @@
<& /elements/popup_link.html,
'action' => $p. 'view/map.html?'. $query,
'label' => mt('map'),
- 'actionlabel' => $name,
+ 'actionlabel' => $js_name,
'width' => 763,
'height' => 575,
#'color'
@@ -12,8 +12,8 @@
% if ( $origin ) {
<& /elements/popup_link.html,
'action' => $p. "view/directions.html?origin=$origin;". $query,
- 'label' => mt('dir'),
- 'actionlabel' => $name,
+ 'label' => mt('directions'),
+ 'actionlabel' => $js_name,
'width' => 763,
'height' => 575,
&>
@@ -29,6 +29,12 @@ my $query = 'name='. uri_escape_utf8($name).
';lat='. $latitude.
';lon='. $longitude;
+my $js_name = $name;
+$js_name =~ s/[<>"]/ /g;
+$m->interp->apply_escapes($js_name, 'js_string');
+$js_name =~ s/^'//;
+$js_name =~ s/'$//;
+
my $origin;
#for directions link
if ( $agentnum =~ /^\d+$/ ) {
diff --git a/httemplate/elements/customer-table.html b/httemplate/elements/customer-table.html
index aa085c41a..79443dc8b 100644
--- a/httemplate/elements/customer-table.html
+++ b/httemplate/elements/customer-table.html
@@ -41,6 +41,8 @@ Example:
<SCRIPT TYPE="text/javascript">
+ var num_open_invoices = new Array;
+
function clearhint_invnum() {
if ( this.value == 'Not found' || this.value == 'Multiple' ) {
@@ -67,7 +69,46 @@ Example:
this.value = '';
}
-
+
+ function update_customer(searchrow, customerArray) {
+
+ var custnum_obj = document.getElementById('custnum'+searchrow);
+ var customer = document.getElementById('customer'+searchrow);
+ var customer_select = document.getElementById('cust_select'+searchrow);
+
+ custnum_obj.disabled = false;
+ custnum_obj.style.backgroundColor = '#ffffff';
+ customer.disabled = false;
+ customer.style.backgroundColor = '#ffffff';
+
+ if ( customerArray.length == 0 ) {
+
+ custnum_obj.value = 'Not found';
+ customer.value = 'Not found';
+ custnum_obj.style.color = '#ff0000';
+ customer.style.color = '#ff0000';
+
+ customer.style.display = '';
+ customer_select.style.display = 'none';
+ return false;
+
+ } else if ( customerArray.length == 6 ) {
+
+ custnum_obj.value = customerArray[0];
+ custnum_obj.style.color = '#000000';
+ customer.value = customerArray[1];
+
+ update_balance_text(searchrow, customerArray[2]);
+ update_status_text( searchrow, customerArray[3]);
+ update_status_color(searchrow, '#'+customerArray[4]);
+ update_num_open(searchrow, customerArray[5]);
+
+ customer.style.display = '';
+ customer_select.style.display = 'none';
+ return true;
+ }
+ }
+
function <% $opt{prefix} %>search_invnum() {
this.style.color = '#000000'
@@ -99,55 +140,26 @@ Example:
customer_select.style.display = 'none';
var custnum_obj = document.getElementById('custnum'+searchrow);
- var balance = document.getElementById('balance'+searchrow);
- var status = document.getElementById('status'+searchrow);
- balance.innerHTML = '';
- status.innerHTML = '';
+ update_balance_text(searchrow, '');
+ update_status_text(searchrow, '');
+ update_status_color(searchrow, '#000000');
+ update_num_open(searchrow, 0);
function search_invnum_update(customers) {
var customerArray = eval('(' + customers + ')');
-
- custnum_obj.disabled = false;
- custnum_obj.style.backgroundColor = '#ffffff';
- customer.disabled = false;
- customer.style.backgroundColor = '#ffffff';
-
- if ( customerArray.length == 0 ) {
-
- custnum_obj.value = 'Not found';
- customer.value = 'Not found';
- custnum_obj.style.color = '#ff0000';
- customer.style.color = '#ff0000';
-
- customer.style.display = '';
- customer_select.style.display = 'none';
-
- } else if ( customerArray.length == 5 ) {
-
- custnum_obj.value = customerArray[0];
- custnum_obj.style.color = '#000000';
- customer.value = customerArray[1];
- balance.innerHTML = '<% $money_char %>' + customerArray[2] + ' &nbsp; ';
- status.innerHTML = customerArray[3];
- status.style.color = '#'+customerArray[4];
-
- customer.style.display = '';
- customer_select.style.display = 'none';
+ update_customer(searchrow, customerArray);
% if ( $opt{invnum_update_callback} ) {
<% $opt{invnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
% }
- }
-
}
invnum_search( invnum, search_invnum_update );
}
-
function <% $opt{prefix} %>search_custnum() {
this.style.color = '#000000'
@@ -167,55 +179,62 @@ Example:
if ( ( <% $opt{prefix} %>rownum - searchrow ) == 1 ) {
<% $opt{prefix} %>addRow();
}
- var customer = document.getElementById('customer'+searchrow);
- customer.value = 'searching...';
- customer.disabled = true;
- customer.style.color = '#000000';
- customer.style.backgroundColor = '#dddddd';
+
+ var customer_obj = document.getElementById('customer'+searchrow);
+ customer_obj.value = 'searching...';
+ customer_obj.disabled = true;
+ customer_obj.style.color = '#000000';
+ customer_obj.style.backgroundColor = '#dddddd';
var customer_select = document.getElementById('cust_select'+searchrow);
- customer.style.display = '';
+ customer_obj.style.display = '';
customer_select.style.display = 'none';
var invnum = document.getElementById('invnum'+searchrow);
invnum.value = '';
-
- var balance = document.getElementById('balance'+searchrow);
- balance.innerHTML = '';
-
- var status = document.getElementById('status'+searchrow);
- status.innerHTML = '';
+
+ update_balance_text(searchrow, '');
+ update_status_text( searchrow, '');
+ update_status_color(searchrow, '#000000');
+ update_num_open(searchrow, 0);
function search_custnum_update(customers) {
- var customerArray = eval('(' + customers + ')');
+ var customerArrayArray = eval('(' + customers + ')') || [];
- customer.disabled = false;
- customer.style.backgroundColor = '#ffffff';
-
- if ( customerArray.length == 0 ) {
+ if ( customerArrayArray.length == 1 ) {
- customer.value = 'Not found';
- customer.style.color = '#ff0000';
+ update_customer(searchrow, customerArrayArray[0]);
+% if ( $opt{custnum_update_callback} ) {
+ <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
+% }
+
+ } else {
+
+ custnum_obj.value = 'Multiple'; // or something
custnum_obj.style.color = '#ff0000';
- } else if ( customerArray.length == 5 ) {
+ //blank the current list
+ customer_select.options.length = 0;
+
+ opt(customer_select, '', 'Multiple customers match "' + custnum + '" - select one', '#ff0000');
+ //add the multiple customers
+ for ( var s = 0; s < customerArrayArray.length; s++ ) {
+ opt(customer_select,
+ JSON.stringify(customerArrayArray[s]),
+ customerArrayArray[s][1],
+ '#000000');
+ }
- custnum_obj.value = customerArray[0];
- custnum_obj.style.color = '#000000';
- customer.value = customerArray[1];
- balance.innerHTML = '<% $money_char %>' + customerArray[2] + ' &nbsp; ';
- status.innerHTML = customerArray[3];
- status.style.color = '#'+customerArray[4];
+ opt(customer_select, 'cancel', '(Edit search string)', '#000000');
- customer.style.display = '';
- customer_select.style.display = 'none';
+ customer_obj.style.display = 'none';
+
+ customer_select.style.display = '';
-% if ( $opt{custnum_update_callback} ) {
- <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
-% }
}
+
}
custnum_search(custnum, search_custnum_update );
@@ -251,39 +270,16 @@ Example:
var customer_select = document.getElementById('cust_select'+searchrow);
- var balance = document.getElementById('balance'+searchrow);
- balance.innerHTML = '';
-
- var status = document.getElementById('status'+searchrow);
- status.innerHTML = '';
-
function search_customer_update(customers) {
- var customerArray = eval('(' + customers + ')');
+ var customerArrayArray = eval('(' + customers + ')') || [ [] ];
custnum_obj.disabled = false;
custnum_obj.style.backgroundColor = '#ffffff';
- if ( customerArray.length == 0 ) {
-
- custnum_obj.value = 'Not found';
- custnum_obj.style.color = '#ff0000';
- customer_obj.style.color = '#ff0000';
-
- customer_obj.style.display = '';
- customer_select.style.display = 'none';
-
- } else if ( customerArray.length == 1 ) {
-
- custnum_obj.value = customerArray[0][0];
- customer_obj.value = customerArray[0][1];
- balance.innerHTML = '<% $money_char %>' + customerArray[0][2] + ' &nbsp; ';
- status.innerHTML = customerArray[0][3];
- status.style.color = '#'+customerArray[0][4];
-
- customer_obj.style.display = '';
- customer_select.style.display = 'none';
+ if ( customerArrayArray.length == 1 ) {
+ update_customer(searchrow, customerArrayArray[0]);
% if ( $opt{custnum_update_callback} ) {
<% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
% }
@@ -294,14 +290,16 @@ Example:
custnum_obj.style.color = '#ff0000';
//blank the current list
- for ( var i = customer_select.length; i >= 0; i-- )
- customer_select.options[i] = null;
+ customer_select.options.length = 0;
opt(customer_select, '', 'Multiple customers match "' + customer + '" - select one', '#ff0000');
-
//add the multiple customers
- for ( var s = 0; s < customerArray.length; s++ )
- opt(customer_select, customerArray[s][0] + '_' + customerArray[s][2] + '_' + customerArray[s][3] + '_' + customerArray[s][4], customerArray[s][1], '#000000');
+ for ( var s = 0; s < customerArrayArray.length; s++ ) {
+ opt(customer_select,
+ JSON.stringify(customerArrayArray[s]),
+ customerArrayArray[s][1],
+ '#000000');
+ }
opt(customer_select, 'cancel', '(Edit search string)', '#000000');
@@ -341,27 +339,7 @@ Example:
} else {
- var pos_underscore1 = custnum_balance_status.indexOf('_');
- var pos_underscore2 = custnum_balance_status.indexOf('_',pos_underscore1+1);
- var pos_underscore3 = custnum_balance_status.indexOf('_',pos_underscore2+1);
- var custnum = custnum_balance_status.substring(0,pos_underscore1);
- var balance = custnum_balance_status.substring(pos_underscore1+1,pos_underscore2) + ' &nbsp; ';
- var status = custnum_balance_status.substring(pos_underscore2+1,pos_underscore3);
- var color = custnum_balance_status.substring(pos_underscore3+1);
-
- custnum_obj.value = custnum;
- custnum_obj.style.color = '#000000';
-
- customer_obj.value = customer;
- customer_obj.style.color = '#000000';
-
- balance_obj.innerHTML = '<% $money_char %>' + balance;
-
- status_obj.innerHTML = status;
- status_obj.style.color = '#'+color;
-
- this.style.display = 'none';
- customer_obj.style.display = '';
+ update_customer(searchrow, JSON.parse(custnum_balance_status));
% if ( $opt{custnum_update_callback} ) {
<% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
@@ -378,6 +356,26 @@ Example:
what.options[length] = optionName;
}
+ function update_status_text(rownum, newval) {
+ document.getElementById('status'+rownum).value = newval;
+ document.getElementById('status'+rownum+'_text').innerHTML = newval;
+ }
+
+ function update_status_color(rownum, newval) {
+ document.getElementById('statuscolor'+rownum).value = newval;
+ document.getElementById('status'+rownum+'_text').style.color = newval;
+ }
+
+ function update_balance_text(rownum, newval) {
+ document.getElementById('balance'+rownum).value = newval;
+ document.getElementById('balance'+rownum+'_text').innerHTML = newval;
+ }
+
+ function update_num_open(rownum, newval) {
+ num_open_invoices[rownum] = newval;
+ }
+
+
</SCRIPT>
<TABLE ID="<% $opt{prefix} %>OneTrueTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
@@ -395,7 +393,7 @@ Example:
% my $row = 0;
% for ( $row = 0; exists($param->{"custnum$row"}); $row++ ) {
- <TR>
+ <TR id="row<%$row%>" rownum="<%$row%>">
<TD>
<INPUT TYPE = "text"
NAME = "invnum<% $row %>"
@@ -430,14 +428,26 @@ Example:
</SCRIPT>
</TD>
- <TD>
- <SPAN
+ <TD STYLE="text-align: center">
+ <SPAN
+ ID = "status<% $row %>_text"
+ rownum = "<% $row %>"
+ STYLE = "font-weight: bold;
+ color: <%$param->{"statuscolor$row"} || '#000000'%>"
+
+ ><% $param->{"status$row"} %></SPAN>
+ <INPUT TYPE = "hidden"
NAME = "status<% $row %>"
ID = "status<% $row %>"
+ VALUE = "<% $param->{"status$row"} %>"
+ rownum = "<% $row %>"
+ >
+ <INPUT TYPE = "hidden"
+ NAME = "statuscolor<% $row %>"
+ ID = "statuscolor<% $row %>"
+ VALUE = "<% $param->{"statuscolor$row"} %>"
rownum = "<% $row %>"
- STYLE = "text-align:center; font-weight: bold"
>
- </SPAN>
</TD>
<TD>
@@ -456,6 +466,21 @@ Example:
</SCRIPT>
</TD>
+ <TD STYLE="text-align:right">
+ <% $money_char %>
+ <SPAN
+ ID = "balance<% $row %>_text"
+ rownum = "<% $row %>"
+ ><% $param->{"balance$row"} %></SPAN>
+ &nbsp;
+ <INPUT TYPE = "hidden"
+ NAME = "balance<% $row %>"
+ ID = "balance<% $row %>"
+ VALUE = "<% $param->{"balance$row"} %>"
+ rownum = "<% $row %>"
+ >
+ </TD>
+
% my $col = 0;
% foreach my $field ( @{$opt{fields}} ) {
% my $value;
@@ -470,19 +495,24 @@ Example:
% my $color = $opt{color}->[$col];
% my $font = $color ? qq(<FONT COLOR="$color">) : '';
% my $onchange = '';
-% if ( $opt{footer}->[$col] eq '_TOTAL' ) {
+% if ( $opt{onchange}->[$col] ) {
+% $onchange = 'onchange="'.$opt{onchange}->[$col].'"';
+% }
+% elsif ( $opt{footer}->[$col] eq '_TOTAL' ) {
% $total[$col] += $value;
% $onchange = $opt{prefix}. "calc_total$col();";
% $onchange = qq(onchange="$onchange" onkeyup="$onchange");
% }
<TD ALIGN="<% $align %>">
-% if (! $types->[$col] || $types->[$col] eq 'text') {
- <INPUT TYPE = "text"
+% my $type = $types->[$col] || 'text';
+% if ($type eq 'text' or $type eq 'checkbox') {
+ <INPUT TYPE = "<% $type %>"
NAME = "<% $name %>"
ID = "<% $name %>"
SIZE = "<% $size %>"
STYLE = "text-align: <% $align %>;"
VALUE = "<% $value %>"
+ rownum = "<% $row %>"
<% $onchange %>
>
% } elsif ($types->[$col] eq 'immutable') {
@@ -494,19 +524,10 @@ Example:
</TD>
% $col++;
% }
- <TD STYLE="text-align:right;">
- <SPAN
- NAME = "balance<% $row %>"
- ID = "balance<% $row %>"
- rownum = "<% $row %>"
- >
- </SPAN>
- &nbsp;
- </TD>
</TR>
% }
-<TR>
+<TR id="row_total">
<TH COLSPAN=5 ID="<% $opt{'prefix'} %>_TOTAL_TOTAL">
Total <% $row ? $row-1 : 0 %>
<% PL($opt{name_singular} || 'customer', ( $row ? $row-1 : 0 ) ) %>
@@ -580,7 +601,8 @@ Example:
var table = document.getElementById('<% $opt{prefix} %>OneTrueTable');
var tablebody = table.getElementsByTagName('tbody').item(0);
- var row = table.insertRow(rownum+1);
+ var row = table.insertRow(table.rows.length - 1);
+ row.setAttribute('id', 'row'+rownum);
var invnum_cell = document.createElement('TD');
@@ -613,15 +635,28 @@ Example:
row.appendChild(custnum_cell);
var status_cell = document.createElement('TD');
+ status_cell.style.textAlign = 'center';
- var status_span = document.createElement('SPAN');
- status_span.setAttribute('name', 'status'+<% $opt{prefix} %>rownum);
- status_span.setAttribute('id', 'status'+<% $opt{prefix} %>rownum);
- status_span.style.textAlign = 'center';
- status_span.style.fontWeight = 'bold';
- status_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
- status_cell.appendChild(status_span);
+ var status_span = document.createElement('SPAN');
+ status_span.setAttribute('id', 'status'+<% $opt{prefix} %>rownum+'_text');
+ status_span.style.fontWeight = 'bold';
+ status_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ status_cell.appendChild(status_span);
+ var status_input = document.createElement('INPUT');
+ status_input.setAttribute('type', 'hidden');
+ status_input.setAttribute('name', 'status'+<% $opt{prefix} %>rownum);
+ status_input.setAttribute('id', 'status'+<% $opt{prefix} %>rownum);
+ status_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ status_cell.appendChild(status_input);
+
+ var statuscolor_input = document.createElement('INPUT');
+ statuscolor_input.setAttribute('type', 'hidden');
+ statuscolor_input.setAttribute('name', 'statuscolor'+<% $opt{prefix} %>rownum);
+ statuscolor_input.setAttribute('id', 'statuscolor'+<% $opt{prefix} %>rownum);
+ statuscolor_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ status_cell.appendChild(statuscolor_input);
+
row.appendChild(status_cell);
var customer_cell = document.createElement('TD');
@@ -649,13 +684,25 @@ Example:
row.appendChild(customer_cell);
var balance_cell = document.createElement('TD');
- balance_cell.style.textAlign = 'right';
-
- var balance_span = document.createElement('SPAN');
- balance_span.setAttribute('name', 'balance'+<% $opt{prefix} %>rownum);
- balance_span.setAttribute('id', 'balance'+<% $opt{prefix} %>rownum);
- balance_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
- balance_cell.appendChild(balance_span);
+
+ balance_cell.style.textAlign = 'right';
+ balance_cell.appendChild(document.createTextNode('<%$money_char%>'));
+
+ var balance_span = document.createElement('SPAN');
+ balance_span.setAttribute('id', 'balance'+<% $opt{prefix} %>rownum+'_text');
+ balance_span.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ balance_cell.appendChild(balance_span);
+
+ balance_cell.appendChild(
+ document.createTextNode(String.fromCharCode(160)) //&nbsp;
+ );
+
+ var balance_input = document.createElement('INPUT');
+ balance_input.setAttribute('type', 'hidden');
+ balance_input.setAttribute('name', 'balance'+<% $opt{prefix} %>rownum);
+ balance_input.setAttribute('id', 'balance'+<% $opt{prefix} %>rownum);
+ balance_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+ balance_cell.appendChild(balance_input);
row.appendChild(balance_cell);
@@ -672,19 +719,27 @@ Example:
% } else {
% $value = $param->{"$field$row"};
% }
- var my_text = document.createTextNode('<% $value %>');
+ var my_text = document.createTextNode(<% $value |js_string %>);
my_cell.appendChild(my_text);
% }
+% my $name = (ref($field) eq 'CODE') ? "column${col}_" : $field;
var my_input = document.createElement('INPUT');
- my_input.setAttribute('name', '<% $field %>'+<% $opt{prefix} %>rownum);
- my_input.setAttribute('id', '<% $field %>'+<% $opt{prefix} %>rownum);
+ my_input.setAttribute('name', '<% $name %>'+<% $opt{prefix} %>rownum);
+ my_input.setAttribute('id', '<% $name %>'+<% $opt{prefix} %>rownum);
my_input.style.textAlign = '<% $align{ $opt{align}->[$col] || 'l' } %>';
my_input.setAttribute('size', <% $sizes->[$col] || 10 %>);
-% if ($types->[$col] eq 'immutable') {
+ my_input.setAttribute('rownum', <% $opt{prefix} %>rownum);
+% if ( $types->[$col] eq 'immutable' ) {
my_input.setAttribute('type', 'hidden');
% }
-% if ( $opt{footer}->[$col] eq '_TOTAL' ) {
+% elsif ( $types->[$col] eq 'checkbox' ) {
+ my_input.setAttribute('type', 'checkbox');
+% }
+% if ( $opt{onchange}->[$col] ) {
+ my_input.onchange = <% $opt{onchange}->[$col] %>;
+% }
+% elsif ( $opt{footer}->[$col] eq '_TOTAL' ) {
my_input.onchange = <% $opt{prefix} %>calc_total<%$col%>;
my_input.onkeyup = <% $opt{prefix} %>calc_total<%$col%>;
% }
@@ -708,6 +763,11 @@ Example:
+ ' <% PL($opt{name_singular} || 'customer') %>';
}
+% if ( $opt{add_row_callback} ) {
+ <% $opt{add_row_callback} %>(<% $opt{prefix} %>rownum,
+ '<% $opt{prefix} %>');
+% }
+
<% $opt{prefix} %>rownum++;
}
@@ -725,7 +785,7 @@ my $conf = new FS::Conf;
$opt{prefix} = '' unless defined $opt{prefix};
$opt{prefix} .= '_' if $opt{prefix};
-my $types = $opt{'types'} ? [ @{$opt{'types'}} ] : [];
+my $types = $opt{'type'} ? [ @{$opt{'type'}} ] : [];
my $sizes = $opt{'size'} ? [ @{$opt{'size'}} ] : [];
my $param = $opt{param};
@@ -742,5 +802,4 @@ my %align = (
);
my $money_char = $conf->config('money_char') || '$';
-
</%init>
diff --git a/httemplate/elements/form-create_ticket.html b/httemplate/elements/form-create_ticket.html
new file mode 100644
index 000000000..362e82397
--- /dev/null
+++ b/httemplate/elements/form-create_ticket.html
@@ -0,0 +1,38 @@
+<FORM METHOD="GET" NAME="CreateTicketForm" STYLE="display:inline">
+<SCRIPT TYPE="text/javascript">
+function updateTicketLink() {
+ var link = document.getElementById('CreateTicketLink');
+ var selector = document.getElementById('Queue')
+ link.href = "<% $new_base.'?'.
+ join(';', map(
+ { ($_ eq 'Queue') ? () : "$_=$new_param{$_}"}
+ keys %new_param),'Queue=') %>" + selector.options[selector.selectedIndex].value;
+}
+</SCRIPT>
+<A NAME="tickets"><FONT CLASS="fsinnerbox-title">Tickets</FONT></A>
+<A id="CreateTicketLink" HREF="<% $new_link %>"><% mt('Create new ticket') |h %></A>
+ <% mt('in queue') |h %>
+%# fetch list of queues in which the user can create tickets
+% my %queues = FS::TicketSystem->queues('', 'CreateTicket');
+% if( $conf->exists('ticket_system-force_default_queueid') ) {
+<B><% $queues{$new_param{'Queue'}} %></B>
+<INPUT TYPE="hidden" NAME="Queue" VALUE="<% $new_param{'Queue'} %>">
+% }
+% else {
+<SELECT NAME="Queue" id="Queue" onchange="updateTicketLink()">
+% foreach my $queueid ( sort { $queues{$a} cmp $queues{$b} } keys %queues ) {
+ <OPTION VALUE="<% $queueid %>"
+ <% $queueid == $new_param{'Queue'} ? 'SELECTED' : '' %>
+ ><% $queues{$queueid} |h %>
+% }
+</SELECT>
+<SCRIPT DEFER TYPE="text/javascript">updateTicketLink();</SCRIPT>
+% }
+</FORM>
+<%init>
+my %opt = @_;
+my $conf = new FS::Conf;
+my $object = $opt{'object'}; # must be a cust_main, cust_svc, or svc_...
+my ($new_base, %new_param) = FS::TicketSystem->href_params_new_ticket($object);
+my $new_link = FS::TicketSystem->href_new_ticket($object);
+</%init>
diff --git a/httemplate/elements/freeside.css b/httemplate/elements/freeside.css
index 44a4a3ca2..82eb9b562 100644
--- a/httemplate/elements/freeside.css
+++ b/httemplate/elements/freeside.css
@@ -108,8 +108,8 @@ a.fstab {
-moz-border-radius-topright:8px;
-webkit-border-radius-topleft:8px;
-webkit-border-radius-topright:8px;
- border-radius-topleft:8px;
- border-radius-topright:8px;
+ border-top-left-radius:8px;
+ border-top-right-radius:8px;
/*font-weight:bold;*/
/*padding-left:12px;
padding-right:12px;*/
@@ -141,8 +141,8 @@ a.fstabselected {
-moz-border-radius-topright:8px;
-webkit-border-radius-topleft:8px;
-webkit-border-radius-topright:8px;
- border-radius-topleft:8px;
- border-radius-topright:8px;
+ border-top-left-radius:8px;
+ border-top-right-radius:8px;
/*font-weight:bold;*/
/*padding-left:12px;
padding-right:12px;*/
@@ -175,8 +175,8 @@ div.fstabcontainer {
-moz-border-radius-bottomright:8px;
-webkit-border-radius-bottomleft:8px;
-webkit-border-radius-bottomright:8px;
- border-radius-bottomleft:8px;
- border-radius-bottomright:8px;
+ border-bottom-left-radius:8px;
+ border-bottom-right-radius:8px;
-moz-box-shadow: #666666 1px 1px 2px;
-webkit-box-shadow: #666666 1px 1px 2px;
box-shadow: #666666 1px 1px 2px;
@@ -206,8 +206,8 @@ div.fstabcontainer {
-moz-border-radius-bottomright:8px;
-webkit-border-radius-bottomleft:8px;
-webkit-border-radius-bottomright:8px;
- border-radius-bottomleft:8px;
- border-radius-bottomright:8px;
+ border-bottom-left-radius:8px;
+ border-bottom-right-radius:8px;
-moz-box-shadow: #666666 1px 1px 2px;
-webkit-box-shadow: #666666 1px 1px 2px;
box-shadow: #666666 1px 1px 2px;
@@ -238,8 +238,8 @@ div.fstabcontainer {
-moz-border-radius-topright:8px;
-webkit-border-radius-topleft:8px;
-webkit-border-radius-topright:8px;
- border-radius-topleft:8px;
- border-radius-topright:8px;
+ border-top-left-radius:8px;
+ border-top-right-radius:8px;
-moz-box-shadow: 1px 0px 1px #999999;
-webkit-box-shadow: 1px 0px 1px #999999;
box-shadow: 1px 0px 1px #999999;
@@ -250,3 +250,31 @@ div.fstabcontainer {
background-color:#f8f8f8;
}
+table.grid {
+ border: 1px solid #cccccc;
+ -moz-box-shadow: 1px 1px 2px #666666;
+ -webkit-box-shadow: 1px 1px 2px #666666;
+ box-shadow: 1px 1px 2px #666666;
+ filter: progid:DXImageTransform.Microsoft.Shadow(color='#666666', Direction=135, Strength=2);
+}
+
+th.grid {
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-bottom: 2px;
+ border: none;
+ empty-cells: show;
+}
+
+td.grid {
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-bottom: 2px;
+ border: none;
+ empty-cells: show;
+}
+
+table.inv { border: none }
+th.inv { border: none }
+td.inv { border: none }
+
diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html
index d0ab3055d..c6ad3c387 100644
--- a/httemplate/elements/header.html
+++ b/httemplate/elements/header.html
@@ -42,6 +42,12 @@ Example:
<% include('init_overlib.html') |n %>
<% include('rs_init_object.html') |n %>
<% include('logout.html') |n %>
+% my $timeout = $conf->config('logout-timeout');
+% if ( $timeout && $timeout =~ /^\s*\d+\s*$/ ) {
+ <script type="text/javascript">
+ setTimeout('logout()', <% 60000 * $timeout %>);
+ </script>
+% }
<% $head |n %>
diff --git a/httemplate/elements/init_overlib.html b/httemplate/elements/init_overlib.html
index d27ca3bda..986adec40 100644
--- a/httemplate/elements/init_overlib.html
+++ b/httemplate/elements/init_overlib.html
@@ -1,9 +1,16 @@
% for my $file (@files) {
<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/<%$file%>.js"></SCRIPT>
% }
+<%shared>
+my $initialized = 0; #won't work if component is "preloaded"... so don't do that
+</%shared>
<%init>
-my @files = map "overlibmws$_", ( '', qw( _iframe _draggable _crossframe ) );
-push @files, map { "${_}contentmws" } qw( iframe ajax );
+my @files = ();
+if ( ! $initialized ) {
+ push @files, map "overlibmws$_", ( '', qw( _iframe _draggable _crossframe ) );
+ push @files, map { "${_}contentmws" } qw( iframe ajax );
+ $initialized++;
+}
</%init>
diff --git a/httemplate/elements/location.html b/httemplate/elements/location.html
index c606523f0..767231856 100644
--- a/httemplate/elements/location.html
+++ b/httemplate/elements/location.html
@@ -3,16 +3,16 @@
Example:
include( '/elements/location.html',
- 'object' => $cust_main, # or $cust_location
- 'prefix' => $pre, #only for cust_main objects
+ 'object' => $cust_location
+ 'prefix' => $pre, # prefixed to form field names
'onchange' => $javascript,
- 'disabled' => $disabled,
- 'same_checked' => $same_checked,
'geocode' => $geocode, #passed through
'censustract' => $censustract, #passed through
'no_asterisks' => 0, #set true to disable the red asterisks next
#to required fields
'address1_label' => 'Address', #label for address
+ 'enable_district' => 1, #show tax district field
+ 'enable_censustract' => 1, #show censustract field
)
</%doc>
@@ -40,12 +40,12 @@ Example:
% }
<TR>
- <<%$th%> ALIGN="right"><%$r%><% $opt{'address1_label'} || emt('Address') %></<%$th%>>
+ <<%$th%> STYLE="width:16ex" ALIGN="right"><%$r%><% $opt{'address1_label'} || emt('Address') %></<%$th%>>
<TD COLSPAN=7>
<INPUT TYPE = "text"
NAME = "<%$pre%>address1"
ID = "<%$pre%>address1"
- VALUE = "<% $object->get($pre.'address1') |h %>"
+ VALUE = "<% $object->get('address1') |h %>"
SIZE = 54
onChange = "<% $onchange %>"
<% $disabled %>
@@ -62,7 +62,7 @@ Example:
<INPUT TYPE = "text"
NAME = "<%$pre%>address2"
ID = "<%$pre%>address2"
- VALUE = "<% $object->get($pre.'address2') |h %>"
+ VALUE = "<% $object->get('address2') |h %>"
SIZE = 54
onChange = "<% $onchange %>"
<% $disabled %>
@@ -75,7 +75,7 @@ Example:
<INPUT TYPE = "hidden"
NAME = "<%$pre%>address2"
- VALUE = "<% $object->get($pre.'address2') |h %>"
+ VALUE = "<% $object->get('address2') |h %>"
>
<TR>
@@ -83,7 +83,7 @@ Example:
<TD COLSPAN=7>
% my $location_type = scalar($cgi->param('location_type'))
-% || $object->get($pre.'location_type');
+% || $object->get('location_type');
% #my $location_number = scalar($cgi->param('location_number'))
% # || $object->get($pre.'location_number');
%
@@ -130,7 +130,7 @@ Example:
<INPUT TYPE="text"
NAME = "location_number"
ID = "location_number"
- VALUE = "<% scalar($cgi->param('location_number')) || $object->get($pre.'location_number') |h %>"
+ VALUE = "<% scalar($cgi->param('location_number')) || $object->get('location_number') |h %>"
SIZE = "5"
<% $disabled || ($location_type ? '' : 'DISABLED') %>
<% $style %>
@@ -161,7 +161,7 @@ Example:
<INPUT TYPE = "text"
NAME = "<%$pre%>zip"
ID = "<%$pre%>zip"
- VALUE = "<% $object->get($pre.'zip') |h %>"
+ VALUE = "<% $object->get('zip') |h %>"
SIZE = 10
onChange = "<% $onchange %>"
<% $disabled %>
@@ -181,7 +181,7 @@ Example:
<INPUT TYPE = "text"
NAME = "<%$pre%>latitude"
ID = "<%$pre%>latitude"
- VALUE = "<% $object->get($pre.'latitude') |h %>"
+ VALUE = "<% $object->get('latitude') |h %>"
<% $disabled %>
<% $style %>
>
@@ -189,36 +189,44 @@ Example:
<INPUT TYPE = "text"
NAME = "<%$pre%>longitude"
ID = "<%$pre%>longitude"
- VALUE = "<% $object->get($pre.'longitude') |h %>"
+ VALUE = "<% $object->get('longitude') |h %>"
<% $disabled %>
<% $style %>
>
</TD>
</TR>
-<INPUT TYPE="hidden" NAME="<%$pre%>coord_auto" VALUE="<% $object->get($pre.'coord_auto') %>">
+<INPUT TYPE="hidden" NAME="<%$pre%>coord_auto" VALUE="<% $object->coord_auto %>">
-% if ( !$pre ) {
- <INPUT TYPE="hidden" NAME="geocode" VALUE="<% $opt{geocode} %>">
+<INPUT TYPE="hidden" NAME="<%$pre%>geocode" VALUE="<% $object->geocode %>">
+<INPUT TYPE="hidden" NAME="<%$pre%>censusyear" VALUE="<% $object->censusyear %>">
+<TR>
+% if ( $opt{enable_censustract} ) {
+ <TD ALIGN="right">Census&nbsp;tract</TD>
+ <TD COLSPAN=8>
+ <INPUT TYPE="text" SIZE=15
+ NAME="<%$pre%>censustract"
+ VALUE="<% $object->censustract %>">
+ <% '(automatic)' %>
+ </TD>
% } else {
-% if ( $pre eq 'ship_' && $conf->exists('cust_main-require_censustract') ) {
- <TR><<%$th%> ALIGN="right">Census tract<BR>(automatic)</<%$th%>>
- <TD>
- <INPUT TYPE="text" NAME="censustract" VALUE="<% $opt{censustract} %>">
- <INPUT TYPE="hidden" NAME="censusyear" VALUE="<% $object->get('censusyear') %>">
- </TD>
- </TR>
+ <INPUT TYPE="hidden" NAME="<%$pre%>censustract" VALUE="<% $object->censustract %>">
+% }
+</TR>
+% if ( $conf->config('tax_district_method') ) {
+ <TR>
+% if ( $opt{enable_district} ) {
+ <TD ALIGN="right">Tax&nbsp;district</TD>
+ <TD COLSPAN=8>
+ <INPUT TYPE="text" SIZE=15
+ NAME="<%$pre%>district"
+ VALUE="<% $object->district %>">
+ <% '(automatic)' %>
+ </TD>
% } else {
- <INPUT TYPE="hidden" NAME="censustract" VALUE="<% $opt{censustract} %>">
-% }
-% if ( $conf->config('tax_district_method') or $object->get('district') ) {
- <TR>
- <<%$th%> ALIGN="right">Tax district<BR>(automatic)</<%$th%>>
- <TD>
- <INPUT TYPE="text" NAME="district" VALUE="<%$object->get('district')%>">
- </TD>
- </TR>
+ <INPUT TYPE="hidden" NAME="<%$pre%>district" VALUE="<% $object->district %>">
% }
-% }
+ </TR>
+% }
<%init>
@@ -233,16 +241,13 @@ my $conf = new FS::Conf;
my $r = $opt{'no_asterisks'} ? '' : qq!<font color="#ff0000">*</font>&nbsp;!;
-#false laziness with ship state
my $countrydefault = $conf->config('countrydefault') || 'US';
-$object->set($pre.'country', $countrydefault )
- unless $object->get($pre.'country');
-
-my $statedefault = $conf->config('statedefault')
+my $statedefault = $conf->config('statedefault')
|| ($countrydefault eq 'US' ? 'CA' : '');
-$object->set($pre.'state', $statedefault )
- unless $object->get($pre.'state')
- || $object->get($pre.'country') ne $countrydefault;
+$object ||= FS::cust_location->new({
+ 'country' => $countrydefault,
+ 'state' => $statedefault,
+});
my $alt_err = ($opt{'alt_format'} && !$disabled) ? $object->alternize : '';
@@ -255,8 +260,8 @@ push @address2_label_style, 'visibility:hidden'
|| ! $conf->exists('cust_main-require_address2')
|| ( !$pre && !$opt{'same_checked'} );
-my @counties = counties( $object->get($pre.'state'),
- $object->get($pre.'country'),
+my @counties = counties( $object->get('state'),
+ $object->get('country'),
);
my @county_style = ();
push @county_style, 'display:none' # 'visibility:hidden'
@@ -276,10 +281,10 @@ my $county_style =
: '';
my %select_hash = (
- 'city' => $object->get($pre.'city'),
- 'county' => $object->get($pre.'county'),
- 'state' => $object->get($pre.'state'),
- 'country' => $object->get($pre.'country'),
+ 'city' => $object->get('city'),
+ 'county' => $object->get('county'),
+ 'state' => $object->get('state'),
+ 'country' => $object->get('country'),
'prefix' => $pre,
'onchange' => $onchange,
'disabled' => $disabled,
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 3b0969f5c..019afe94e 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -28,7 +28,7 @@
% 'width' => 300,
% 'height' => 375,
% 'color' => '#7e0079',
-% 'scrolling' => 'no',
+% #'scrolling' => 'no',
% );
% $fs_popup =~ s/return false;//;
function about_freeside() {
@@ -106,12 +106,11 @@ $report_customers_lists{'with USPS-unvalidated addresses'} = [ $fsurl. 'search/c
tie my %report_customers, 'Tie::IxHash';
$report_customers{'List customers'} = [ \%report_customers_lists, 'List customers' ]
- if $curuser->access_right('List customers');
+ if $curuser->access_right('List all customers');
$report_customers{'Zip code distribution'} = [ $fsurl. 'search/report_cust_main-zip.html', 'Zip codes by number of customers' ];
$report_customers{'Customer signup report'} = [ $fsurl. 'graph/report_cust_signup.html', 'New customer signups by date' ],
$report_customers{'Advanced customer reports'} = [ $fsurl. 'search/report_cust_main.html', 'by status, signup date, agent, etc.' ]
- if $curuser->access_right('List customers')
- && $curuser->access_right('List packages');
+ if $curuser->access_right('Advanced customer search');
tie my %report_invoices_open, 'Tie::IxHash',
'All open invoices' => [ $fsurl.'search/cust_bill.html?OPEN_date', 'All invoices with an unpaid balance' ],
@@ -201,10 +200,10 @@ foreach my $svcdb ( FS::part_svc->svc_tables() ) {
];
}
- if ( $svcdb =~ /^svc_(acct|broadband|hardware)$/ ) {
$report_svc{"Advanced $lcsname reports"} =
- [ $fsurl."search/report_$svcdb.html", '' ];
- }
+ [ $fsurl."search/report_$svcdb.html", '' ]
+ if $svcdb =~ /^svc_(acct|broadband|hardware)$/
+ && $curuser->access_right("Services: $name: Advanced search");
if ( $svcdb eq 'svc_phone' ) {
@@ -221,7 +220,8 @@ foreach my $svcdb ( FS::part_svc->svc_tables() ) {
}
- $report_services{$name} = [ \%report_svc, $longname ];
+ $report_services{$name} = [ \%report_svc, $longname ]
+ if $curuser->access_right("Services: $name");
}
@@ -238,9 +238,11 @@ if ( $curuser->access_right('Financial reports') ) {
$report_packages{'separator2'} = '';
}
$report_packages{'All customer packages'} = [ $fsurl.'search/cust_pkg.cgi?pkgnum', 'List all customer packages', ];
-$report_packages{'Package summary'} = [ $fsurl.'search/cust_pkg_summary.html', 'Show package sales summary', ];
+$report_packages{'Package summary'} = [ $fsurl.'search/cust_pkg_summary.html', 'Show package sales summary', ]
+ if $curuser->access_right('Summarize packages');
$report_packages{'Suspended customer packages'} = [ $fsurl.'search/cust_pkg.cgi?magic=suspended', 'List suspended packages' ];
-$report_packages{'Suspension summary'} = [ $fsurl.'search/cust_pkg_susp.html', 'Show suspension activity', ];
+$report_packages{'Suspension summary'} = [ $fsurl.'search/cust_pkg_susp.html', 'Show suspension activity', ]
+ if $curuser->access_right('Summarize packages');
$report_packages{'Customer packages with unconfigured services'} = [ $fsurl.'search/cust_pkg.cgi?APKG_pkgnum', 'List packages which have provisionable services' ];
$report_packages{'FCC Form 477 packages'} = [ $fsurl.'search/report_477.html', 'Summarize packages by census tract for particular types' ]
if $conf->exists('cust_main-require_censustract');
@@ -251,14 +253,17 @@ tie my %report_inventory, 'Tie::IxHash',
'Inventory activity' => [ $fsurl.'search/report_h_inventory_item.html', '' ],
;
-tie my %report_rating, 'Tie::IxHash',
- 'RADIUS sessions' => [ $fsurl.'search/sqlradius.html', '' ],
- 'Call Detail Records (CDRs)' => [ $fsurl.'search/report_cdr.html', '' ],
- 'Unrateable CDRs' => [ $fsurl.'search/cdr.html?freesidestatus=failed'.
- ';cdrbatchnum=_ALL_' ],
- 'Time worked' => [ $fsurl.'search/report_rt_transaction.html', '' ],
- 'Time worked summary' => [ $fsurl.'search/report_rt_ticket.html', '' ],
-;
+tie my %report_rating, 'Tie::IxHash';
+$report_rating{'RADIUS sessions'} = [ $fsurl.'search/sqlradius.html', '' ]
+ if $curuser->access_right("Usage: RADIUS sessions");
+$report_rating{'Call Detail Records (CDRs)'} = [ $fsurl.'search/report_cdr.html', '' ]
+ if $curuser->access_right("Usage: Call Detail Records (CDRs)");
+$report_rating{'Unrateable CDRs'} = [ $fsurl.'search/cdr.html?freesidestatus=failed;cdrbatchnum=_ALL_' ]
+ if $curuser->access_right("Usage: Unrateable CDRs");
+if ( $curuser->access_right("Usage: Time worked") ) {
+ $report_rating{'Time worked'} = [ $fsurl.'search/report_rt_transaction.html', '' ];
+ $report_rating{'Time worked summary'} = [ $fsurl.'search/report_rt_ticket.html', '' ];
+}
tie my %report_ticketing_statistics, 'Tie::IxHash',
'Tickets per day per Queue' => [ $fsurl.'rt/RTx/Statistics/CallsQueueDay', 'View the number of tickets created, resolved or deleted in a specific Queue, over the requested period of days' ],
@@ -270,13 +275,14 @@ tie my %report_ticketing_statistics, 'Tie::IxHash',
;
tie my %report_ticketing, 'Tie::IxHash',
- 'Resolved by owner' => [ $fsurl.'rt/Tools/Reports/ResolvedByOwner.html', '' ],
- 'Resolved in date range' => [ $fsurl.'rt/Tools/Reports/ResolvedByDates.html', '' ],
- 'Created in date range' => [ $fsurl.'rt/Tools/Reports/CreatedByDates.html', '' ],
- 'separator' => '',
+# fix TimeToResolve extension? or redo in a more modern way?
+# 'Resolved by owner' => [ $fsurl.'rt/Tools/Reports/ResolvedByOwner.html', '' ],
+# 'Resolved in date range' => [ $fsurl.'rt/Tools/Reports/ResolvedByDates.html', '' ],
+# 'Created in date range' => [ $fsurl.'rt/Tools/Reports/CreatedByDates.html', '' ],
+# 'separator' => '',
'Statistics' => [ \%report_ticketing_statistics, '' ],
'separator2' => '',
- 'Advanced ticket reports' => [ $fsurl.'rt/Search/Build.html', 'List tickets by any criteria' ],
+ 'Advanced ticket reports' => [ $fsurl.'rt/Search/Build.html?NewQuery=1', 'List tickets by any criteria' ],
;
tie my %report_bill_event, 'Tie::IxHash',
@@ -308,6 +314,7 @@ if($curuser->access_right('Financial reports')) {
'Daily Sales, Credits and Receipts' => [ $fsurl.'graph/report_money_time_daily.html', 'Sales, credits and receipts (broken down by day) summary graph' ],
'Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg.html', 'Sales report and graph (by agent, package class and/or date range)' ],
'Rated Call Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg_detail.html', 'Sales report and graph (by agent, package class, usage class and/or date range)' ],
+ 'Sales With Advertising Source' => [ $fsurl.'search/report_cust_bill_pkg_referral.html' ],
'Employee Commission Report' => [ $fsurl.'search/report_employee_commission.html', '' ],
'Credit Report' => [ $fsurl.'search/report_cust_credit.html', 'Credit report (by employee and/or date range)' ],
'Unapplied Credits' => [ $fsurl.'search/report_cust_credit.html?unapplied=1', 'Unapplied credit report (by type and/or date range)' ],
@@ -385,10 +392,18 @@ tie my %tools_exporting, 'Tie::IxHash',
'Download database dump' => [ $fsurl. 'misc/dump.cgi', '' ],
;
+tie my %tools_ticketing_articles, 'Tie::IxHash',
+ 'Overview' => [ $fsurl.'rt/Articles/index.html', '' ],
+ 'Search' => [ $fsurl.'rt/Articles/Article/Search.html', '' ],
+ 'Topics' => [ $fsurl.'rt/Articles/Topics.html', '' ],
+;
+
tie my %tools_ticketing, 'Tie::IxHash',
- 'Offline' => [ $fsurl.'rt/Tools/Offline.html', '' ],
+ 'Articles' => [ \%tools_ticketing_articles, '' ],
'My Day' => [ $fsurl.'rt/Tools/MyDay.html', '' ],
- 'My Approvals' => [ $fsurl.'rt/Approvals/', '' ],
+ 'My Reminders' => [ $fsurl.'rt/Tools/MyReminders.html', '' ],
+ 'Offline' => [ $fsurl.'rt/Tools/Offline.html', '' ],
+ 'Approval' => [ $fsurl.'rt/Approvals/', '' ],
;
$tools_ticketing{'Cron Tool'} = [ $fsurl.'rt/Developer/CronTool/', '' ]
if $conf->exists('rt-crontool');
@@ -451,6 +466,7 @@ tie my %config_radius, 'Tie::IxHash',
tie my %config_export_svc, 'Tie::IxHash', ();
if ( $curuser->access_right('Configuration') ) {
$config_export_svc{'Service definitions'} = [ $fsurl.'browse/part_svc.cgi', 'Services are items you offer to your customers' ];
+ $config_export_svc{'Service classes'} = [ $fsurl.'browse/part_svc_class.html', 'Services classes are user-defined, informational types for services' ];
$config_export_svc{'Provisioning exports'} = [ $fsurl.'browse/part_export.cgi', 'Provisioning services to external machines, databases and APIs' ];
}
$config_export_svc{'Dialup'} = [ \%config_dialup, '' ]
@@ -504,6 +520,10 @@ tie my %config_agent, 'Tie::IxHash',
'Agent payment gateways' => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors for agent overrides' ];
;
+tie my %config_sales, 'Tie::IxHash',
+ 'Sales' => [ $fsurl.'browse/sales.cgi', 'Sales bring in new business.' ],
+;
+
tie my %config_billing_rates, 'Tie::IxHash',
'Rate plans' => [ $fsurl.'browse/rate.cgi', 'Manage rate plans' ],
'Regions and prefixes' => [ $fsurl.'browse/rate_region.html', 'Manage regions and prefixes' ],
@@ -539,12 +559,44 @@ if ( $curuser->access_right('Configuration') ) {
$config_billing{'Credit reason types'} = [ $fsurl.'browse/reason_type.html?class=R', 'Credit reason types define groups of reasons.' ];
}
+#XXX also to be unified
+tie my %config_ticketing_groups, 'Tie::IxHash',
+ 'Select' => [ $fsurl.'rt/Admin/Groups', '' ],
+ 'Create' => [ $fsurl.'rt/Admin/Groups/Modify.html?Create=1', '' ],
+;
+
+tie my %config_ticketing_queues, 'Tie::IxHash',
+ 'Select' => [ $fsurl.'rt/Admin/Queues', '' ],
+ 'Create' => [ $fsurl.'rt/Admin/Queues/Modify.html?Create=1', '' ],
+;
+
+tie my %config_ticketing_customfields, 'Tie::IxHash',
+ 'Select' => [ $fsurl.'rt/Admin/CustomFields', '' ],
+ 'Create' => [ $fsurl.'rt/Admin/CustomFields/Modify.html?Create=1', '' ],
+;
+
+tie my %config_ticketing_articles_classes, 'Tie::IxHash',
+ 'Select' => [ $fsurl.'rt/Admin/Articles/Classes/', '' ],
+ 'Create' => [ $fsurl.'rt/Admin/Articles/Classes/Modify.html?Create=1', '' ],
+;
+
+tie my %config_ticketing_articles_customfields, 'Tie::IxHash',
+ 'Select' => [ $fsurl.'rt/Admin/CustomFields/index.html?type=RT%3A%3AClass-RT%3A%3AArticle', '' ],
+ 'Create' => [ $fsurl.'rt/Admin/CustomFields/Modify.html?Create=1&LookupType=RT%3A%3AClass-RT%3A%3AArticle', '' ],
+;
+
+tie my %config_ticketing_articles, 'Tie::IxHash',
+ 'Classes' => [ \%config_ticketing_articles_classes, '' ],
+ 'Custom Fields' => [ \%config_ticketing_articles_customfields, '' ],
+;
+
tie my %config_ticketing, 'Tie::IxHash',
- 'Ticketing Users' => [ $fsurl.'rt/Admin/Users', 'View/Edit ticketing users' ], #XXX to be unified
- 'Ticketing Groups' => [ $fsurl.'rt/Admin/Groups', 'View/Edit ticketing groups and group membership' ], #XXX to be unified
- 'Ticketing Queues' => [ $fsurl.'rt/Admin/Queues', 'View/Edit ticketing queues and queue-specific properties' ],
- 'Ticket Custom Fields' => [ $fsurl.'rt/Admin/CustomFields', 'View/Edit ticketing custom fields' ],
- 'Ticketing Global' => [ $fsurl.'rt/Admin/Global', 'View/Edit ticketing configuration applicable to all queues' ],
+ 'Ticketing Users' => [ $fsurl.'rt/Admin/Users', 'Edit ticketing users' ], #XXX to be unified
+ 'Ticketing Groups' => [ \%config_ticketing_groups, 'View/Edit ticketing groups and group membership' ], #XXX to be unified
+ 'Ticketing Queues' => [ \%config_ticketing_queues, 'View/Edit ticketing queues and queue-specific properties' ],
+ 'Ticket Custom Fields' => [ \%config_ticketing_customfields, 'View/Edit ticketing custom fields' ],
+ 'Ticketing Global' => [ $fsurl.'rt/Admin/Global', 'View/Edit ticketing configuration applicable to all queues' ], #XXX the individual items
+ 'Ticketing Articles' => [ \%config_ticketing_articles, '' ],
#"System Configuraiton"? useless, just makes people report errors about missing Module::Versions::Report #'Ticketing Tools' => [ $fsurl.'rt/Admin/Tools', '' ],
;
@@ -554,9 +606,8 @@ tie my %config_nms, 'Tie::IxHash',
tie my %config_misc, 'Tie::IxHash';
$config_misc{'Message templates'} = [ $fsurl.'browse/msg_template.html', 'Templates for customer notices' ]
- if $curuser->access_right('Edit templates')
- || $curuser->access_right('Edit global templates')
- || $curuser->access_right('Configuration');
+ if $curuser->access_right(['View templates', 'View global templates',
+ 'Edit templates', 'Edit global templates', ]);
$config_misc{'Advertising sources'} = [ $fsurl.'browse/part_referral.html', 'Where a customer heard about your service.' ]
if $curuser->access_right('Edit advertising sources')
|| $curuser->access_right('Edit global advertising sources');
@@ -569,6 +620,9 @@ $config_misc{'Inventory classes and inventory'} = [ $fsurl.'browse/inventory_cla
|| $curuser->access_right('Edit global inventory')
|| $curuser->access_right('Configuration');
+$config_misc{'FTP targets'} = [ $fsurl.'browse/ftp_target.html', 'FTP servers for billing and payment processing' ]
+ if $curuser->access_right('Configuration');
+
tie my %config_menu, 'Tie::IxHash';
if ( $curuser->access_right('Configuration' ) ) {
%config_menu = (
@@ -576,6 +630,7 @@ if ( $curuser->access_right('Configuration' ) ) {
'separator' => '', #its a separator!
'Employees' => [ \%config_employees, '' ],
'Resellers' => [ \%config_agent, '' ],
+ 'Sales People' => [ \%config_sales, '' ],
'separator2' => '', #its a separator!
'Customers' => [ \%config_cust, '' ],
#or this? 'Customers and Contacts' => [ \%config_cust, '' ],
@@ -620,17 +675,24 @@ my $doc_link = $conf->config('support-key')
eval "use RT;"
if $conf->config('ticket_system') eq 'RT_Internal';
-tie my %help_menu, 'Tie::IxHash', 'Billing documentation' => [ $doc_link, 'Freeside documentation' ];
-$help_menu{'Ticketing documentation'} = [ 'http://wiki.bestpractical.com/', 'Request Tracker Wiki' ]
- if $conf->config('ticket_system') eq 'RT_Internal';
-$help_menu{'Networking monitoring documentation'} = [ 'http://torrus.org/userguide.pod.html', 'Torrus User Guide' ]
- if $conf->config('network_monitoring_system') eq 'Torrus_Internal';
-$help_menu{'separator'} = '';
-$help_menu{"About Freeside v$FS::VERSION"} = [ "javascript:about_freeside()", '' ];
-$help_menu{"About RT v$RT::VERSION"} = [ 'http://www.bestpractical.com/rt', 'Request Tracker Homepage' ]
- if $conf->config('ticket_system') eq 'RT_Internal';
-$help_menu{"About Torrus v1.0.9"} = [ 'http://www.torrus.org/', 'Torrus Homepage' ] #XXX manual version
- if $conf->config('network_monitoring_system') eq 'Torrus_Internal';
+tie my %help_menu, 'Tie::IxHash';
+my $agentnum = $conf->config('brand-agent');
+if ( $agentnum ) {
+ my $company_name = $conf->config('company_name', $agentnum);
+ $help_menu{"About $company_name"} = [ "javascript:about_freeside()", '' ];
+} else {
+ $help_menu{'Billing documentation'} = [ $doc_link, 'Freeside documentation' ];
+ $help_menu{'Ticketing documentation'} = [ 'http://wiki.bestpractical.com/', 'Request Tracker Wiki' ]
+ if $conf->config('ticket_system') eq 'RT_Internal';
+ $help_menu{'Networking monitoring documentation'} = [ 'http://torrus.org/userguide.pod.html', 'Torrus User Guide' ]
+ if $conf->config('network_monitoring_system') eq 'Torrus_Internal';
+ $help_menu{'separator'} = '';
+ $help_menu{"About Freeside v$FS::VERSION"} = [ "javascript:about_freeside()", '' ];
+ $help_menu{"About RT v$RT::VERSION"} = [ 'http://www.bestpractical.com/rt', 'Request Tracker Homepage' ]
+ if $conf->config('ticket_system') eq 'RT_Internal';
+ $help_menu{"About Torrus v1.0.9"} = [ 'http://www.torrus.org/', 'Torrus Homepage' ] #XXX manual version
+ if $conf->config('network_monitoring_system') eq 'Torrus_Internal';
+}
tie my %menu, 'Tie::IxHash';
diff --git a/httemplate/elements/order_pkg_link.html b/httemplate/elements/order_pkg_link.html
new file mode 100644
index 000000000..d8aa8fd4e
--- /dev/null
+++ b/httemplate/elements/order_pkg_link.html
@@ -0,0 +1,26 @@
+<& /elements/popup_link-cust_main.html,
+ 'action' => $p. 'misc/order_pkg.html',
+ 'label' => $opt{'label'} || emt('Order new package'),
+ 'actionlabel' => $opt{'actionlabel'} || emt('Order new package'),
+ 'color' => '#333399',
+ 'cust_main' => $opt{cust_main},
+ 'prospect_main' => $opt{prospect_main},
+ 'custnum' => $opt{custnum},
+ 'prospectnum' => $opt{prospectnum},
+ 'closetext' => emt('Close'),
+ 'width' => 960, #763,
+ 'height' => $height,
+ %optional,
+&>
+<%init>
+
+my(%opt) = @_;
+
+my %optional =
+ map { $_ => $opt{$_} }
+ grep $opt{$_},
+ qw( lock_pkgpart lock_locationnum qualnum quotationnum svcpart );
+
+my $height = $opt{'lock_locationnum'} ? 336 : 576;
+
+</%init>
diff --git a/httemplate/elements/popup_link-cust_main.html b/httemplate/elements/popup_link-cust_main.html
index 14137859f..541bb5837 100644
--- a/httemplate/elements/popup_link-cust_main.html
+++ b/httemplate/elements/popup_link-cust_main.html
@@ -22,7 +22,7 @@ Example:
)
</%doc>
-% if ( $params->{'cust_main'} ) {
+% if ( $custnum || $prospectnum ) {
<% include('/elements/popup_link.html', $params ) %>\
% }
<%init>
@@ -34,13 +34,26 @@ if (ref($_[0]) eq 'HASH') {
} else {
$params = { %$params, @_ };
}
+
+my $custnum = $params->{'cust_main'}
+ ? $params->{'cust_main'}->custnum
+ : $params->{'custnum'};
+
+$params->{'action'} .= ( $params->{'action'} =~ /\?/ ? ';' : '?' ).
+ "custnum=$custnum"
+ if $custnum;
+
+#(maybe i should be called popup_link-cust_or_prospect_main.html now)
+my $prospectnum = $params->{'prospect_main'}
+ ? $params->{'prospect_main'}->prospectnum
+ : $params->{'prospectnum'};
-$params->{'action'} .=
- ( $params->{'action'} =~ /\?/ ? ';' : '?' ).
- 'custnum='. $params->{'cust_main'}->custnum;
+$params->{'action'} .= ( $params->{'action'} =~ /\?/ ? ';' : '?' ).
+ "prospectnum=$prospectnum"
+ if $prospectnum;
$params->{'action'} .= ";$_=".$params->{$_}
foreach grep $params->{$_},
- qw( lock_pkgpart lock_locationnum qualnum svcpart );
+ qw( lock_pkgpart lock_locationnum qualnum quotationnum svcpart );
</%init>
diff --git a/httemplate/elements/progress-init.html b/httemplate/elements/progress-init.html
index 2ec248e32..7a282a34c 100644
--- a/httemplate/elements/progress-init.html
+++ b/httemplate/elements/progress-init.html
@@ -54,10 +54,7 @@ sub process_whatever { #class method
)
%>
-% if (!$noinit) {
<& /elements/init_overlib.html &>
-% $noinit = 1;
-% }
<SCRIPT TYPE="text/javascript">
@@ -117,9 +114,6 @@ function <%$key%>myCallback( jobnum ) {
</SCRIPT>
-<%once>
-my $noinit = 0;
-</%once>
<%init>
my( $formname, $fields, $action, $url_or_message, $key ) = @_;
diff --git a/httemplate/elements/searchbar-ticket.html b/httemplate/elements/searchbar-ticket.html
index 774ca9875..30624f7d3 100644
--- a/httemplate/elements/searchbar-ticket.html
+++ b/httemplate/elements/searchbar-ticket.html
@@ -2,7 +2,7 @@
<FORM ACTION="<% FS::TicketSystem->baseurl %>index.html" METHOD="GET" STYLE="margin:0">
<INPUT NAME="q" TYPE="text" VALUE="<% $ticketing_label |n %>" STYLE="width:<% $width %>" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" CLASS="fstext"><BR>
- <A HREF="<% FS::TicketSystem->baseurl %>Search/Build.html" CLASS="fslink" STYLE="font-size:11px"><% mt('Advanced') |h %></A>
+ <A HREF="<% FS::TicketSystem->baseurl %>Search/Build.html?NewQuery=1" CLASS="fslink" STYLE="font-size:11px"><% mt('Advanced') |h %></A>
<INPUT TYPE="submit" VALUE="<% mt('Search tickets') |h %>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:11px">
</FORM>
<% $menu_position eq 'left' ? '<BR>' : '' %>
diff --git a/httemplate/elements/select-cust-part_pkg.html b/httemplate/elements/select-cust-part_pkg.html
index 731beae2a..2d4cd183d 100644
--- a/httemplate/elements/select-cust-part_pkg.html
+++ b/httemplate/elements/select-cust-part_pkg.html
@@ -27,9 +27,9 @@ Example:
my( %opt ) = @_;
-my $cust_main = $opt{'cust_main'}
- or die "cust_main not specified";
+my $cust_or_prospect_main = $opt{'cust_main'} || $opt{'prospect_main'}
+ or die "neither cust_main nor prospect_main specified";
-$opt{'extra_sql'} .= ' AND '. FS::part_pkg->agent_pkgs_sql( $cust_main->agent );
+$opt{'extra_sql'} .= ' AND '. FS::part_pkg->agent_pkgs_sql( $cust_or_prospect_main->agent );
</%init>
diff --git a/httemplate/elements/select-part_svc_class.html b/httemplate/elements/select-part_svc_class.html
new file mode 100644
index 000000000..280e3e17d
--- /dev/null
+++ b/httemplate/elements/select-part_svc_class.html
@@ -0,0 +1,22 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'part_svc_class',
+ 'name_col' => 'classname',
+ 'value' => $classnum,
+ 'empty_label' => '(none)',
+ 'hashref' => \%hash,
+ %opt,
+ )
+%>
+<%init>
+
+my %opt = @_;
+my $classnum = $opt{'curr_value'} || $opt{'value'};
+
+my %hash = ();
+$hash{'disabled'} = '' unless $opt{'showdisabled'};
+
+
+$opt{'records'} = delete $opt{'part_svc_class'}
+ if $opt{'part_svc_class'};
+
+</%init>
diff --git a/httemplate/elements/select-rt-customfield.html b/httemplate/elements/select-rt-customfield.html
new file mode 100644
index 000000000..7a45bb14b
--- /dev/null
+++ b/httemplate/elements/select-rt-customfield.html
@@ -0,0 +1,34 @@
+<SELECT NAME="<% $opt{name} %>">
+% while ( @fields ) {
+<OPTION VALUE="<% shift @fields %>"><% shift @fields %></OPTION>
+% }
+</SELECT>
+<%once>
+RT::Init();
+</%once>
+<%init>
+my %opt = @_;
+my $lookuptype = $opt{lookuptype};
+my $valuetype = $opt{valuetype};
+# get a list of TimeValue-type custom fields
+my $CurrentUser = RT::CurrentUser->new();
+$CurrentUser->LoadByName($FS::CurrentUser::CurrentUser->username);
+die "RT not configured" unless $CurrentUser->id;
+my $CFs = RT::CustomFields->new($CurrentUser);
+
+$CFs->Limit(FIELD => 'LookupType',
+ OPERATOR => 'ENDSWITH',
+ VALUE => $lookuptype)
+ if $lookuptype;
+
+$CFs->Limit(FIELD => 'Type',
+ VALUE => $valuetype)
+ if $valuetype;
+
+my @fields;
+push @fields, '', $opt{empty_label} if exists($opt{empty_label});
+
+while (my $CF = $CFs->Next) {
+ push @fields, $CF->Name, ($CF->Description || $CF->Name);
+}
+</%init>
diff --git a/httemplate/elements/select-table.html b/httemplate/elements/select-table.html
index c0dde7414..127028ee5 100644
--- a/httemplate/elements/select-table.html
+++ b/httemplate/elements/select-table.html
@@ -93,10 +93,17 @@ Example:
% )
% {
% my $recvalue = $record->$key();
+% my $selected;
+% if ( $opt{'all_selected'} ) {
+% $selected = 1;
+% } elsif ( $opt{'compare_sub'} && !ref($value) ) {
+% $selected = &{ $opt{'compare_sub'} }( $value, $recvalue );
+% } else {
+% $selected = ( ref($value) && $value->{$recvalue} )
+% || ( $value && $value eq $recvalue ); #not == because of value_col
+% }
<OPTION VALUE="<% $recvalue %>"
- <% $opt{'all_selected'} || ref($value) && $value->{$recvalue} || $value && $value eq $recvalue # not == because of value_col
- ? ' SELECTED' : ''
- %>
+ <% $selected ? ' SELECTED' : '' %>
% foreach my $att ( @{ $opt{'extra_option_attributes'} } ) {
data-<% $att %>="<% $record->$att() |h %>"
% }
diff --git a/httemplate/elements/standardize_locations.js b/httemplate/elements/standardize_locations.js
index e6a4aa607..86f8d2be8 100644
--- a/httemplate/elements/standardize_locations.js
+++ b/httemplate/elements/standardize_locations.js
@@ -10,7 +10,7 @@ function standardize_locations() {
'onlyship', 1,
% } else {
% if ( $withfirm ) {
- 'company', cf.elements['<% $main_prefix %>company'].value,
+ 'company', cf.elements['company'].value,
% }
'address1', cf.elements['<% $main_prefix %>address1'].value,
'address2', cf.elements['<% $main_prefix %>address2'].value,
@@ -18,9 +18,6 @@ function standardize_locations() {
'state', state_el.options[ state_el.selectedIndex ].value,
'zip', cf.elements['<% $main_prefix %>zip'].value,
% }
-% if ( $withfirm ) {
- 'ship_company', cf.elements['<% $ship_prefix %>company'].value,
-% }
'ship_address1', cf.elements['<% $ship_prefix %>address1'].value,
'ship_address2', cf.elements['<% $ship_prefix %>address2'].value,
'ship_city', cf.elements['<% $ship_prefix %>city'].value,
diff --git a/httemplate/elements/table-grid.html b/httemplate/elements/table-grid.html
index 4d7deeaa4..4f4200570 100644
--- a/httemplate/elements/table-grid.html
+++ b/httemplate/elements/table-grid.html
@@ -1,15 +1,4 @@
-<STYLE TYPE="text/css">
-
-.grid TH { padding-left: 3px; padding-right: 3px; padding-bottom: 2px; border: none; empty-cells: show }
-.grid TD { padding-left: 3px; padding-right: 3px; padding-bottom: 2px; border: none; empty-cells: show }
-
-.inv table { border: none }
-.inv TH { border: none }
-.inv TD { border: none }
-
-</STYLE>
-
-<TABLE CLASS="grid" CELLSPACING=<% $opt{cellspacing} %> CELLPADDING=<% $opt{cellpadding} %> <% $opt{bgcolor} %> STYLE="border: 1px solid #cccccc;">
+<TABLE CLASS="grid" CELLSPACING=<% $opt{cellspacing} %> CELLPADDING=<% $opt{cellpadding} %> <% $opt{bgcolor} %>>
<%init>
diff --git a/httemplate/elements/table-tickets.html b/httemplate/elements/table-tickets.html
new file mode 100644
index 000000000..6d1a45a0d
--- /dev/null
+++ b/httemplate/elements/table-tickets.html
@@ -0,0 +1,159 @@
+<& /elements/form-create_ticket.html, object => $object &>
+ |
+View
+<A HREF="<% $open_link %>"><% mt($openlabel) |h %></A> |
+<A HREF="<% $res_link %>"><% mt('resolved') |h %></A>
+ <BR>
+
+<& /elements/table-grid.html &>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+
+<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('#') |h %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Subject') |h %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Status') |h %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Queue') |h %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Owner') |h %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Due') |h %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Estimated Time') |h %></TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Priority') |h %></TH>
+% if ( $ss_priority ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Customer Priority') |h %></TH>
+% }
+% if ( $object->isa('FS::cust_main') ) {
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Service') |h %></TH>
+% }
+</TR>
+
+% foreach my $ticket ( @tickets ) {
+% my $href = FS::TicketSystem->href_ticket($ticket->{id});
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+
+ <TR>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF=<%$href%>><% $ticket->{id} %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF=<%$href%>><% $ticket->{subject} %></A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{status} %>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{queue} %>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{owner} %>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $date_formatter->($ticket->{due}) %>
+ </TD>
+
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{timeestimated} %>
+ </TD>
+
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{content}
+ ? $ticket->{content}.' ('.$ticket->{priority}.')'
+ : $ticket->{priority}
+ %>
+ </TD>
+
+% if ( $ss_priority ) {
+ <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <% $ticket->{"CF.{$ss_priority}"} %>
+ </TD>
+% }
+% if ( $object->isa('FS::cust_main') ) {
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><FONT SIZE=-1><B>
+% foreach (@{ $ticket->{svcnums} }) {
+% my $cust_svc = FS::cust_svc->by_key($_) or next;
+ <% FS::UI::Web::svc_label_link($m, $cust_svc->part_svc, $cust_svc) %>
+ <BR>
+% }
+ </B></FONT></TD>
+% }
+
+ </TR>
+
+% }
+
+</TABLE>
+
+<%init>
+use Date::Parse qw(str2time);
+use Date::Format qw(time2str);
+
+my %opt = @_;
+my $conf = new FS::Conf;
+
+my $object = $opt{'object'};
+$object = $object->cust_svc if $object->isa('FS::svc_Common');
+my( @tickets ) = $object->tickets;
+
+my ($openlabel, $open_link, $res_link, $thing);
+$openlabel = join('/', FS::TicketSystem->statuses );
+
+# not the nicest way to do this--FS::has_tickets_Common?
+if ( $object->isa('FS::cust_main') ) {
+ $thing = 'customer';
+ $open_link = FS::TicketSystem->href_customer_tickets($object->custnum);
+
+ $res_link = FS::TicketSystem->href_customer_tickets(
+ $object->custnum,
+ { 'statuses' => [ 'resolved' ] }
+ );
+}
+elsif ( $object->isa('FS::cust_svc') ) {
+ $thing = 'service';
+ $open_link = FS::TicketSystem->href_service_tickets($object->svcnum);
+
+ $res_link = FS::TicketSystem->href_service_tickets(
+ $object->svcnum,
+ { 'statuses' => [ 'resolved' ] }
+ );
+}
+
+my $ss_priority = FS::TicketSystem->selfservice_priority;
+if ( $ss_priority ) {
+ my $dir = $conf->exists('ticket_system-priority_reverse') ? -1 : 1;
+ use sort 'stable';
+ # sort in the following way:
+ @tickets = sort {
+ # within a severity level...
+ ( $a->{'content'} eq $b->{'content'} ) ? (
+ # no-priority tickets sort last
+ (
+ ($a->{'_selfservice_priority'} eq '') <=>
+ ($b->{'_selfservice_priority'} eq '')
+ ) ||
+ # otherwise obey ticket_system-priority_reverse
+ ( $dir *
+ ($b->{'_selfservice_priority'} <=> $a->{'_selfservice_priority'})
+ )
+ ) : 0; # but don't rearrange between severity levels
+ } @tickets;
+}
+
+my $format = $conf->config('date_format') || '%Y-%m-%d';
+
+my $date_formatter = sub {
+ my $time = str2time($_[0], 'GMT');
+ # exclude times within 24 hours of zero
+ ($time > 86400) ? time2str($format, $time) : '';
+};
+
+</%init>
diff --git a/httemplate/elements/tr-cust_svc.html b/httemplate/elements/tr-cust_svc.html
index ca5de86b4..1ca22f6d4 100644
--- a/httemplate/elements/tr-cust_svc.html
+++ b/httemplate/elements/tr-cust_svc.html
@@ -24,7 +24,7 @@ Usage:
<TD ALIGN="right" VALIGN="top"><%
FS::UI::Web::svc_link($m, $part_svc, $cust_svc)
%></TD>
- <TD STYLE="padding-bottom:0px"><B><%
+ <TD STYLE="padding-bottom:0px"><B><% $cust_svc->agent_svcid ? $cust_svc->agent_svcid.': ' : '' %><%
FS::UI::Web::svc_label_link($m, $part_svc, $cust_svc)
%></B></TD>
<TD ALIGN="right"><% FS::UI::Web::svc_export_links($m, $part_svc, $cust_svc) %>
diff --git a/httemplate/elements/tr-fixed-cust_main.html b/httemplate/elements/tr-fixed-cust_main.html
new file mode 100644
index 000000000..00bcb66d8
--- /dev/null
+++ b/httemplate/elements/tr-fixed-cust_main.html
@@ -0,0 +1,15 @@
+% if ( $cust_main ) {
+ <% include('tr-fixed.html', %opt ) %>
+% }
+<%init>
+
+my %opt = @_;
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+my $cust_main = $value ? qsearchs('cust_main', {custnum=>$value} )
+ : '';
+
+$opt{'formatted_value'} = $cust_main->name if $cust_main;
+
+</%init>
diff --git a/httemplate/elements/tr-fixed-date.html b/httemplate/elements/tr-fixed-date.html
new file mode 100644
index 000000000..716e5ceb8
--- /dev/null
+++ b/httemplate/elements/tr-fixed-date.html
@@ -0,0 +1,13 @@
+<% include('tr-fixed.html', %opt ) %>
+<%init>
+
+my %opt = @_;
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+my $conf = new FS::Conf;
+my $date_format = $conf->config('date_format') || '%m/%d/%Y';
+
+$opt{'formatted_value'} = time2str($date_format, $value);
+
+</%init>
diff --git a/httemplate/elements/tr-fixed-prospect_main.html b/httemplate/elements/tr-fixed-prospect_main.html
new file mode 100644
index 000000000..8da0ffb84
--- /dev/null
+++ b/httemplate/elements/tr-fixed-prospect_main.html
@@ -0,0 +1,15 @@
+% if ( $prospect_main ) {
+ <% include('tr-fixed.html', %opt ) %>
+% }
+<%init>
+
+my %opt = @_;
+
+my $value = $opt{'curr_value'} || $opt{'value'};
+
+my $prospect_main = $value ? qsearchs('prospect_main', {prospectnum=>$value} )
+ : '';
+
+$opt{'formatted_value'} = $prospect_main->name if $prospect_main;
+
+</%init>
diff --git a/httemplate/elements/tr-fixed.html b/httemplate/elements/tr-fixed.html
index f358343dd..dd07d90b6 100644
--- a/httemplate/elements/tr-fixed.html
+++ b/httemplate/elements/tr-fixed.html
@@ -13,13 +13,15 @@ my %opt = @_;
my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
my $value = $opt{'formatted_value'} || $opt{'curr_value'} || $opt{'value'};
-#compatibility with select-table and friends
-if ( $opt{'multiple'} ) {
- $value = [ split(/\s*,\s*/, $value) ] if !ref $value;
- $value = join('<BR>', map {encode_entities($_)} @$value);
-}
-else {
- $value = encode_entities($value)
+
+unless ( $opt{'noescape'} ) {
+ #compatibility with select-table and friends
+ if ( $opt{'multiple'} ) {
+ $value = [ split(/\s*,\s*/, $value) ] if !ref $value;
+ $value = join('<BR>', map {encode_entities($_)} @$value);
+ } else {
+ $value = encode_entities($value)
+ }
}
</%init>
diff --git a/httemplate/elements/tr-select-agent.html b/httemplate/elements/tr-select-agent.html
index ce03c40f5..321bd6b32 100644
--- a/httemplate/elements/tr-select-agent.html
+++ b/httemplate/elements/tr-select-agent.html
@@ -22,7 +22,7 @@ Example:
);
</%doc>
-% if ( scalar(@agents) == 1 ) {
+% if ( scalar(@agents) == 1 || $opt{'fixed'} ) {
<INPUT TYPE = "hidden"
NAME = "<% $opt{'field'} || 'agentnum' %>"
@@ -30,9 +30,20 @@ Example:
VALUE = "<% $agents[0]->agentnum %>"
>
-%# YUCK. empty row so we don't throw g_row in edit.html off :/
- <TR>
- </TR>
+% if ( scalar(@agents) != 1 ) {
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || emt('Agent') %></TD>
+ <TD BGCOLOR="#dddddd" <% $colspan %>>
+% my $agent = qsearchs('agent', { 'agentnum' => $agentnum });
+ <% $agent ? $agent->agent : '(all)' |h %>
+ </TD>
+ </TR>
+
+% } else { # YUCK. empty row so we don't throw g_row in edit.html off :/
+ <TR>
+ </TR>
+% }
+%
% } else {
<TR>
diff --git a/httemplate/elements/tr-select-cust-part_pkg.html b/httemplate/elements/tr-select-cust-part_pkg.html
index 767d23264..848ab0a4b 100644
--- a/httemplate/elements/tr-select-cust-part_pkg.html
+++ b/httemplate/elements/tr-select-cust-part_pkg.html
@@ -51,8 +51,11 @@
}
- get_part_pkg( <% $cust_main->custnum %>, classnum, update_part_pkg );
-
+ get_part_pkg( <% $cust_main ? $cust_main->custnum : '0' %>,
+ <% $prospect_main ? $prospect_main->prospectnum : '0' %>,
+ classnum,
+ update_part_pkg
+ );
}
</SCRIPT>
@@ -74,10 +77,11 @@
<TH ALIGN="right"><% mt('Package') |h %></TH>
<TD COLSPAN=7>
<& /elements/select-cust-part_pkg.html,
- 'curr_value' => $opt{'curr_value'}, #$pkgpart
- 'classnum' => $opt{'classnum'},
- 'cust_main' => $opt{'cust_main'}, #$cust_main
- 'onchange' => 'pkg_changed',
+ 'curr_value' => $opt{'curr_value'}, #$pkgpart
+ 'classnum' => $opt{'classnum'},
+ 'cust_main' => $opt{'cust_main'}, #$cust_main
+ 'prospect_main' => $opt{'prospect_main'}, #$prospect_main
+ 'onchange' => 'pkg_changed',
&>
</TD>
</TR>
@@ -91,8 +95,13 @@ my %opt = @_;
my $pre_label = $opt{'pre_label'} || '';
$pre_label .= ' ' if length($pre_label) && $pre_label =~ /\S$/;
-my $cust_main = $opt{'cust_main'}
- or die "cust_main not specified";
+my $cust_main = $opt{'cust_main'};
+my $prospect_main = $opt{'prospect_main'};
+
+die "neither cust_main nor prospect_main specified"
+ unless $cust_main || $prospect_main;
+
+my $agent = $cust_main ? $cust_main->agent : $prospect_main->agent;
#"normal" part_pkg agent virtualization (agentnum or type)
my @part_pkg = qsearch({
@@ -101,7 +110,7 @@ my @part_pkg = qsearch({
'hashref' => { 'disabled' => '' },
'extra_sql' =>
' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql( 'null'=>1 ).
- ' AND '. FS::part_pkg->agent_pkgs_sql( $opt{'cust_main'}->agent ),
+ ' AND '. FS::part_pkg->agent_pkgs_sql( $agent ),
});
my @pkg_class =
diff --git a/httemplate/elements/tr-select-cust_location.html b/httemplate/elements/tr-select-cust_location.html
index 0ca255b3e..d9e3e9e27 100644
--- a/httemplate/elements/tr-select-cust_location.html
+++ b/httemplate/elements/tr-select-cust_location.html
@@ -11,7 +11,6 @@ Example:
#optional
'empty_label' => '(default service address)',
- 'disable_empty' => 0, #1 to disable
)
</%doc>
@@ -52,11 +51,12 @@ Example:
var ftype = what.form.<%$_%>.tagName;
if( ftype != 'SELECT') what.form.<%$_%>.style.backgroundColor = '#ffffff';
% }
-
+% if ( $opt{'alt_format'} ) {
if ( what.form.location_type.options[what.form.location_type.selectedIndex].value ) {
what.form.location_number.disabled = false;
what.form.location_number.style.backgroundColor = '#ffffff';
}
+% }
}
function locationnum_changed(what) {
@@ -101,25 +101,8 @@ Example:
return;
}
- if ( locationnum == 0 ) { //(default service address)
-% if ( $cust_main ) {
- what.form.address1.value = <% $cust_main->get($prefix.'address1') |js_string %>;
- what.form.address2.value = <% $cust_main->get($prefix.'address2') |js_string %>;
- what.form.city.value = <% $cust_main->get($prefix.'city') |js_string %>;
- what.form.zip.value = <% $cust_main->get($prefix.'zip') |js_string %>;
-
- changeSelect(what.form.country, <% $cust_main->get($prefix.'country') | js_string %> );
-
- country_changed( what.form.country,
- fix_state_factory( <% $cust_main->get($prefix.'state') | js_string %>,
- <% $cust_main->get($prefix.'county') | js_string %>
- )
- );
-% }
-
- } else {
- get_location( locationnum, update_location );
- }
+%# default service address is now just another location
+ get_location( locationnum, update_location );
% if ( $editable ) {
if ( locationnum == 0 ) {
@@ -203,14 +186,16 @@ Example:
ID = "locationnum"
onChange = "locationnum_changed(this);"
>
-% if ( !$prospect_main && !$opt{'disable_empty'} ) {
- <OPTION VALUE=""><% $opt{'empty_label'} || '(default service address)' |h %>
+% if ( $cust_main ) {
+ <OPTION VALUE="<% $cust_main->ship_locationnum %>"><% $opt{'empty_label'} || '(default service address)' |h %>
% }
% if ( $opt{'is_optional'} ) {
<OPTION VALUE="-2" <% $locationnum == -2 ? 'SELECTED' : ''%>><% $opt{'optional_label'} || '(not required)' |h %>
% }
%
% foreach my $loc ( @cust_location ) {
+% # don't show the ship_location redundantly
+% next if $cust_main && $cust_main->ship_locationnum == $loc->locationnum;
<OPTION VALUE="<% $loc->locationnum %>"
<% $locationnum == $loc->locationnum ? 'SELECTED' : '' %>
><% $loc->line |h %>
@@ -233,7 +218,9 @@ Example:
'alt_format' => $opt{'alt_format'},
)
%>
-
+<SCRIPT TYPE="text/javascript">
+ locationnum_changed(document.getElementById('locationnum'));
+</SCRIPT>
<%init>
my $conf = new FS::Conf;
@@ -246,8 +233,7 @@ my $cgi = $opt{'cgi'};
my $cust_pkg = $opt{'cust_pkg'};
my $cust_main = $opt{'cust_main'};
my $prospect_main = $opt{'prospect_main'};
-
-my $prefix = ($cust_main && length($cust_main->ship_last)) ? 'ship_' : '';
+die "cust_main or prospect_main required" unless $cust_main or $prospect_main;
my $locationnum = '';
if ( $cgi->param('error') ) {
@@ -259,9 +245,9 @@ if ( $cgi->param('error') ) {
} elsif ($prospect_main) {
my @cust_location = $prospect_main->cust_location;
$locationnum = $cust_location[0]->locationnum if scalar(@cust_location)==1;
- } else { #?
+ } else { #$cust_main
$cgi->param('locationnum') =~ /^(\-?\d*)$/ or die "illegal locationnum";
- $locationnum = $1;
+ $locationnum = $1 || $cust_main->ship_locationnum;
}
}
@@ -277,7 +263,7 @@ if ( $opt{'alt_format'} ) {
push @location_fields, qw( location_type location_number location_kind );
}
-my $cust_location;
+my $cust_location; #the one that shows by default in the location edit space
if ( $locationnum && $locationnum > 0 ) {
$cust_location = qsearchs('cust_location', { 'locationnum' => $locationnum } )
or die "unknown locationnum";
@@ -290,7 +276,7 @@ if ( $locationnum && $locationnum > 0 ) {
$cust_location->$_( $pkg_location->$_ ) foreach @location_fields;
$opt{'empty_label'} ||= 'package address: '.$pkg_location->line;
} elsif ( $cust_main ) {
- $cust_location->$_( $cust_main->get($prefix.$_) ) foreach @location_fields;
+ $cust_location = $cust_main->ship_location; #I think
}
}
@@ -301,7 +287,7 @@ my $location_sort = sub {
or lc($a->address2) cmp lc($b->address2)
};
-my @cust_location = ();
+my @cust_location;
push @cust_location, $cust_main->cust_location if $cust_main;
push @cust_location, $prospect_main->cust_location if $prospect_main;
push @cust_location, $cust_location
@@ -311,14 +297,14 @@ push @cust_location, $cust_location
@cust_location = sort $location_sort grep !$_->disabled, @cust_location;
$cust_location = $cust_location[0]
- if ( $prospect_main || $opt{'disable_empty'} )
+ if ( $prospect_main )
&& !$opt{'is_optional'}
&& @cust_location;
my $disabled =
( $locationnum < 0
|| ( $editable && $locationnum )
- || ( ( $prospect_main || $opt{'disable_empty'} )
+ || ( $prospect_main
&& !$opt{'is_optional'} && !@cust_location && $addnew
)
)
diff --git a/httemplate/elements/tr-select-cust_tag.html b/httemplate/elements/tr-select-cust_tag.html
index b2b6d967e..5312644ef 100644
--- a/httemplate/elements/tr-select-cust_tag.html
+++ b/httemplate/elements/tr-select-cust_tag.html
@@ -28,7 +28,7 @@ my $cgi = $opt{'cgi'};
my $is_report = $opt{'is_report'};
my @curr_tagnum = ();
-if ( $cgi->param('error') ) {
+if ( $cgi && $cgi->param('error') ) {
@curr_tagnum = $cgi->param('tagnum');
} elsif ( $opt{'custnum'} ) {
@curr_tagnum = map $_->tagnum,
diff --git a/httemplate/elements/tr-select-from_to.html b/httemplate/elements/tr-select-from_to.html
index 100381234..a27412f99 100644
--- a/httemplate/elements/tr-select-from_to.html
+++ b/httemplate/elements/tr-select-from_to.html
@@ -39,7 +39,7 @@
my %hash = (
'show_month_abbr' => 1,
'start_year' => '1999',
- 'end_year' => '2012', #haha, well...
+ 'end_year' => '2013', #haha, well...
@_,
);
</%init>
diff --git a/httemplate/elements/tr-select-part_svc_class.html b/httemplate/elements/tr-select-part_svc_class.html
new file mode 100644
index 000000000..2f4b09381
--- /dev/null
+++ b/httemplate/elements/tr-select-part_svc_class.html
@@ -0,0 +1,27 @@
+% if ( scalar(@{ $opt{'part_svc_class'} }) == 0 ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'classnum' %>" VALUE="">
+
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || 'Service class' %></TD>
+ <TD>
+ <% include( '/elements/select-part_svc_class.html',
+ 'curr_value' => $classnum,
+ %opt
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+
+<%init>
+
+my %opt = @_;
+my $classnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'part_svc_class'} ||= [ qsearch( 'part_svc_class', { disabled=>'' } ) ];
+
+</%init>
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
index ed8fe810f..95d1787b8 100644
--- a/httemplate/elements/tr-select-router_block_ip.html
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -1,23 +1,34 @@
<script type="text/javascript">
var manual_addr_routernum = <% encode_json(\%manual_addr_routernum) %>;
var ip_addr_curr_value = <% $opt{'ip_addr'} |js_string %>;
-function lock_ip_addr(obj, i) {
- var routernum = obj.value;
- var select_blocknum = document.getElementsByName('blocknum')[0];
+var blocknum_curr_value = <% $opt{'blocknum'} |js_string %>;
+function update_ip_addr(obj, i) {
+ var routernum = document.getElementById('router_select_0').value;
+ var select_blocknum = document.getElementById('router_select_1');
+ var blocknum = select_blocknum.value;
var input_ip_addr = document.getElementById('input_ip_addr');
if ( manual_addr_routernum[routernum] == 'Y' ) {
-%# enable ip_addr, default it to its previous value, and hide block selection
+%# hide block selection and default ip address to its previous value
select_blocknum.style.display = 'none';
input_ip_addr.value = ip_addr_curr_value;
- input_ip_addr.disabled = false;
}
else {
%# the reverse
select_blocknum.style.display = '';
- input_ip_addr.disabled = true;
- input_ip_addr.value = '(automatic)';
+%# default ip address to null, unless the router/block are set to the
+%# previous value, in which case default it to current value
+ if ( routernum == router_curr_values[0] &&
+ blocknum == router_curr_values[1] ) {
+ input_ip_addr.value = ip_addr_curr_value;
+ } else {
+ input_ip_addr.value = <% mt('(automatic)') |js_string %>;
+ }
}
}
+function clearhint_ip_addr (what) {
+ if ( what.value == <% mt('(automatic)') |js_string %> )
+ what.value = '';
+}
</script>
<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router') &>
<td>
@@ -27,7 +38,7 @@ function lock_ip_addr(obj, i) {
records => \@routers,
name_col => 'routername',
value_col => 'routernum',
- onchange => 'lock_ip_addr',
+ onchange => 'update_ip_addr',
curr_value=> $opt{'routernum'},
},
{
@@ -39,6 +50,7 @@ function lock_ip_addr(obj, i) {
name_col => 'cidr',
link_col => 'routernum',
empty_label => '(any)',
+ onchange => 'update_ip_addr',
curr_value => $opt{'blocknum'},
},
]
@@ -52,12 +64,11 @@ function lock_ip_addr(obj, i) {
% }
% else {
<input type="text" id="input_ip_addr" name="ip_addr"
- value="<% $opt{'ip_addr'} |h%>">
+ value="<% $opt{'ip_addr'} |h%>" onfocus="clearhint_ip_addr(this)">
% }
</td> </tr>
-<input type="hidden" name="prev_ip_addr" value="<% $opt{'ip_addr'} |h%>">
<script type="text/javascript">
-lock_ip_addr(document.getElementsByName('routernum')[0],0);
+update_ip_addr();
</script>
<%init>
diff --git a/httemplate/elements/xmenu.css b/httemplate/elements/xmenu.css
index 73699b6f9..e6a39f675 100644
--- a/httemplate/elements/xmenu.css
+++ b/httemplate/elements/xmenu.css
@@ -20,7 +20,7 @@
.webfx-menu {
position: absolute;
- z-index: 100;
+ z-index: 9999;
visibility: hidden;
border: 1px solid #7e0079;
-moz-border-radius: 4px;
diff --git a/httemplate/elements/xmenu.top.css b/httemplate/elements/xmenu.top.css
index ff0d6f684..7a42254e4 100644
--- a/httemplate/elements/xmenu.top.css
+++ b/httemplate/elements/xmenu.top.css
@@ -20,7 +20,7 @@
.webfx-menu {
position: absolute;
- z-index: 100;
+ z-index: 9999;
visibility: hidden;
border: 1px solid #7e0079;
-moz-border-radius: 4px;
diff --git a/httemplate/graph/cust_bill_pkg.cgi b/httemplate/graph/cust_bill_pkg.cgi
index af4d045fe..e7a3bd27e 100644
--- a/httemplate/graph/cust_bill_pkg.cgi
+++ b/httemplate/graph/cust_bill_pkg.cgi
@@ -49,6 +49,21 @@ elsif ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
die "agentnum $agentnum not found!" unless $sel_agent;
}
my $title = $sel_agent ? $sel_agent->agent.' ' : '';
+
+my( $refnum, $sel_part_referral, $all_part_referral ) = ('', '', '');
+if ( $cgi->param('refnum') eq 'all' ) {
+ $refnum = 0;
+ $all_part_referral = 'ALL';
+}
+elsif ( $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ $refnum = $1;
+ $bottom_link .= "refnum=$refnum;";
+ $sel_part_referral = qsearchs('part_referral', { 'refnum' => $refnum } );
+ die "part_referral $refnum not found!" unless $sel_part_referral;
+}
+$title .= $sel_part_referral->referral.' '
+ if $sel_part_referral;
+
$title .= 'Sales Report (Gross)';
$title .= ', average per customer package' if $average_per_cust_pkg;
@@ -130,42 +145,49 @@ foreach my $agent ( $all_agent || $sel_agent || qsearch('agent', { 'disabled' =>
### fixup the color handling for package classes...
### and usage
- my $n = 0;
-
- foreach my $pkg_class ( @pkg_class ) {
- foreach my $component ( @components ) {
-
- push @items, 'cust_bill_pkg';
-
- push @labels,
- ( $all_agent || $sel_agent ? '' : $agent->agent.' ' ).
- ( $classnum eq '0'
- ? ( ref($pkg_class) ? $pkg_class->classname : $pkg_class )
- : ''
- ).
- ' '.$charge_labels{$component};
-
- my $row_classnum = ref($pkg_class) ? $pkg_class->classnum : 0;
- my $row_agentnum = $all_agent || $agent->agentnum;
- push @params, [ ($all_class ? () : ('classnum' => $row_classnum) ),
- ($all_agent ? () : ('agentnum' => $row_agentnum) ),
- 'use_override' => $use_override,
- 'charges' => $component,
- 'average_per_cust_pkg' => $average_per_cust_pkg,
- 'distribute' => $distribute,
- ];
-
- push @links, "$link;".($all_agent ? '' : "agentnum=$row_agentnum;").
- ($all_class ? '' : "classnum=$row_classnum;").
- "distribute=$distribute;".
- "use_override=$use_override;charges=$component;";
-
- @recur_colors = ($col_scheme->colors)[0,4,8,1,5,9]
- unless @recur_colors;
- @onetime_colors = ($col_scheme->colors)[2,6,10,3,7,11]
- unless @onetime_colors;
- push @colors, shift @recur_colors;
+ foreach my $part_referral ( $all_part_referral || $sel_part_referral || qsearch('part_referral', { 'disabled' => '' } ) ) {
+
+ foreach my $pkg_class ( @pkg_class ) {
+ foreach my $component ( @components ) {
+
+ push @items, 'cust_bill_pkg';
+
+ push @labels,
+ ( $all_agent || $sel_agent ? '' : $agent->agent.' ' ).
+ ( $all_part_referral || $sel_part_referral ? '' : $part_referral->referral.' ' ).
+ ( $classnum eq '0'
+ ? ( ref($pkg_class) ? $pkg_class->classname : $pkg_class )
+ : ''
+ ).
+ ' '.$charge_labels{$component};
+
+ my $row_classnum = ref($pkg_class) ? $pkg_class->classnum : 0;
+ my $row_agentnum = $all_agent || $agent->agentnum;
+ my $row_refnum = $all_part_referral || $part_referral->refnum;
+ push @params, [ ($all_class ? () : ('classnum' => $row_classnum) ),
+ ($all_agent ? () : ('agentnum' => $row_agentnum) ),
+ ($all_part_referral ? () : ('refnum' => $row_refnum) ),
+ 'use_override' => $use_override,
+ 'charges' => $component,
+ 'average_per_cust_pkg' => $average_per_cust_pkg,
+ 'distribute' => $distribute,
+ ];
+
+ push @links, "$link;".
+ ($all_agent ? '' : "agentnum=$row_agentnum;").
+ ($all_part_referral ? '' : "refnum=$row_refnum;").
+ ($all_class ? '' : "classnum=$row_classnum;").
+ "distribute=$distribute;".
+ "use_override=$use_override;charges=$component;";
+
+ @recur_colors = ($col_scheme->colors)[0,4,8,1,5,9]
+ unless @recur_colors;
+ @onetime_colors = ($col_scheme->colors)[2,6,10,3,7,11]
+ unless @onetime_colors;
+ push @colors, shift @recur_colors;
+
+ }
}
}
diff --git a/httemplate/graph/cust_signup.html b/httemplate/graph/cust_signup.html
index dd9100f1e..a3eb702f2 100644
--- a/httemplate/graph/cust_signup.html
+++ b/httemplate/graph/cust_signup.html
@@ -9,7 +9,7 @@
'agentnum' => $agentnum,
'sprintf' => '%u',
'disable_money' => 1,
- 'bottom_total' => (scalar @items > 1 ? 1 : 0),
+ 'bottom_total' => (scalar @items > 1 && !$indirect ? 1 : 0),
'bottom_link' => $bottom_link,
'link_fromparam' => 'signupdate_begin',
'link_toparam' => 'signupdate_end',
@@ -59,25 +59,34 @@ elsif ( $cgi->param('refnum') =~ /^(\d*)$/ ) {
}
}
+my $indirect = ($cgi->param('indirect') eq 'Y' ? 1 : 0);
+
my (@items, @labels, @colors, @params, @links);
my $hue = 0;
-my $hue_increment = 125;
+my $hue_increment = 75;
my @signup_colors;
foreach my $referral (@referral) {
+ my %params = ('refnum' => $referral->refnum) unless $all_referral;
+
push @items, 'signups';
push @labels, ( $all_referral ? 'Signups' : $referral->referral );
- push @params, ( $all_referral ? [] : [ 'refnum' => $referral->refnum ] );
+ push @params, [ %params ];
push @links, $link . ($all_referral ? '' : "refnum=".$referral->refnum.';');
- if ( !@signup_colors ) {
- @signup_colors = Color::Scheme->new
- ->from_hue($hue)
- ->scheme('analogic')
- ->colors;
- $hue += $hue_increment;
+ # rotate hue for each referral type
+ @signup_colors = Color::Scheme->new->from_hue($hue)->colors;
+ $hue += $hue_increment;
+ push @colors, $signup_colors[0];
+ if ( $indirect ) {
+ push @items, 'signups';
+ push @labels, $all_referral ?
+ 'Referrals' :
+ $referral->referral . ' referrals';
+ push @params, [ %params, 'indirect' => 1 ];
+ push @links, '';
+ push @colors, $signup_colors[1];
}
- push @colors, shift @signup_colors;
}
</%init>
diff --git a/httemplate/graph/elements/monthly.html b/httemplate/graph/elements/monthly.html
index 072798c2a..839a3873e 100644
--- a/httemplate/graph/elements/monthly.html
+++ b/httemplate/graph/elements/monthly.html
@@ -35,6 +35,7 @@ Example:
#optional
'agentnum' => $agentnum,
+ 'refnum' => $refnum,
'nototal' => 1,
'graph_type' => 'LinesPoints',
'remove_empty' => 1,
@@ -92,10 +93,7 @@ $opt{'start_year'} ||= $cgi->param('start_year'); # || 1899+$curyear;
$opt{'end_month'} ||= $cgi->param('end_month'); # || $curmon+1;
$opt{'end_year'} ||= $cgi->param('end_year'); # || 1900+$curyear;
-#find end of projection
-$opt{'project_month'} ||= $cgi->param('project_month') || 0;
-$opt{'project_year'} ||= $cgi->param('project_year') || 0;
-# setting these to zero prevents projection on reports that don't support it
+$opt{'projection'} ||= $cgi->param('projection') ? 1 : 0;
if ( $opt{'daily'} ) { # daily granularity
$opt{'start_day'} ||= $cgi->param('start_day');
@@ -118,10 +116,9 @@ my %reportopts = (
'end_day' => $opt{'end_day'},
'end_month' => $opt{'end_month'},
'end_year' => $opt{'end_year'},
- 'project_day' => $opt{'project_day'},
- 'project_month' => $opt{'project_month'},
- 'project_year' => $opt{'project_year'},
+ 'projection' => $opt{'projection'},
'agentnum' => $opt{'agentnum'},
+ 'refnum' => $opt{'refnum'},
'remove_empty' => $opt{'remove_empty'},
'doublemonths' => $opt{'doublemonths'},
);
diff --git a/httemplate/graph/money_time.cgi b/httemplate/graph/money_time.cgi
index cde71be76..166735fc6 100644
--- a/httemplate/graph/money_time.cgi
+++ b/httemplate/graph/money_time.cgi
@@ -1,5 +1,5 @@
<% include('elements/monthly.html',
- 'title' => $agentname.
+ 'title' => $agentname. $referralname.
'Sales, Credits and Receipts Summary',
'items' => \@items,
'labels' => \%label,
@@ -7,6 +7,7 @@
'colors' => \%color,
'links' => \%link,
'agentnum' => $agentnum,
+ 'refnum' => $refnum,
'nototal' => scalar($cgi->param('12mo')),
)
%>
@@ -22,9 +23,17 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
$agent = qsearchs('agent', { 'agentnum' => $agentnum } );
die "agentnum $agentnum not found!" unless $agent;
}
-
my $agentname = $agent ? $agent->agent.' ' : '';
+my( $refnum, $part_referral ) = ('', '');
+if ( $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ $refnum = $1;
+ $part_referral = qsearchs('part_referral', { 'refnum' => $refnum } );
+ die "refnum $refnum not found!" unless $part_referral;
+}
+my $referralname = $part_referral ? $part_referral->referral.' ' : '';
+
+
my @items = qw( invoiced netsales
credits netcredits
payments receipts
@@ -83,15 +92,17 @@ my %color = (
$color{$_.'_12mo'} = $color{$_}
foreach keys %color;
+my $ar = "agentnum=$agentnum;refnum=$refnum";
+
my %link = (
- 'invoiced' => "${p}search/cust_bill.html?agentnum=$agentnum;",
- 'netsales' => "${p}search/cust_bill.html?agentnum=$agentnum;net=1;",
- 'credits' => "${p}search/cust_credit.html?agentnum=$agentnum;",
- 'netcredits' => "${p}search/cust_credit_bill.html?agentnum=$agentnum;",
- 'payments' => "${p}search/cust_pay.html?magic=_date;agentnum=$agentnum;",
- 'receipts' => "${p}search/cust_bill_pay.html?agentnum=$agentnum;",
- 'refunds' => "${p}search/cust_refund.html?magic=_date;agentnum=$agentnum;",
- 'netrefunds' => "${p}search/cust_credit_refund.html?agentnum=$agentnum;",
+ 'invoiced' => "${p}search/cust_bill.html?$ar;",
+ 'netsales' => "${p}search/cust_bill.html?$ar;net=1;",
+ 'credits' => "${p}search/cust_credit.html?$ar;",
+ 'netcredits' => "${p}search/cust_credit_bill.html?$ar;",
+ 'payments' => "${p}search/cust_pay.html?magic=_date;$ar;",
+ 'receipts' => "${p}search/cust_bill_pay.html?$ar;",
+ 'refunds' => "${p}search/cust_refund.html?magic=_date;$ar;",
+ 'netrefunds' => "${p}search/cust_credit_refund.html?$ar;",
);
# XXX link 12mo?
diff --git a/httemplate/graph/report_cust_bill_pkg.html b/httemplate/graph/report_cust_bill_pkg.html
index f2c486cf4..4cedcef17 100644
--- a/httemplate/graph/report_cust_bill_pkg.html
+++ b/httemplate/graph/report_cust_bill_pkg.html
@@ -7,14 +7,20 @@
<% include('/elements/tr-select-from_to.html' ) %>
<TR>
- <TD ALIGN="right">Project to:</TD>
- <TD><& /elements/select-month_year.html,
- prefix => 'project',
- show_month_abbr => 1 &></TD>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="projection" VALUE="1"></TD>
+ <TD>Show projected data for future months</TD>
</TR>
<% include('/elements/tr-select-agent.html',
- 'label' => 'For agent: ',
+ 'label' => 'Agent ',
+ 'disable_empty' => 0,
+ 'pre_options' => [ 'all' => 'all (aggregate)' ],
+ 'empty_label' => 'all (breakdown)',
+ )
+%>
+
+<% include('/elements/tr-select-part_referral.html',
+ 'label' => 'Advertising source ',
'disable_empty' => 0,
'pre_options' => [ 'all' => 'all (aggregate)' ],
'empty_label' => 'all (breakdown)',
diff --git a/httemplate/graph/report_cust_signup.html b/httemplate/graph/report_cust_signup.html
index 9d3f5006b..12dec8e6a 100644
--- a/httemplate/graph/report_cust_signup.html
+++ b/httemplate/graph/report_cust_signup.html
@@ -22,6 +22,12 @@
)
%>
+<& /elements/tr-td-label.html, label => 'Show customer referrals' &>
+<TD>
+ <INPUT TYPE="checkbox" NAME="indirect" VALUE="Y">
+</TD>
+</TR>
+
</TABLE>
<BR><INPUT TYPE="submit" VALUE="Display">
diff --git a/httemplate/graph/report_money_time.html b/httemplate/graph/report_money_time.html
index b85bb6552..97876c996 100644
--- a/httemplate/graph/report_money_time.html
+++ b/httemplate/graph/report_money_time.html
@@ -24,6 +24,13 @@
)
%>
+<% include('/elements/tr-select-part_referral.html',
+ 'label' => 'Advertising source ',
+ 'disable_empty' => 0,
+ 'empty_label' => 'all',
+ )
+%>
+
<TR>
<TD ALIGN="right"><INPUT TYPE="checkbox" NAME="12mo" VALUE="1"></TD>
<TD>Show 12 month totals instead of monthly values</TD>
diff --git a/httemplate/loginout/logout.html b/httemplate/loginout/logout.html
index 40371d596..d8e1c634a 100644
--- a/httemplate/loginout/logout.html
+++ b/httemplate/loginout/logout.html
@@ -8,11 +8,11 @@
<BODY>
<BR><BR>
<CENTER>
- You have logged out
+ You have logged out.
</CENTER>
<BR><BR>
<CENTER>
- Return to <a href="..">freeside</a>
+ You can <a href="..">log in</a> again.
</CENTER>
</BODY>
</HTML>
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
index 11fdeee61..887b92489 100644
--- a/httemplate/misc/batch-cust_pay.html
+++ b/httemplate/misc/batch-cust_pay.html
@@ -1,6 +1,9 @@
-<% include('/elements/header.html', 'Quick payment entry') %>
+<& /elements/header.html, {
+ title => 'Quick payment entry',
+ etc => 'onload="preload()"'
+} &>
-<% include('/elements/error.html') %>
+<& /elements/error.html &>
<SCRIPT TYPE="text/javascript">
function warnUnload() {
@@ -14,6 +17,21 @@ function warnUnload() {
}
window.onbeforeunload = warnUnload;
+function add_row_callback(rownum, prefix) {
+ document.getElementById('enable_app'+rownum).disabled = true;
+}
+
+function custnum_update_callback(rownum, prefix) {
+ var custnum = document.getElementById('custnum'+rownum).value;
+ document.getElementById('enable_app'+rownum).disabled = (
+ custnum == 0 ||
+ num_open_invoices[rownum] < 2
+ );
+% if ( $use_discounts ) {
+ select_discount_term(rownum, prefix);
+% }
+}
+
function select_discount_term(row, prefix) {
var custnum_obj = document.getElementById('custnum'+prefix+row);
var select_obj = document.getElementById('discount_term'+prefix+row);
@@ -46,6 +64,265 @@ function select_discount_term(row, prefix) {
discount_terms(custnum_obj.value, select_discount_term_update);
}
+
+var invoices_for_row = new Object;
+
+function update_invoices(rownum, invoices) {
+ invoices_for_row[rownum] = new Object;
+ // only called before create_application_row
+ for ( var i=0; i<invoices.length; i++ ) {
+ invoices_for_row[rownum][ invoices[i].invnum ] = invoices[i];
+ }
+}
+
+function toggle_application_row(ev, next) {
+ if (!next) next = function(){}; //optional continuation
+ var rownum = this.getAttribute('rownum');
+ if ( this.checked ) {
+ var custnum = document.getElementById('custnum'+rownum).value;
+ if (!custnum) return;
+ lock_payment_row(rownum, true);
+ custnum_search_open( custnum,
+ function(returned) {
+ update_invoices(rownum, JSON.parse(returned));
+ create_application_row(rownum, 0);
+ next.call(this, rownum);
+ }
+ );
+ }
+}
+
+function lock_payment_row(rownum, flag) {
+% foreach (qw(invnum custnum customer)) {
+ obj = document.getElementById('<% $_ %>'+rownum);
+ obj.readOnly = flag;
+% }
+ document.getElementById('enable_app'+rownum).disabled = flag;
+}
+
+function delete_application_row() {
+ var rownum = this.getAttribute('rownum');
+ var appnum = this.getAttribute('appnum');
+ var tr_app = document.getElementById('row'+rownum+'.'+appnum);
+ var select_invnum = document.getElementById('invnum'+rownum+'.'+appnum);
+ if ( select_invnum.value ) {
+ invoices_for_row[rownum][ select_invnum.value ] = select_invnum.curr_invoice;
+ }
+
+ tr_app.parentNode.removeChild(tr_app);
+ if ( appnum > 0 ) {
+ document.getElementById('delete'+rownum+'.'+(appnum-1)).style.display = '';
+ }
+ else {
+ lock_payment_row(rownum, false);
+ document.getElementById('enable_app'+rownum).checked = false;
+ }
+}
+
+function amount_unapplied(rownum) {
+ var appnum = 0;
+ var total = 0;
+ var payment_amount = parseFloat(document.getElementById('paid'+rownum).value)
+ || 0;
+ while (true) {
+ var input_amount = document.getElementById('amount'+rownum+'.'+appnum);
+ if ( input_amount ) {
+ total += parseFloat(input_amount.value || 0);
+ appnum++;
+ }
+ else {
+ return payment_amount - total;
+ }
+ }
+}
+
+var change_app_amount;
+
+function choose_app_invnum() {
+ var rownum = this.getAttribute('rownum');
+ var appnum = this.getAttribute('appnum');
+ var last_invoice = this.curr_invoice;
+ if ( last_invoice ) {
+ invoices_for_row[rownum][ last_invoice['invnum'] ] = last_invoice;
+ }
+
+ if ( this.value ) {
+ var this_invoice = invoices_for_row[rownum][this.value];
+ this.curr_invoice = invoices_for_row[rownum][this.value];
+ var span_owed = document.getElementById('owed'+rownum+'.'+appnum);
+ span_owed.innerHTML = this_invoice['owed'] + '&nbsp;';
+ delete invoices_for_row[rownum][this.value];
+
+ var input_amount = document.getElementById('amount'+rownum+'.'+appnum);
+ if ( input_amount.value == '' ) {
+ input_amount.value =
+ Math.max(
+ 0, Math.min( amount_unapplied(rownum), this_invoice['owed'])
+ ).toFixed(2);
+ // trigger onchange
+ change_app_amount.call(input_amount);
+ }
+ }
+}
+
+function focus_app_invnum() {
+% # invoice numbers just display as invoice numbers
+ var rownum = this.getAttribute('rownum');
+ var add_opt = function(obj, value) {
+ var o = document.createElement('OPTION');
+ o.text = value;
+ o.value = value;
+ obj.add(o);
+ }
+ this.options.length = 0;
+ var this_invoice = this.curr_invoice;
+ if ( this_invoice ) {
+ add_opt(this, this_invoice.invnum);
+ } else {
+ add_opt(this, '');
+ }
+ for ( var x in invoices_for_row[rownum] ) {
+ add_opt(this, invoices_for_row[rownum][x].invnum);
+ }
+}
+
+function change_app_amount() {
+ var rownum = this.getAttribute('rownum');
+ var appnum = this.getAttribute('appnum');
+%# maybe some kind of warning if amount_unapplied < 0?
+%# only spawn a new application row if there are open invoices left,
+%# and this is the highest-numbered application row for the customer,
+%# and the sum of the applied amounts is < the amount of the payment,
+ if ( Object.keys(invoices_for_row[rownum]).length > 0
+ && !document.getElementById( 'row'+rownum+'.'+(parseInt(appnum) + 1) )
+ && amount_unapplied(rownum) > 0 ) {
+
+ create_application_row(rownum, parseInt(appnum) + 1);
+
+ }
+}
+
+function create_application_row(rownum, appnum) {
+ var payment_row = document.getElementById('row'+rownum);
+ var tr_app = document.createElement('TR');
+ tr_app.setAttribute('rownum', rownum);
+ tr_app.setAttribute('appnum', appnum);
+ tr_app.setAttribute('id', 'row'+rownum+'.'+appnum);
+
+ var td_invnum = document.createElement('TD');
+ td_invnum.setAttribute('colspan', 4);
+ td_invnum.style.textAlign = 'right';
+ td_invnum.appendChild(
+ document.createTextNode('<% mt('Apply to Invoice ') %>')
+ );
+ var select_invnum = document.createElement('SELECT');
+ select_invnum.setAttribute('rownum', rownum);
+ select_invnum.setAttribute('appnum', appnum);
+ select_invnum.setAttribute('id', 'invnum'+rownum+'.'+appnum);
+ select_invnum.setAttribute('name', 'invnum'+rownum+'.'+appnum);
+ select_invnum.style.textAlign = 'right';
+ select_invnum.style.width = '50px';
+ select_invnum.onchange = choose_app_invnum;
+ select_invnum.onfocus = focus_app_invnum;
+
+ td_invnum.appendChild(select_invnum);
+ tr_app.appendChild(td_invnum);
+
+ var td_owed = document.createElement('TD');
+ td_owed.style.textAlign= 'right';
+ var span_owed = document.createElement('SPAN');
+ span_owed.setAttribute('rownum', rownum);
+ span_owed.setAttribute('appnum', appnum);
+ span_owed.setAttribute('id', 'owed'+rownum+'.'+appnum);
+ td_owed.appendChild(span_owed);
+ tr_app.appendChild(td_owed);
+
+ var td_amount = document.createElement('TD');
+ td_amount.style.textAlign = 'right';
+ var input_amount = document.createElement('INPUT');
+ input_amount.size = 6;
+ input_amount.setAttribute('rownum', rownum);
+ input_amount.setAttribute('appnum', appnum);
+ input_amount.setAttribute('name', 'amount'+rownum+'.'+appnum);
+ input_amount.setAttribute('id', 'amount'+rownum+'.'+appnum);
+ input_amount.style.textAlign = 'right';
+ input_amount.onchange = change_app_amount;
+ td_amount.appendChild(input_amount);
+ tr_app.appendChild(td_amount);
+
+ var td_delete = document.createElement('TD');
+ td_delete.setAttribute('colspan', <% scalar(@fields)-2 %>);
+ var button_delete = document.createElement('INPUT');
+ button_delete.setAttribute('rownum', rownum);
+ button_delete.setAttribute('appnum', appnum);
+ button_delete.setAttribute('id', 'delete'+rownum+'.'+appnum);
+ button_delete.setAttribute('type', 'button');
+ button_delete.setAttribute('value', 'X');
+ button_delete.onclick = delete_application_row;
+ button_delete.style.color = '#ff0000';
+ button_delete.style.fontWeight = 'bold';
+ button_delete.style.paddingLeft = '2px';
+ button_delete.style.paddingRight = '2px';
+ td_delete.appendChild(button_delete);
+ tr_app.appendChild(td_delete);
+
+ var td_error = document.createElement('TD');
+ var span_error = document.createElement('SPAN');
+ span_error.setAttribute('rownum', rownum);
+ span_error.setAttribute('appnum', appnum);
+ span_error.setAttribute('id', 'error'+rownum+'.'+appnum);
+ span_error.style.color = '#ff0000';
+ td_error.appendChild(span_error);
+ tr_app.appendChild(td_error);
+
+ if ( appnum > 0 ) {
+ //remove delete button on the previous row
+ document.getElementById('delete'+rownum+'.'+(appnum-1)).style.display = 'none';
+ }
+ rownum++;
+ var next_row = document.getElementById('row'+rownum); // always exists
+ payment_row.parentNode.insertBefore(tr_app, next_row);
+
+}
+
+%# for error handling--ugly, but the alternative is translating the whole
+%# process of creating rows into Mason
+var row_array = <% encode_json(\@rows) %>;
+function preload() {
+ var rownum;
+ var appnum;
+ for (rownum=0; rownum < row_array.length; rownum++) {
+ if ( row_array[rownum].length ) {
+ var enable = document.getElementById('enable_app'+rownum);
+ enable.checked = true;
+ var preload_row = function(r) {//continuation from toggle_application_row
+ for (appnum=0; appnum < row_array[r].length; appnum++) {
+ this_app = row_array[r][appnum];
+ var x = r + '.' + appnum;
+ //set invnum
+ var select_invnum = document.getElementById('invnum'+x);
+ focus_app_invnum.call(select_invnum);
+ for (i=0; i<select_invnum.options.length; i++) {
+ if (select_invnum.options[i].value == this_app.invnum) {
+ select_invnum.selectedIndex = i;
+ }
+ }
+ choose_app_invnum.call(select_invnum);
+ //set amount
+ var input_amount = document.getElementById('amount'+x);
+ input_amount.value = this_app.amount;
+
+ //set error
+ var span_error = document.getElementById('error'+x);
+ span_error.innerHTML = this_app.error;
+ change_app_amount.call(input_amount); //creates next row
+ } //for appnum
+ }; //preload_row function
+ toggle_application_row.call(enable, null, preload_row);
+ } // if row_array[rownum].length
+ } //for rownum
+}
+
</SCRIPT>
<% include('/elements/xmlhttp.html',
@@ -57,21 +334,26 @@ function select_discount_term(row, prefix) {
<FORM ACTION="process/batch-cust_pay.cgi" NAME="OneTrueForm" METHOD="POST" onsubmit="document.OneTrueForm.btnsubmit.disabled=true;window.onbeforeunload = null;">
<!-- <B>Batch</B> <INPUT TYPE="text" NAME="paybatch"><BR><BR> -->
+<& /elements/xmlhttp.html,
+ url => $p.'misc/xmlhttp-cust_bill-search.html',
+ subs => ['custnum_search_open']
+&>
-<% include( "/elements/customer-table.html",
- name_singular => 'payment',
- 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,
- )
-%>
+<& /elements/customer-table.html,
+ name_singular => 'payment',
+ header => \@header,
+ fields => \@fields,
+ type => \@types,
+ align => \@align,
+ size => \@sizes,
+ color => \@colors,
+ param => \%param,
+ footer => \@footer,
+ footer_align => \@footer_align,
+ onchange => \@onchange,
+ custnum_update_callback => 'custnum_update_callback',
+ add_row_callback => 'add_row_callback',
+&>
<BR>
<INPUT TYPE="button" VALUE="Post payment batch" name="btnsubmit" onclick="window.onbeforeunload = null; document.OneTrueForm.submit(); this.disabled = true;">
@@ -96,18 +378,20 @@ die "access denied"
my $conf = new FS::Conf;
my $money_char = $conf->config('money_char') || '$';
-my @header = ( '', 'Amount', 'Check #' );
-my @fields = ( sub { "$money_char" }, 'paid', 'payinfo' );
-my @types = ( 'immutable', '', '' );
-my @align = ( 'c', 'r', 'r' );
-my @sizes = ( 0, 8, 10 );
-my @colors = ( '', '', '' );
+my @header = ( 'Amount', 'Check #' );
+my @fields = ( 'paid', 'payinfo' );
+my @types = ( '', '' );
+my @align = ( 'r', 'r' );
+my @sizes = ( 8, 10 );
+my @colors = ( '', '' );
my %param = ();
-my @footer = ( "$money_char", '_TOTAL', '' );
-my @footer_align = ( 'c', 'r', 'r' );
-my $custnum_update_callback = '';
+my @footer = ( '_TOTAL', '' );
+my @footer_align = ( 'r', 'r' );
+my @onchange = ( '', '' );;
+my $use_discounts = '';
if ( FS::Record->scalar_sql('SELECT COUNT(*) FROM part_pkg_discount') ) {
+ #push @header, 'Discount';
push @header, '';
push @fields, 'discount_term';
push @types, 'immutable';
@@ -116,9 +400,21 @@ if ( FS::Record->scalar_sql('SELECT COUNT(*) FROM part_pkg_discount') ) {
push @colors, '';
push @footer, '';
push @footer_align, '';
- $custnum_update_callback = 'select_discount_term';
+ push @onchange, '';
+ $use_discounts = 'Y';
}
+push @header, 'Allocate';
+push @fields, 'enable_app';
+push @types, 'checkbox';
+push @align, 'c';
+push @sizes, '0';
+push @colors, '';
+push @footer, '';
+push @footer_align, '';
+push @onchange, 'toggle_application_row';
+
+#push @header, 'Error';
push @header, '';
push @fields, 'error';
push @types, 'immutable';
@@ -127,7 +423,34 @@ push @sizes, '0';
push @colors, '#ff0000';
push @footer, '';
push @footer_align, '';
+push @onchange, '';
$m->comp('/elements/handle_uri_query');
+# set up for preloading
+my @rows;
+my @row_errors;
+if ( $cgi->param('error') ) {
+ my $param = $cgi->Vars;
+ my $enum = 0; #errors numbered separately
+ for( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+ $rows[$row] = [];
+ $row_errors[$row] = $param->{"error$enum"};
+ $enum++;
+ for( my $app = 0; exists($param->{"invnum$row.$app"}); $app++ ) {
+ next if !$param->{"invnum$row.$app"};
+ my %this_app = map { $_ => ($param->{$_.$row.'.'.$app} || '') }
+ qw( invnum amount );
+ $this_app{'error'} = $param->{"error$enum"} || '';
+ $param->{"error$enum"} = ''; # don't pass this error through
+ $rows[$row][$app] = \%this_app;
+ $enum++;
+ }
+ }
+ for( my $row = 0; $row < @row_errors; $row++ ) {
+ $param->{"error$row"} = $row_errors[$row];
+ }
+}
+#warn Dumper {rows => \@rows, row_errors => \@row_errors };
+
</%init>
diff --git a/httemplate/misc/cancel_pkg.html b/httemplate/misc/cancel_pkg.html
index 42cc56dfe..f9a46a898 100755
--- a/httemplate/misc/cancel_pkg.html
+++ b/httemplate/misc/cancel_pkg.html
@@ -13,22 +13,53 @@
% my $date_init = 0;
% if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume') {
% $submit =~ /^(\w*)\s/;
-<& /elements/tr-input-date-field.html, {
- 'name' => 'date',
- 'value' => $date,
- 'label' => mt("$1 package on"),
- 'format' => $date_format,
-} &>
+ <& /elements/tr-input-date-field.html, {
+ 'name' => 'date',
+ 'value' => $date,
+ 'label' => mt("$1 package on"),
+ 'format' => $date_format,
+ } &>
% $date_init = 1;
% }
-% unless ( $method eq 'resume' ) { #the only one that doesn't need a reason
-<& /elements/tr-select-reason.html,
- 'field' => 'reasonnum',
- 'reason_class' => $class,
- 'curr_value' => $reasonnum,
- 'control_button' => "document.getElementById('confirm_cancel_pkg_button')",
-&>
+% if ($method eq 'uncancel' ) {
+%
+% #XXX customer also requested setup
+% # setup: what usefulness is changing or blanking this? re-charge setup fee?
+% # an option that says that would be better if that's what we want to do
+
+% # last_bill: isn't this informational? what good would editing it do?
+% # something about invoice display?
+ <& /elements/tr-input-date-field.html, {
+ 'name' => 'last_bill',
+ 'value' => ( $cgi->param('last_bill') || $cust_pkg->get('last_bill') ),
+ 'label' => mt("Last bill date"),
+ 'format' => $date_format,
+ } &>
+
+ <& /elements/tr-input-date-field.html, {
+ 'name' => 'bill',
+ 'value' => ( $cgi->param('bill') || $cust_pkg->get('bill') ),
+ 'label' => mt("Next bill date"),
+ 'format' => $date_format,
+ } &>
+
+ <& /elements/tr-checkbox.html,
+ 'label' => mt("Uncancel even if a service can't be re-provisioned"),
+ 'field' => 'svc_not_fatal',
+ 'value' => 'Y',
+ &>
+
+% $date_init = 1;
+% }
+
+% unless ( $method eq 'resume' || $method eq 'uncancel' ) {
+ <& /elements/tr-select-reason.html,
+ field => 'reasonnum',
+ reason_class => $class,
+ curr_value => $reasonnum,
+ control_button => "document.getElementById('confirm_cancel_pkg_button')",
+ &>
% }
% if ( $method eq 'adjourn' || $method eq 'suspend' ) {
@@ -49,26 +80,27 @@
% ? str2time($cgi->param('resume_date'))
% : $cust_pkg->get('resume');
-<& /elements/tr-input-date-field.html, {
- 'name' => 'resume_date',
- 'value' => $resume_date,
- 'label' => mt('Unsuspend on'),
- 'format' => $date_format,
- 'noinit' => $date_init,
-} &>
+ <& /elements/tr-input-date-field.html, {
+ 'name' => 'resume_date',
+ 'value' => $resume_date,
+ 'label' => mt('Unsuspend on'),
+ 'format' => $date_format,
+ 'noinit' => $date_init,
+ } &>
% }
</TABLE>
<BR>
<INPUT TYPE="submit" NAME="submit" ID="confirm_cancel_pkg_button"
VALUE="<% mt($submit) |h %>"
- <% $method ne 'resume' ? 'DISABLED' : '' %>>
+ <% $method !~ /^(resume|uncancel)$/ ? 'DISABLED' : '' %>>
</FORM>
</BODY>
</HTML>
<%init>
+use Date::Parse qw(str2time);
my $conf = new FS::Conf;
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
@@ -111,6 +143,10 @@ if ($method eq 'cancel') {
$class = '';
$submit = 'Unsuspend Later';
$right = 'Unsuspend customer package'; #later?
+} elsif ( $method eq 'uncancel') {
+ $class = '';
+ $submit = 'Un-Cancel';
+ $right = 'Un-cancel customer package'; #later?
} else {
die 'illegal query (unknown method param)';
}
@@ -119,6 +155,7 @@ my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied" unless $curuser->access_right($right);
my $title = ucfirst($method) . ' Package';
+$title =~ s/Uncancel/Un-cancel/;
my $cust_pkg = qsearchs('cust_pkg', {'pkgnum' => $pkgnum})
or die "Unknown pkgnum: $pkgnum";
diff --git a/httemplate/misc/cust-part_pkg.cgi b/httemplate/misc/cust-part_pkg.cgi
index dcd033ff2..a277ba407 100644
--- a/httemplate/misc/cust-part_pkg.cgi
+++ b/httemplate/misc/cust-part_pkg.cgi
@@ -1,11 +1,19 @@
<% objToJson( \@return ) %>
<%init>
-my( $custnum, $classnum ) = $cgi->param('arg');
+my( $custnum, $prospectnum, $classnum ) = $cgi->param('arg');
-#XXX i guess i should be agent-virtualized. cause "packages a customer can
-#order" is such a huge deal
-my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } );
+
+my $agent;
+if ( $custnum ) {
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or die 'unknown custnum';
+ $agent = $cust_main->agent;
+} else {
+ my $prospect_main = qsearchs('prospect_main', {'prospectnum'=>$prospectnum} )
+ or die 'unknown prospectnum';
+ $agent = $prospect_main->agent;
+}
my %hash = ( 'disabled' => '' );
if ( $classnum > 0 ) {
@@ -19,7 +27,7 @@ my @part_pkg = qsearch({
'hashref' => \%hash,
'extra_sql' =>
' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql( 'null'=>1 ).
- ' AND '. FS::part_pkg->agent_pkgs_sql( $cust_main->agent ),
+ ' AND '. FS::part_pkg->agent_pkgs_sql( $agent ),
'order_by' => 'ORDER BY pkg',
});
diff --git a/httemplate/misc/cust_main-cancel.cgi b/httemplate/misc/cust_main-cancel.cgi
index 002c0011a..2ae9f1021 100755
--- a/httemplate/misc/cust_main-cancel.cgi
+++ b/httemplate/misc/cust_main-cancel.cgi
@@ -1,4 +1,4 @@
-<& /elements/header.html, mt("Customer cancelled") &>
+<& /elements/header-popup.html, mt("Customer cancelled") &>
<SCRIPT TYPE="text/javascript">
window.top.location.reload();
</SCRIPT>
diff --git a/httemplate/misc/cust_main-import.cgi b/httemplate/misc/cust_main-import.cgi
index 664651069..74f9b4c89 100644
--- a/httemplate/misc/cust_main-import.cgi
+++ b/httemplate/misc/cust_main-import.cgi
@@ -35,6 +35,7 @@ Import a file containing customer records.
<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
+ <OPTION VALUE="birthdates-acct_phone_hardware">Birthdates and account, phone and hardware services
</SELECT>
</TD>
</TR>
@@ -106,6 +107,9 @@ Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets.
<b>External service and phone 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, countrycode, phonenum, sip_password, pin</i>
<BR><BR>
+<b>Birthdates and account, phone and hardware services</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, birthdate, spouse_birthdate, payinfo, paycvv, paydate, invoicing_list, pkgpart, next_bill_date, username, _password, countrycode, phonenum, sip_password, pin, typenum, ip_addr, hw_addr, serial</i>
+<BR><BR>
+
<%$req%> Required fields
<BR><BR>
diff --git a/httemplate/misc/cust_main-suspend.cgi b/httemplate/misc/cust_main-suspend.cgi
new file mode 100755
index 000000000..61851364e
--- /dev/null
+++ b/httemplate/misc/cust_main-suspend.cgi
@@ -0,0 +1,75 @@
+<& /elements/header-popup.html, mt("Customer suspended") &>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+</HTML>
+<%init>
+
+#false laziness w/cust_main-cancel.cgi
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Suspend customer');
+
+my $custnum;
+my $adjourn = '';
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $custnum = $1;
+ $adjourn = $cgi->param('adjourn');
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ || die "Illegal custnum";
+ $custnum = $1;
+}
+
+#false laziness w/process/cancel_pkg.html
+
+#untaint reasonnum
+my $reasonnum = $cgi->param('reasonnum');
+$reasonnum =~ /^(-?\d+)$/ || die "Illegal reasonnum";
+$reasonnum = $1;
+
+if ($reasonnum == -1) {
+ $reasonnum = {
+ 'typenum' => scalar( $cgi->param('newreasonnumT') ),
+ 'reason' => scalar( $cgi->param('newreasonnum' ) ),
+ };
+}
+
+#eslaf
+
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+} );
+
+my @errors;
+if($cgi->param('now_or_later')) {
+ $adjourn = parse_datetime($adjourn);
+ if($adjourn) {
+ #warn "setting adjourn dates on custnum#$custnum\n";
+ my @pkgs = $cust_main->unsuspended_pkgs;
+ @errors = grep {$_} map { $_->suspend(
+ 'reason' => $reasonnum,
+ 'date' => $adjourn,
+ ) } @pkgs;
+ }
+ else {
+ @errors = ("error parsing adjourn date: ".$cgi->param('adjourn'));
+ }
+}
+else {
+ warn "suspending $cust_main";
+ @errors = $cust_main->suspend(
+ 'reason' => $reasonnum,
+ );
+}
+my $error = join(' / ', @errors) if scalar(@errors);
+
+if ( $error ) {
+ $cgi->param('error', $error);
+ print $cgi->redirect(popurl(1). "suspend_cust.html?". $cgi->query_string );
+}
+
+</%init>
diff --git a/httemplate/misc/cust_main-unsuspend.cgi b/httemplate/misc/cust_main-unsuspend.cgi
new file mode 100755
index 000000000..eb4a2c8f8
--- /dev/null
+++ b/httemplate/misc/cust_main-unsuspend.cgi
@@ -0,0 +1,56 @@
+<& /elements/header-popup.html, mt("Customer unsuspended") &>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY>
+</HTML>
+<%init>
+
+#false laziness w/cust_main-cancel.cgi
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Unsuspend customer');
+
+my $custnum;
+my $resume = '';
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $custnum = $1;
+ $resume = $cgi->param('resume');
+} else {
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ || die "Illegal custnum";
+ $custnum = $1;
+}
+
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+} );
+
+my @errors;
+if($cgi->param('now_or_later')) {
+ $resume = parse_datetime($resume);
+ if($resume) {
+ #warn "setting resume dates on custnum#$custnum\n";
+ my @pkgs = $cust_main->suspended_pkgs;
+ @errors = grep {$_} map { $_->unsuspend(
+ 'date' => $resume,
+ ) } @pkgs;
+ }
+ else {
+ @errors = ("error parsing adjourn date: ".$cgi->param('adjourn'));
+ }
+}
+else {
+ warn "unsuspending $cust_main";
+ @errors = $cust_main->unsuspend;
+}
+my $error = join(' / ', @errors) if scalar(@errors);
+
+if ( $error ) {
+ $cgi->param('error', $error);
+ print $cgi->redirect(popurl(1). "unsuspend_cust.html?". $cgi->query_string );
+}
+
+</%init>
diff --git a/httemplate/misc/delete-ftp_target.html b/httemplate/misc/delete-ftp_target.html
new file mode 100644
index 000000000..c8bd29701
--- /dev/null
+++ b/httemplate/misc/delete-ftp_target.html
@@ -0,0 +1,18 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect("${p}browse/ftp_target.html") %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal targetnum";
+my $targetnum = $1;
+
+my $target = qsearchs('ftp_target',{'targetnum'=>$targetnum});
+my $error = $target->delete;
+
+</%init>
diff --git a/httemplate/misc/download-batch.cgi b/httemplate/misc/download-batch.cgi
index 23deba712..f3a31eb3b 100644
--- a/httemplate/misc/download-batch.cgi
+++ b/httemplate/misc/download-batch.cgi
@@ -1,4 +1,4 @@
-<% $pay_batch->export_batch($format) %><%init>
+<% $pay_batch->export_batch(%opt) %><%init>
#http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes
http_header('Content-Type' => 'text/plain' ); # not necessarily correct...
@@ -10,9 +10,14 @@ if ( $cgi->param('batchnum') =~ /^(\d+)$/ ) {
die "No batch number (bad URL) \n";
}
-my $format;
-if ( $cgi->param('format') =~ /^([\w\- ]+)$/ ) {
- $format = $1;
+my %opt;
+if ( $cgi->param('gatewaynum') =~ /^(\d+)$/ ) {
+ my $gateway = FS::payment_gateway->by_key($1);
+ die "gatewaynum $1 not found" unless $gateway;
+ $opt{'gateway'} = $gateway;
+}
+elsif ( $cgi->param('format') =~ /^([\w\- ]+)$/ ) {
+ $opt{'format'} = $1;
}
my $pay_batch = qsearchs('pay_batch', { batchnum => $batchnum } );
diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html
index 2332f2028..7aa024a34 100644
--- a/httemplate/misc/order_pkg.html
+++ b/httemplate/misc/order_pkg.html
@@ -32,6 +32,15 @@
&>
% }
+% if ( $conf->exists('invoice-unitprice') ) {
+ <TR>
+ <TH ALIGN="right"><% mt('Quantity') |h %> </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="quantity" SIZE=4 VALUE="<% $quantity %>">
+ </TD>
+ </TR>
+% }
+
<TR>
<TH ALIGN="right"><% mt('Start date') |h %> </TD>
<TD COLSPAN=6>
@@ -163,6 +172,11 @@ if ( $cgi->param('lock_pkgpart') ) {
my $pkgpart = $part_pkg ? $part_pkg->pkgpart : scalar($cgi->param('pkgpart'));
+my $quantity = 1;
+if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) {
+ $quantity = $1;
+}
+
my $format = $date_format. ' %T %z (%Z)'; #false laziness w/REAL_cust_pkg.cgi?
my $start_date = '';
if( ! $conf->exists('order_pkg-no_start_date') ) {
diff --git a/httemplate/misc/process/batch-cust_pay.cgi b/httemplate/misc/process/batch-cust_pay.cgi
index aa371266c..1105af943 100644
--- a/httemplate/misc/process/batch-cust_pay.cgi
+++ b/httemplate/misc/process/batch-cust_pay.cgi
@@ -1,51 +1,69 @@
-% die "access denied"
-% unless $FS::CurrentUser::CurrentUser->access_right('Post payment batch');
-%
-% my $param = $cgi->Vars;
-%
-% #my $paybatch = $param->{'paybatch'};
-% my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
-%
-% my @cust_pay = ();
-% #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' => $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"};
-% #$row++;
-% }
-%
-% my @errors = FS::cust_pay->batch_insert(@cust_pay);
-% my $num_errors = scalar(grep $_, @errors);
-%
+<%init>
+my $DEBUG = 0;
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Post payment batch');
+
+my $param = $cgi->Vars;
+warn Dumper($param) if $DEBUG;
+
+#my $paybatch = $param->{'paybatch'};
+my $paybatch = time2str('webbatch-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+my @cust_pay = ();
+#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 ( length($custnum) and !$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
+
+ my $cust_pay = new FS::cust_pay {
+ '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"};
+ next if !$cust_pay;
+ #$row++;
+
+ # payment applications, if any
+ my @cust_bill_pay = ();
+ for ( my $app = 0; exists($param->{"invnum$row.$app"}); $app++ ) {
+ next if !$param->{"invnum$row.$app"};
+ push @cust_bill_pay, new FS::cust_bill_pay {
+ 'invnum' => $param->{"invnum$row.$app"},
+ 'amount' => $param->{"amount$row.$app"}
+ };
+ }
+ $cust_pay->set('apply_to', \@cust_bill_pay) if scalar(@cust_bill_pay) > 0;
+
+ push @cust_pay, $cust_pay;
+
+}
+
+my @errors = FS::cust_pay->batch_insert(@cust_pay);
+my $num_errors = scalar(grep $_, @errors);
+</%init>
% if ( $num_errors ) {
%
% $cgi->param('error', "$num_errors error". ($num_errors>1 ? 's' : '').
@@ -65,4 +83,3 @@
%
<% $cgi->redirect(popurl(3). "search/cust_pay.html?magic=paybatch;paybatch=$paybatch") %>
% }
-
diff --git a/httemplate/misc/process/cancel_pkg.html b/httemplate/misc/process/cancel_pkg.html
index 4f8e11b7f..79e489c70 100755
--- a/httemplate/misc/process/cancel_pkg.html
+++ b/httemplate/misc/process/cancel_pkg.html
@@ -6,19 +6,21 @@
</HTML>
<%once>
-my %past = ( 'cancel' => 'cancelled',
- 'expire' => 'expired',
- 'suspend' => 'suspended',
- 'adjourn' => 'adjourned',
- 'resume' => 'scheduled to resume',
+my %past = ( 'cancel' => 'cancelled',
+ 'expire' => 'expired',
+ 'suspend' => 'suspended',
+ 'adjourn' => 'adjourned',
+ 'resume' => 'scheduled to resume',
+ 'uncancel' => 'un-cancelled',
);
#i'm sure this is false laziness with somewhere, at least w/misc/cancel_pkg.html
-my %right = ( 'cancel' => 'Cancel customer package immediately',
- 'expire' => 'Cancel customer package later',
- 'suspend' => 'Suspend customer package',
- 'adjourn' => 'Suspend customer package later',
- 'resume' => 'Unsuspend customer package', #later?
+my %right = ( 'cancel' => 'Cancel customer package immediately',
+ 'expire' => 'Cancel customer package later',
+ 'suspend' => 'Suspend customer package',
+ 'adjourn' => 'Suspend customer package later',
+ 'resume' => 'Unsuspend customer package', #later?
+ 'uncancel' => 'Un-cancel customer package',
);
</%once>
@@ -26,7 +28,8 @@ my %right = ( 'cancel' => 'Cancel customer package immediately',
#untaint method
my $method = $cgi->param('method');
-$method =~ /^(cancel|expire|suspend|adjourn|resume)$/ or die "Illegal method";
+$method =~ /^(cancel|expire|suspend|adjourn|resume|uncancel)$/
+ or die "Illegal method";
$method = $1;
my $past_method = $past{$method};
@@ -39,7 +42,7 @@ $pkgnum =~ /^(\d+)$/ or die "Illegal pkgnum";
$pkgnum = $1;
my $date = time;
-if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume'){
+if ($method eq 'expire' || $method eq 'adjourn' || $method eq 'resume') {
#untaint date
$date = $cgi->param('date'); #huh?
parse_datetime($cgi->param('date')) =~ /^(\d+)$/ or die "Illegal date";
@@ -64,7 +67,7 @@ my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
#untaint reasonnum
my $reasonnum = $cgi->param('reasonnum');
-if ( $method ne 'unsuspend' ) { #i.e. 'resume'
+if ( $method !~ /^(unsuspend|uncancel)$/ ) {
$reasonnum =~ /^(-?\d+)$/ or die "Illegal reasonnum";
$reasonnum = $1;
@@ -76,9 +79,20 @@ if ( $method ne 'unsuspend' ) { #i.e. 'resume'
}
}
+#for uncancel
+my $last_bill =
+ $cgi->param('last_bill') ? parse_datetime($cgi->param('last_bill')) : '';
+my $bill =
+ $cgi->param('bill') ? parse_datetime($cgi->param('bill')) : '';
+
+my $svc_fatal = ( $cgi->param('svc_not_fatal') ne 'Y' );
+
my $error = $cust_pkg->$method( 'reason' => $reasonnum,
'date' => $date,
'resume_date' => $resume_date,
+ 'last_bill' => $last_bill,
+ 'bill' => $bill,
+ 'svc_fatal' => $svc_fatal,
'options' => $options,
);
diff --git a/httemplate/misc/suspend_cust.html b/httemplate/misc/suspend_cust.html
new file mode 100644
index 000000000..b41f36f8c
--- /dev/null
+++ b/httemplate/misc/suspend_cust.html
@@ -0,0 +1,79 @@
+<& /elements/header-popup.html, mt('Suspend customer') &>
+
+<& /elements/error.html &>
+
+<FORM NAME="cust_suspend_popup" ACTION="<% popurl(1) %>cust_main-suspend.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+ <P ALIGN="center"><B><% mt('Suspend this customer?') |h %></B>
+
+<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"><% mt('Suspend now') |h %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">
+ <INPUT TYPE="radio" NAME="now_or_later" VALUE="1" onclick="toggle(true)" />
+ </TD>
+ <TD ALIGN="left"><% mt('Suspend on date: ') |h %>
+ <& /elements/input-date-field.html, {
+ 'name' => 'adjourn',
+ 'value' => time,
+ } &>
+ </TD>
+</TR>
+</TABLE>
+<SCRIPT type="text/javascript">
+function toggle(val) {
+ document.getElementById("adjourn_text").disabled = !val;
+ document.getElementById("adjourn_button").style.visibility =
+ val ? 'visible' : 'hidden';
+}
+toggle(false);
+</SCRIPT>
+
+<TABLE BGCOLOR="#cccccc", BORDER="0" CELLSPACING="2"
+STYLE="margin-left:auto; margin-right:auto">
+<& /elements/tr-select-reason.html,
+ 'field' => 'reasonnum',
+ 'reason_class' => 'C',
+ 'cgi' => $cgi,
+ 'control_button' => "document.getElementById('confirm_suspend_cust_button')",
+&>
+
+</TABLE>
+
+<BR>
+<P ALIGN="CENTER">
+<INPUT TYPE="submit" NAME="submit" ID="confirm_suspend_cust_button" VALUE="<% mt('Suspend customer') |h %>" DISABLED>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<INPUT TYPE="BUTTON" VALUE="<% mt("Don't suspend") |h %>" onClick="parent.cClick();">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+#false laziness w/cancel_cust.html
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
+my $custnum = $1;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied" unless $curuser->access_right('Suspend 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/unsuspend_cust.html b/httemplate/misc/unsuspend_cust.html
new file mode 100644
index 000000000..600eb268a
--- /dev/null
+++ b/httemplate/misc/unsuspend_cust.html
@@ -0,0 +1,68 @@
+<& /elements/header-popup.html, mt('Unsuspend customer') &>
+
+<& /elements/error.html &>
+
+<FORM NAME="cust_unsuspend_popup" ACTION="<% popurl(1) %>cust_main-unsuspend.cgi" METHOD=POST>
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+ <P ALIGN="center"><B><% mt('Unsuspend this customer?') |h %></B>
+
+<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"><% mt('Unsuspend now') |h %></TD>
+</TR>
+<TR>
+ <TD ALIGN="right">
+ <INPUT TYPE="radio" NAME="now_or_later" VALUE="1" onclick="toggle(true)" />
+ </TD>
+ <TD ALIGN="left"><% mt('Unsuspend on date: ') |h %>
+ <& /elements/input-date-field.html, {
+ 'name' => 'resume',
+ 'value' => time,
+ } &>
+ </TD>
+</TR>
+</TABLE>
+<SCRIPT type="text/javascript">
+function toggle(val) {
+ document.getElementById("resume_text").disabled = !val;
+ document.getElementById("resume_button").style.visibility =
+ val ? 'visible' : 'hidden';
+}
+toggle(false);
+</SCRIPT>
+
+<BR>
+<P ALIGN="CENTER">
+<INPUT TYPE="submit" NAME="submit" ID="confirm_unsuspend_cust_button" VALUE="<% mt('Unsuspend customer') |h %>">
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+<INPUT TYPE="BUTTON" VALUE="<% mt("Don't unsuspend") |h %>" onClick="parent.cClick();">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+#false laziness w/cancel_cust.html
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
+my $custnum = $1;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied" unless $curuser->access_right('Unsuspend 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/xmlhttp-cust_bill-search.html b/httemplate/misc/xmlhttp-cust_bill-search.html
new file mode 100644
index 000000000..46f15d1ab
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_bill-search.html
@@ -0,0 +1,18 @@
+<% encode_json(\@return) %>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die 'access denied' unless $curuser->access_right('View invoices');
+my @return;
+if ( $cgi->param('sub') eq 'custnum_search_open' ) {
+ my $custnum = $cgi->param('arg');
+ #warn "searching invoices for $custnum\n";
+ my $cust_main = FS::cust_main->by_key($custnum);
+ @return = map {
+ +{ $_->hash,
+ 'owed' => $_->owed }
+ } $cust_main->open_cust_bill
+ if $curuser->agentnums_href->{ $cust_main->agentnum };
+}
+
+</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi
index 71e2da597..b524e69fc 100644
--- a/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi
+++ b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi
@@ -2,15 +2,18 @@
%
% 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 ( $custnum =~ /^\d+$/ ) {
+% 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 ];
+% if ($cust_main) {
+% $return = [ map [ $_, sprintf("%d months", $_) ],
+% $cust_main->discount_terms ];
+% }
% }
%
<% objToJson($return) %>
diff --git a/httemplate/misc/xmlhttp-cust_main-search.cgi b/httemplate/misc/xmlhttp-cust_main-search.cgi
index 68c5bf597..16f7cd2bc 100644
--- a/httemplate/misc/xmlhttp-cust_main-search.cgi
+++ b/httemplate/misc/xmlhttp-cust_main-search.cgi
@@ -1,9 +1,9 @@
% if ( $sub eq 'custnum_search' ) {
% my $custnum = $cgi->param('arg');
% my $return = [];
-% if ( $custnum =~ /^(\d+)$/ ) {
-% $return = findbycustnum($1,0);
-% $return = findbycustnum($1,1) if(!scalar(@$return));
+% if ( $custnum =~ /^(\d+)$/ ) { #should also handle
+% # cust_main-agent_custid-format') eq 'ww?d+'
+% $return = findbycustnum_or_agent_custid($1);
% }
<% objToJson($return) %>
% } elsif ( $sub eq 'smart_search' ) {
@@ -12,15 +12,27 @@
% my @cust_main = smart_search( 'search' => $string,
% 'no_fuzzy_on_exact' => 1, #pref?
% );
-% my $return = [ map [ $_->custnum, $_->name, $_->balance, $_->ucfirst_status, $_->statuscolor ], @cust_main ];
+% my $return = [ map [ $_->custnum,
+% $_->name,
+% $_->balance,
+% $_->ucfirst_status,
+% $_->statuscolor,
+% scalar($_->open_cust_bill)
+% ],
+% @cust_main
+% ];
%
<% objToJson($return) %>
% } elsif ( $sub eq 'invnum_search' ) {
%
% my $string = $cgi->param('arg');
-% my $inv = qsearchs('cust_bill', { 'invnum' => $string });
-% my $return = $inv ? findbycustnum($inv->custnum,0) : [];
+% if ( $string =~ /^(\d+)$/ ) {
+% my $inv = qsearchs('cust_bill', { 'invnum' => $1 });
+% my $return = $inv ? findbycustnum($inv->custnum) : [];
<% objToJson($return) %>
+% } else { #return nothing
+[]
+% }
% }
% elsif ( $sub eq 'exact_search' ) {
% # XXX possibly should query each element separately
@@ -39,22 +51,58 @@
% }
<%init>
-my $conf = new FS::Conf;
-
my $sub = $cgi->param('sub');
-sub findbycustnum{
- my $custnum = shift;
- my $agent = shift;
- my $hashref = { 'custnum' => $custnum };
- $hashref = { 'agent_custid' => $custnum } if $agent;
- my $c = qsearchs({
- 'table' => 'cust_main',
- 'hashref' => $hashref,
- 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
- });
- return [ $c->custnum, $c->name, $c->balance, $c->ucfirst_status, $c->statuscolor ]
- if $c;
- [];
+sub findbycustnum {
+
+ my $c = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => shift },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+ }) or return [];
+
+ [ $c->custnum,
+ $c->name,
+ $c->balance,
+ $c->ucfirst_status,
+ $c->statuscolor,
+ scalar($c->open_cust_bill)
+ ];
}
+
+sub findbycustnum_or_agent_custid {
+ my $num = shift;
+
+ my @or = ( 'agent_custid = ?' );
+ my @param = ( $num );
+
+ if ( $num =~ /^\d+$/ && $num <= 2147483647 ) { #need a bigint custnum? wow
+ my $conf = new FS::Conf;
+ if ( $conf->exists('cust_main-default_agent_custid') ) {
+ push @or, "( agent_custid IS NULL AND custnum = $num )";
+ } else {
+ push @or, "custnum = $num";
+ }
+ }
+
+ my $extra_sql = ' WHERE '. $FS::CurrentUser::CurrentUser->agentnums_sql.
+ ' AND ( '. join(' OR ', @or). ' )';
+
+ [ map [ $_->custnum,
+ $_->name,
+ $_->balance,
+ $_->ucfirst_status,
+ $_->statuscolor,
+ scalar($_->open_cust_bill),
+ ],
+
+ qsearch({
+ 'table' => 'cust_main',
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ 'extra_param' => \@param,
+ })
+ ];
+}
+
</%init>
diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html
index 974b96dc2..bd6bb860a 100644
--- a/httemplate/pref/pref-process.html
+++ b/httemplate/pref/pref-process.html
@@ -49,7 +49,7 @@ unless ( $error ) { # if ($access_user) {
#XXX autogen
my @paramlist = qw( locale menu_position default_customer_view mobile_menu
- disable_html_editor
+ disable_html_editor disable_enter_submit_onetimecharge
email_address
snom-ip snom-username snom-password
vonage-fromnumber vonage-username vonage-password
diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html
index 8fd1eaa73..8e56355db 100644
--- a/httemplate/pref/pref.html
+++ b/httemplate/pref/pref.html
@@ -32,7 +32,7 @@ Interface
<TR>
<TH ALIGN="right">Locale: </TH>
- <TD>
+ <TD COLSPAN=2>
<SELECT NAME="locale">
% foreach my $locale ( FS::Locales->locales ) {
% my %info = FS::Locales->locale_info($locale);
@@ -83,6 +83,13 @@ Interface
</TD>
</TR>
+ <TR>
+ <TH ALIGN="right" COLSPAN=1>Disable submission on [Enter] key - one-time charges: </TH>
+ <TD ALIGN="left" COLSPAN=2>
+ <INPUT TYPE="checkbox" NAME="disable_enter_submit_onetimecharge" VALUE="1" <% $curuser->option('disable_enter_submit_onetimecharge') ? 'CHECKED' : '' %>>
+ </TD>
+ </TR>
+
</TABLE>
<BR>
diff --git a/httemplate/search/cust_bill.html b/httemplate/search/cust_bill.html
index 813f9b843..406486a85 100755
--- a/httemplate/search/cust_bill.html
+++ b/httemplate/search/cust_bill.html
@@ -93,6 +93,10 @@ if ( $cgi->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/ ) {
$search{'agentnum'} = $1;
}
+ if ( $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ $search{'refnum'} = $1;
+ }
+
if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
$search{'custnum'} = $1;
}
diff --git a/httemplate/search/cust_bill_pay.html b/httemplate/search/cust_bill_pay.html
index 1fc8ffd78..22e9a6795 100644
--- a/httemplate/search/cust_bill_pay.html
+++ b/httemplate/search/cust_bill_pay.html
@@ -92,6 +92,13 @@ if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
$title = $agent->agent. " $title";
}
+if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ push @search, "refnum = $1";
+ my $part_referral = qsearchs('part_referral', { 'refnum' => $1 } );
+ die "unknown refnum $1" unless $part_referral;
+ $title = $part_referral->referral. " $title";
+}
+
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
push @search, "cust_bill._date >= $beginning ",
"cust_bill._date <= $ending";
diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi
index 94860d3f2..1a46b0097 100644
--- a/httemplate/search/cust_bill_pkg.cgi
+++ b/httemplate/search/cust_bill_pkg.cgi
@@ -9,7 +9,9 @@
'header' => [
emt('Description'),
( $unearned
- ? ( emt('Unearned'), emt('Owed'), emt('Payment date') )
+ ? ( emt('Unearned'),
+ emt('Owed'), # useful in 'paid' mode?
+ emt('Payment date') )
: ( emt('Setup charge') )
),
( $use_usage eq 'usage'
@@ -33,15 +35,9 @@
# they're not applicable to pkg_tax search
sub { my $cust_bill_pkg = shift;
if ( $unearned ) {
- my $period =
- $cust_bill_pkg->edate - $cust_bill_pkg->sdate;
- my $elapsed = $unearned - $cust_bill_pkg->sdate;
- $elapsed = 0 if $elapsed < 0;
- my $remaining = 1 - $elapsed/$period;
-
- sprintf($money_char. '%.2f',
- $remaining * $cust_bill_pkg->recur );
+ sprintf($money_char.'%.2f',
+ $cust_bill_pkg->unearned_revenue)
} else {
sprintf($money_char.'%.2f', $cust_bill_pkg->setup );
@@ -53,7 +49,7 @@
),
sub { my $row = shift;
my $value = 0;
- if ( $use_usage eq 'recurring' ) {
+ if ( $use_usage eq 'recurring' or $unearned ) {
$value = $row->recur - $row->usage;
} elsif ( $use_usage eq 'usage' ) {
$value = $row->usage;
@@ -64,7 +60,10 @@
},
( $unearned
? ( sub { time2str('%b %d %Y', shift->sdate ) },
- sub { time2str('%b %d %Y', shift->edate ) },
+ # shift edate back a day
+ # 82799 = 3600*23 - 1
+ # (to avoid skipping a day during DST)
+ sub { time2str('%b %d %Y', shift->edate - 82799 ) },
)
: ()
),
@@ -76,9 +75,11 @@
'',
'setup', #broken in $unearned case i guess
( $unearned ? ('', '') : () ),
- ( $use_usage eq 'recurring' ? 'recur - usage' :
- $use_usage eq 'usage' ? 'usage'
- : 'recur'
+ ( $use_usage eq 'recurring' or $unearned
+ ? 'recur - usage' :
+ $use_usage eq 'usage'
+ ? 'usage'
+ : 'recur'
),
( $unearned ? ('sdate', 'edate') : () ),
'invnum',
@@ -137,6 +138,12 @@ die "access denied"
my $conf = new FS::Conf;
my $unearned = '';
+my $unearned_mode = '';
+my $unearned_base = '';
+my $unearned_sql = '';
+
+my @select = ( 'cust_bill_pkg.*', 'cust_bill._date' );
+my ($join_cust, $join_pkg ) = ('', '');
#here is the agent virtualization
my $agentnums_sql =
@@ -146,20 +153,28 @@ my @where = ( $agentnums_sql );
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
+ push @where, FS::cust_main->cust_status_sql . " = '$1'";
+}
+
if ( $cgi->param('distribute') == 1 ) {
push @where, "sdate <= $ending",
"edate > $beginning",
;
}
else {
- push @where, "_date >= $beginning",
- "_date <= $ending";
+ push @where, "cust_bill._date >= $beginning",
+ "cust_bill._date <= $ending";
}
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
push @where, "cust_main.agentnum = $1";
}
+if ( $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.refnum = $1";
+}
+
#classnum
# not specified: all classes
# 0: empty class
@@ -218,7 +233,7 @@ if ( $cgi->param('taxclass')
}
-my @loc_param = qw( city county state country );
+my @loc_param = qw( district city county state country );
if ( $cgi->param('out') ) {
@@ -266,7 +281,7 @@ if ( $cgi->param('out') ) {
my %ph = ( 'county' => dbh->quote($_),
map { $_ => dbh->quote( $cgi->param($_) ) }
- qw( city state country )
+ qw( district city state country )
);
my ( $loc_sql, @param ) = FS::cust_pkg->location_sql;
@@ -330,21 +345,71 @@ if ( $cgi->param('out') ) {
push @where, FS::tax_rate_location->location_sql(
map { $_ => (scalar($cgi->param($_)) || '') }
- qw( city county state locationtaxid )
+ qw( district city county state locationtaxid )
);
-} elsif ( $cgi->param('unearned_now') =~ /^(\d+)$/ ) {
+}
+
+# unearned revenue mode
+if ( $cgi->param('unearned_now') =~ /^(\d+)$/ ) {
$unearned = $1;
+ $unearned_mode = $cgi->param('mode');
push @where, "cust_bill_pkg.sdate < $unearned",
"cust_bill_pkg.edate > $unearned",
"cust_bill_pkg.recur != 0",
- "part_pkg.freq != '0'",
+ "part_pkg.freq != '0'";
+
+ if ( !$cgi->param('include_monthly') ) {
+ push @where,
"part_pkg.freq != '1'",
"part_pkg.freq NOT LIKE '%h'",
"part_pkg.freq NOT LIKE '%d'",
"part_pkg.freq NOT LIKE '%w'";
+ }
+
+ my $usage_sql = FS::cust_bill_pkg->usage_sql;
+ push @select, "($usage_sql) AS usage"; # we need this
+ my $paid_sql = 'GREATEST(' .
+ FS::cust_bill_pkg->paid_sql($unearned, '', setuprecur => 'recur') .
+ " - $usage_sql, 0)";
+
+ push @select, "$paid_sql AS paid_no_usage"; # need this either way
+
+ if ( $unearned_mode eq 'paid' ) {
+ # then use the amount paid, minus usage charges
+ $unearned_base = $paid_sql;
+ }
+ else {
+ # use the amount billed, minus usage charges and credits
+ $unearned_base = "GREATEST( cust_bill_pkg.recur - ".
+ FS::cust_bill_pkg->credited_sql($unearned, '', setuprecur => 'recur') .
+ " - $usage_sql, 0)";
+ # include only rows that have some non-usage, non-credited portion
+ }
+ # whatever we're using as the base, only show rows where it's positive
+ push @where, "$unearned_base > 0";
+
+ my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS REAL)";
+ my $elapsed = "GREATEST( $unearned - cust_bill_pkg.sdate, 0 )";
+ my $remaining = "(1 - $elapsed/$period)";
+
+ $unearned_sql = "CAST( $unearned_base * $remaining AS DECIMAL(10,2) )";
+ push @select, "$unearned_sql AS unearned_revenue";
+
+ # last payment/credit date
+ my %t = (pay => 'cust_bill_pay', credit => 'cust_credit_bill');
+ foreach my $x (qw(pay credit)) {
+ my $table = $t{$x};
+ my $link = $table.'_pkg';
+ my $pkey = dbdef->table($table)->primary_key;
+ my $last_date_sql = "SELECT MAX(_date)
+ FROM $table JOIN $link USING ($pkey)
+ WHERE $link.billpkgnum = cust_bill_pkg.billpkgnum
+ AND $table._date <= $unearned";
+ push @select, "($last_date_sql) AS last_$x";
+ }
}
@@ -463,12 +528,12 @@ if ( $cgi->param('pkg_tax') ) {
$count_query = "SELECT COUNT(DISTINCT billpkgnum), ";
}
- if ( $use_usage eq 'recurring' ) {
- $count_query .= "SUM(setup + recur - usage)";
+ if ( $unearned ) {
+ $count_query .= "SUM( $unearned_base ), SUM( $unearned_sql )";
+ } elsif ( $use_usage eq 'recurring' ) {
+ $count_query .= "SUM(cust_bill_pkg.setup + cust_bill_pkg.recur - usage)";
} elsif ( $use_usage eq 'usage' ) {
$count_query .= "SUM(usage)";
- } elsif ( $unearned ) {
- $count_query .= "SUM(cust_bill_pkg.recur)";
} elsif ( scalar( grep( /locationtaxid/, $cgi->param ) ) ) {
$count_query .= "SUM( COALESCE(cust_bill_pkg_tax_rate_location.amount, cust_bill_pkg.setup + cust_bill_pkg.recur))";
} elsif ( $cgi->param('iscredit') eq 'rate') {
@@ -477,38 +542,17 @@ if ( $cgi->param('pkg_tax') ) {
$count_query .= "SUM(cust_bill_pkg.setup + cust_bill_pkg.recur)";
}
- if ( $unearned ) {
-
- #false laziness w/report_prepaid_income.cgi
-
- my $float = 'REAL'; #'DOUBLE PRECISION';
-
- my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS $float)";
- my $elapsed = "(CASE WHEN cust_bill_pkg.sdate > $unearned
- THEN 0
- ELSE ($unearned - cust_bill_pkg.sdate)
- END)";
- #my $elapsed = "CAST($unearned - cust_bill_pkg.sdate AS $float)";
-
- my $remaining = "(1 - $elapsed/$period)";
-
- $count_query .= ", SUM($remaining * cust_bill_pkg.recur)";
-
- }
-
}
-my $join_cust = ' JOIN cust_bill USING ( invnum )
- LEFT JOIN cust_main USING ( custnum ) ';
-
+$join_cust = ' JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum ) ';
-my $join_pkg;
if ( $cgi->param('nottax') ) {
- $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum )
- LEFT JOIN part_pkg USING ( pkgpart )
- LEFT JOIN part_pkg AS override
- ON pkgpart_override = override.pkgpart ';
+ $join_pkg .= ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart )
+ LEFT JOIN part_pkg AS override
+ ON pkgpart_override = override.pkgpart ';
$join_pkg .= ' LEFT JOIN cust_location USING ( locationnum ) '
if $conf->exists('tax-pkg_address');
@@ -567,9 +611,6 @@ if ($use_usage) {
$count_query .= " FROM cust_bill_pkg $join_cust $join_pkg $where";
}
-my @select = ( 'cust_bill_pkg.*',
- 'cust_bill._date', );
-
push @select, 'part_pkg.pkg',
'part_pkg.freq',
unless $cgi->param('istax');
@@ -581,9 +622,9 @@ my $query = {
'table' => 'cust_bill_pkg',
'addl_from' => "$join_cust $join_pkg",
'hashref' => {},
- 'select' => join(', ', @select ),
+ 'select' => join(",\n", @select ),
'extra_sql' => $where,
- 'order_by' => 'ORDER BY _date, billpkgnum',
+ 'order_by' => 'ORDER BY cust_bill._date, billpkgnum',
};
my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
@@ -593,9 +634,8 @@ my $conf = new FS::Conf;
my $money_char = $conf->config('money_char') || '$';
my $owed_sub = sub {
- $money_char. shift->owed_recur; #_recur :/
+ $money_char . shift->get('owed') # owed_recur is not correct here
};
-
my $payment_date_sub = sub {
#my $cust_bill_pkg = shift;
my @cust_pay = sort { $a->_date <=> $b->_date }
diff --git a/httemplate/search/cust_bill_pkg_referral.html b/httemplate/search/cust_bill_pkg_referral.html
new file mode 100644
index 000000000..1bb6b9179
--- /dev/null
+++ b/httemplate/search/cust_bill_pkg_referral.html
@@ -0,0 +1,287 @@
+<& elements/search.html,
+ 'title' => emt('Sales with advertising source'),
+ 'name' => emt('line items'),
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [
+ ($setup ? $money_char. '%.2f setup' : ()),
+ ($recur ? $money_char. '%.2f recurring' : ()),
+ ($usage ? $money_char. '%.2f usage' : ()),
+ ],
+ 'header' => [
+ emt('Description'),
+ ($setup ? emt('Setup') : ()),
+ ($recur ? emt('Recurring') : ()),
+ ($usage ? emt('Usage') : ()),
+ emt('Invoice'),
+ emt('Invoice date'),
+ emt('Paid'),
+ emt('Payment date'),
+ emt('Pkg. status'),
+ emt('Pkg. class'),
+ '', #report class
+ emt('Cust#'),
+ emt('Customer'),
+ emt('Ad source'),
+ emt('Agent'),
+ ],
+ 'fields' => [
+ 'pkg',
+ ($setup ? money_sub('setup') : ()),
+ ($recur ? money_sub('recur_no_usage') : ()),
+ ($usage ? money_sub('recur_usage') : ()),
+ 'invnum',
+ date_sub('_date'),
+ money_sub('paid'),
+ date_sub('last_pay'),
+ sub {
+ my $cust_pkg = shift->cust_pkg;
+ $cust_pkg ? ucfirst($cust_pkg->status) : '';
+ },
+ 'classname',
+ sub { # report_option
+ my $cust_bill_pkg = shift;
+ my $pkgpart = $cust_bill_pkg->pkgpart_override
+ || $cust_bill_pkg->cust_pkg->pkgpart;
+ if ( !exists($report_classes{$pkgpart}) ) {
+ my $part_pkg = FS::part_pkg->by_key($pkgpart);
+ my %opts = $part_pkg->options;
+ $report_classes{$pkgpart} = [
+ map { /^report_option_(\d+)/ ?
+ $report_option_name{$1} :
+ () }
+ keys %opts
+ ];
+ }
+ join( '<BR>', @{ $report_classes{$pkgpart} });
+ },
+ 'custnum',
+ 'name',
+ 'referral', # from query
+ 'agent',
+ ],
+ 'sort_fields' => [
+ '',
+ ($setup ? 'setup' : ()),
+ ($recur ? 'recur_no_usage' : ()),
+ ($usage ? 'recur_usage' : ()),
+ 'invnum',
+ '_date',
+ 'paid',
+ 'last_pay',
+ '', #package status
+ 'classname',
+ '', #report_option
+ 'custnum',
+ '',
+ 'referral',
+ 'agent',
+ ],
+ 'links' => [
+ '', #package/item desc
+ ('') x $x, #setup/recur/usage
+ $ilink, #invnum
+ $ilink, #invoice date
+ '', #paid amt
+ '', #payment date
+ '', #pkg status
+ '', #classnum
+ '', #report class
+ $clink, #custnum
+ $clink, #customer name
+ '', #referral
+ '', #agent
+ ],
+ #'align' => 'rlrrrc'.FS::UI::Web::cust_aligns(),
+ 'align' => 'l' . ('r' x $x) . 'rcrccccrlll',
+ 'color' => [ ('') x (5 + $x),
+ sub {
+ my $cust_pkg = shift->cust_pkg;
+ $cust_pkg ? ucfirst($cust_pkg->statuscolor) : '';
+ },
+ ('') x 6,
+ ],
+ 'style' => [
+ ('') x (5 + $x),
+ 'b',
+ ('') x 6
+ ],
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+my $setup = $cgi->param('setup') ? 1 : 0;
+my $recur = $cgi->param('recur') ? 1 : 0;
+my $usage = $cgi->param('usage') ? 1 : 0;
+
+my $x = $setup + $recur + $usage;
+
+my @select = ( 'cust_bill_pkg.*', 'cust_bill._date' );
+my ($join_cust, $join_pkg ) = ('', '');
+
+#here is the agent virtualization
+my $agentnums_sql =
+ $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+
+my @where = ( $agentnums_sql,
+ 'cust_bill_pkg.pkgnum != 0', # exclude taxes
+ "cust_bill._date >= $beginning",
+ "cust_bill._date <= $ending",
+ );
+
+if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
+ push @where, FS::cust_pkg->cust_status_sql . " = '$1'";
+}
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.agentnum = $1";
+}
+
+#classnum
+# not specified: all classes
+# 0: empty class
+# N: classnum
+my $use_override = 1; #$cgi->param('use_override');
+if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
+ my $comparison = '';
+ if ( $1 == 0 ) {
+ $comparison = "IS NULL";
+ } else {
+ $comparison = "= $1";
+ }
+
+ if ( $use_override ) {
+ push @where, "(
+ part_pkg.classnum $comparison AND pkgpart_override IS NULL OR
+ override.classnum $comparison AND pkgpart_override IS NOT NULL
+ )";
+ } else {
+ push @where, "part_pkg.classnum $comparison";
+ }
+}
+
+# report option
+my @report_option = grep /^\d+$/, ( $cgi->param('report_option') );
+if ( @report_option ) {
+ @report_option = map { "'report_option_$_'" } @report_option;
+ push @where, "EXISTS(
+ SELECT 1 FROM part_pkg_option WHERE optionname IN (".
+ join(',', @report_option).") AND (
+ part_pkg_option.pkgpart = cust_pkg.pkgpart AND pkgpart_override IS NULL
+ OR part_pkg_option.pkgpart = pkgpart_override
+ )
+ )";
+}
+
+my $setup_sql =
+ FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'setup');
+my $recur_sql =
+ FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'recur', no_usage => 1);
+my $usage_sql = FS::cust_bill_pkg->usage_sql;
+
+# exclude zero-amount items
+my @orwhere;
+push @orwhere, "(cust_bill_pkg.setup > 0)" if $setup;
+push @orwhere, "($recur_sql > 0)" if $recur;
+push @orwhere, "($usage_sql > 0)" if $usage;
+push @where, '('.join(' OR ', @orwhere).')' if @orwhere;
+
+$join_cust = ' JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum )
+ LEFT JOIN part_referral USING ( refnum )
+ LEFT JOIN agent ON cust_main.agentnum = agent.agentnum
+ ';
+
+$join_pkg .= ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart )
+ LEFT JOIN part_pkg AS override
+ ON pkgpart_override = override.pkgpart
+ LEFT JOIN pkg_class ON '; #...
+
+if ( $use_override ) {
+ # join to whichever pkgpart is appropriate
+ $join_pkg .= '
+ ( pkgpart_override IS NULL AND part_pkg.classnum = pkg_class.classnum )
+ OR ( pkgpart_override IS NOT NULL AND override.classnum = pkg_class.classnum )';
+} else {
+ $join_pkg .= 'part_pkg.classnum = pkg_class.classnum';
+}
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+# setup and recurring only
+my $count_query = "SELECT
+ COUNT(billpkgnum)".
+ ($setup ? ", SUM($setup_sql)" : '').
+ ($recur ? ", SUM($recur_sql)" : '').
+ ($usage ? ", SUM($usage_sql)" : '').
+ " FROM cust_bill_pkg
+ $join_cust
+ $join_pkg
+ $where
+ ";
+
+my $paid_sql = FS::cust_bill_pkg->paid_sql('', '');
+my $last_pay_sql = "SELECT MAX(_date)
+ FROM cust_bill_pay JOIN cust_bill_pay_pkg USING (billpaynum)
+ WHERE cust_bill_pay_pkg.billpkgnum = cust_bill_pkg.billpkgnum";
+
+push @select, 'part_pkg.pkg',
+ 'part_pkg.freq',
+ 'cust_main.custnum',
+ 'cust_main.first',
+ 'cust_main.last',
+ 'cust_main.company',
+ 'part_referral.referral',
+ "($paid_sql) AS paid",
+ "($last_pay_sql) AS last_pay",
+ "($recur_sql) AS recur_no_usage",
+ "($usage_sql) AS recur_usage",
+ 'pkg_class.classname',
+ 'agent.agent',
+ ;
+
+my $query = {
+ 'table' => 'cust_bill_pkg',
+ 'addl_from' => "$join_cust $join_pkg",
+ 'hashref' => {},
+ 'select' => join(",\n", @select ),
+ 'extra_sql' => $where,
+ 'order_by' => 'ORDER BY cust_bill._date, billpkgnum',
+};
+
+my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my %report_classes; #cache
+my %report_option_name =
+ map { $_->num => $_->name } qsearch('part_pkg_report_option', {});
+
+# should this be in Mason.pm or something?
+sub money_sub {
+ $conf ||= new FS::Conf;
+ $money_char ||= $conf->config('money_char') || '$';
+ my $field = shift;
+ sub {
+ $money_char . sprintf('%.2f', $_[0]->get($field));
+ };
+}
+
+sub date_sub {
+ my $field = shift;
+ sub {
+ my $value = $_[0]->get($field);
+ $value ? time2str('%b %d %Y', $value) : '';
+ };
+}
+
+</%init>
diff --git a/httemplate/search/cust_credit.html b/httemplate/search/cust_credit.html
index 917257034..38f03491d 100755
--- a/httemplate/search/cust_credit.html
+++ b/httemplate/search/cust_credit.html
@@ -96,6 +96,13 @@ if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
$title = $agent->agent. " $title";
}
+if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ push @search, "refnum = $1";
+ my $part_referral = qsearchs('part_referral', { 'refnum' => $1 } );
+ die "unknown refnum $1" unless $part_referral;
+ $title = $part_referral->referral. " $title";
+}
+
if ( $unapplied ) {
push @search, FS::cust_credit->unapplied_sql . ' > 0';
}
diff --git a/httemplate/search/cust_credit_bill.html b/httemplate/search/cust_credit_bill.html
index 7f9eb7887..9fd6a987a 100644
--- a/httemplate/search/cust_credit_bill.html
+++ b/httemplate/search/cust_credit_bill.html
@@ -85,6 +85,13 @@ if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
$title = $agent->agent. " $title";
}
+if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ push @search, "refnum = $1";
+ my $part_referral = qsearchs('part_referral', { 'refnum' => $1 } );
+ die "unknown refnum $1" unless $part_referral;
+ $title = $part_referral->referral. " $title";
+}
+
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
push @search, "cust_bill._date >= $beginning ",
"cust_bill._date <= $ending";
diff --git a/httemplate/search/cust_credit_bill_pkg.html b/httemplate/search/cust_credit_bill_pkg.html
index fef295e62..4612118a2 100644
--- a/httemplate/search/cust_credit_bill_pkg.html
+++ b/httemplate/search/cust_credit_bill_pkg.html
@@ -167,7 +167,7 @@ if ( $cgi->param('taxclass')
}
-my @loc_param = qw( city county state country );
+my @loc_param = qw( district city county state country );
if ( $cgi->param('out') ) {
@@ -215,7 +215,7 @@ if ( $cgi->param('out') ) {
my %ph = ( 'county' => dbh->quote($_),
map { $_ => dbh->quote( $cgi->param($_) ) }
- qw( city state country )
+ qw( district city state country )
);
my ( $loc_sql, @param ) = FS::cust_pkg->location_sql;
diff --git a/httemplate/search/cust_credit_refund.html b/httemplate/search/cust_credit_refund.html
index fd87aa575..361c8ad2f 100644
--- a/httemplate/search/cust_credit_refund.html
+++ b/httemplate/search/cust_credit_refund.html
@@ -78,6 +78,13 @@ if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
$title = $agent->agent. " $title";
}
+if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ push @search, "refnum = $1";
+ my $part_referral = qsearchs('part_referral', { 'refnum' => $1 } );
+ die "unknown refnum $1" unless $part_referral;
+ $title = $part_referral->referral. " $title";
+}
+
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
push @search, "cust_credit._date >= $beginning ",
"cust_credit._date <= $ending";
diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi
index aae8c7e99..859ef04e6 100755
--- a/httemplate/search/cust_main.cgi
+++ b/httemplate/search/cust_main.cgi
@@ -334,7 +334,7 @@
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
- unless $curuser->access_right('List customers');
+ unless $curuser->access_right('List all customers');
my $conf = new FS::Conf;
my $maxrecords = $conf->config('maxsearchrecordsperpage');
diff --git a/httemplate/search/cust_main.html b/httemplate/search/cust_main.html
index 498024ba0..e164b98f4 100755
--- a/httemplate/search/cust_main.html
+++ b/httemplate/search/cust_main.html
@@ -33,9 +33,7 @@
<%init>
die "access denied"
- unless ( $FS::CurrentUser::CurrentUser->access_right('List customers') &&
- $FS::CurrentUser::CurrentUser->access_right('List packages')
- );
+ unless $FS::CurrentUser::CurrentUser->access_right('Advanced customer search');
my %search_hash = ();
@@ -47,7 +45,6 @@ my @scalars = qw (
no_censustract with_geocode custbatch usernum
cancelled_pkgs
cust_fields flattened_pkgs
- refnum
);
for my $param ( @scalars ) {
@@ -56,7 +53,7 @@ for my $param ( @scalars ) {
}
#lists
-for my $param (qw( classnum payby tagnum )) {
+for my $param (qw( classnum refnum payby tagnum )) {
$search_hash{$param} = [ $cgi->param($param) ];
}
@@ -64,14 +61,22 @@ for my $param (qw( classnum payby tagnum )) {
# parse dates
###
-foreach my $field (qw( signupdate )) {
+foreach my $field (qw( signupdate birthdate spouse_birthdate )) {
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
- next if $beginning == 0 && $ending == 4294967295 && !defined($cgi->param('signuphour'));
+ next if $beginning == 0 && $ending == 4294967295 && ( $field ne 'signupdate' || !defined($cgi->param('signuphour')) );
#or $disable{$cgi->param('status')}->{$field};
- $search_hash{$field} = [ $beginning, $ending, $cgi->param('signuphour') ];
+ unless ( $field eq 'signupdate' ) {
+ $beginning -= 43200;
+ $ending -= 43200;
+ }
+
+ my @ary = ( $beginning, $ending );
+ push @ary, scalar($cgi->param('signuphour')) if $field eq 'signupdate';
+
+ $search_hash{$field} = \@ary;
}
diff --git a/httemplate/search/cust_pkg.cgi b/httemplate/search/cust_pkg.cgi
index 297edee90..887ec6039 100755
--- a/httemplate/search/cust_pkg.cgi
+++ b/httemplate/search/cust_pkg.cgi
@@ -20,6 +20,7 @@
emt('Susp. delay'),
emt('Expire'),
emt('Contract end'),
+ emt('Changed'),
emt('Cancel'),
emt('Reason'),
FS::UI::Web::cust_header(
@@ -45,7 +46,7 @@
sub { FS::part_pkg::freq_pretty(shift); },
( map { time_or_blank($_) }
- qw( setup last_bill bill adjourn susp dundate expire contract_end cancel ) ),
+ qw( setup last_bill bill adjourn susp dundate expire contract_end change_date cancel ) ),
sub { my $self = shift;
my $return = '';
@@ -94,13 +95,14 @@
'',
'',
'',
+ '',
FS::UI::Web::cust_colors(),
'',
],
- 'style' => [ '', '', '', '', 'b', '', '', '', '', '', '', '', '', '', '', '', '', '',
+ 'style' => [ '', '', '', '', 'b', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
FS::UI::Web::cust_styles() ],
'size' => [ '', '', '', '', '-1' ],
- 'align' => 'rrlccrrlrrrrrrrrrl'. FS::UI::Web::cust_aligns(). 'r',
+ 'align' => 'rrlccrrlrrrrrrrrrrl'. FS::UI::Web::cust_aligns(). 'r',
'links' => [
$link,
$link,
@@ -117,6 +119,7 @@
'',
'',
'',
+ '', # link to changed-from package?
'',
'',
'',
@@ -182,7 +185,7 @@ my %disable = (
'' => {},
);
-foreach my $field (qw( setup last_bill bill adjourn susp expire contract_end cancel active )) {
+foreach my $field (qw( setup last_bill bill adjourn susp expire contract_end change_date cancel active )) {
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
diff --git a/httemplate/search/cust_pkg_summary.cgi b/httemplate/search/cust_pkg_summary.cgi
index cea4cdcd2..fbeeb92ce 100644
--- a/httemplate/search/cust_pkg_summary.cgi
+++ b/httemplate/search/cust_pkg_summary.cgi
@@ -25,7 +25,7 @@
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
- unless $curuser->access_right('List packages');
+ unless $curuser->access_right('Summarize packages');
my $title = 'Package Summary Report';
my ($begin, $end) = FS::UI::Web::parse_beginning_ending($cgi);
diff --git a/httemplate/search/cust_pkg_summary.html b/httemplate/search/cust_pkg_summary.html
index a0ef47210..8c05f7382 100644
--- a/httemplate/search/cust_pkg_summary.html
+++ b/httemplate/search/cust_pkg_summary.html
@@ -21,4 +21,8 @@
<% include('/elements/footer.html') %>
<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Summarize packages');
+
</%init>
diff --git a/httemplate/search/cust_pkg_susp.cgi b/httemplate/search/cust_pkg_susp.cgi
index 9ab5992d9..d6bbc43d6 100644
--- a/httemplate/search/cust_pkg_susp.cgi
+++ b/httemplate/search/cust_pkg_susp.cgi
@@ -25,7 +25,7 @@
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
- unless $curuser->access_right('List packages');
+ unless $curuser->access_right('Summarize packages');
my $money_char = FS::Conf->new()->config('money_char') || '$';
diff --git a/httemplate/search/cust_pkg_susp.html b/httemplate/search/cust_pkg_susp.html
index c59e6c158..2ac643260 100644
--- a/httemplate/search/cust_pkg_susp.html
+++ b/httemplate/search/cust_pkg_susp.html
@@ -21,4 +21,8 @@
<% include('/elements/footer.html') %>
<%init>
+
+die "access denied"
+ unless $curuser->access_right('Summarize packages');
+
</%init>
diff --git a/httemplate/search/cust_svc.html b/httemplate/search/cust_svc.html
index 9cf4bbda6..2adcbd76f 100644
--- a/httemplate/search/cust_svc.html
+++ b/httemplate/search/cust_svc.html
@@ -13,7 +13,10 @@
sub {
#$_[0]->svc. ': '. $_[0]->label;
my($label, $value, $svcdb) = $_[0]->label;
- "$label: $value";
+ my $id = $_[0]->agent_svcid
+ ? $_[0]->agent_svcid.': '
+ : '';
+ "$label: $id$value";
},
# package?
\&FS::UI::Web::cust_fields,
@@ -42,79 +45,75 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
-my $addl_from = ' LEFT JOIN part_svc USING ( svcpart ) '.
- ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
- ' LEFT JOIN cust_main USING ( custnum ) ';
+my $sql_query;
-my @extra_sql = ();
my $orderby = 'ORDER BY svcnum'; #has to be ordered by something
#for pagination to work
+
if ( length( $cgi->param('search_svc') ) ) {
- my $string = $cgi->param('search_svc');
- $string =~ s/(^\s+|\s+$)//; #trim leading & trailing whitespace
-
- # implement fuzzy searching in subclasses too at some point?
- # service searching maybe shouldn't be fuzzy...
-
- push @extra_sql,
- ' ( '. join(' OR ',
- map { my $table = $_;
- my $search_sql = "FS::$table"->search_sql($string);
- " ( svcdb = '$table'
- AND 0 < ( SELECT COUNT(*) FROM $table
- WHERE $table.svcnum = cust_svc.svcnum
- AND $search_sql
- )
- ) ";
- }
- FS::part_svc->svc_tables
- ). ' ) ';
-
-} elsif ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
-
- $cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unknown svcdb";
- push @extra_sql, "svcdb = '$1'";
- $addl_from .= " LEFT JOIN $1 USING ( svcnum ) ";
-
- push @extra_sql, 'pkgnum IS NULL'
- if $cgi->param('magic') eq 'unlinked';
-
- if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
- my $sortby = $1;
- $orderby = "ORDER BY $sortby";
+ $sql_query = {
+ FS::cust_svc->smart_search_param(
+ 'search' => scalar($cgi->param('search_svc'))
+ )
+ };
+
+} else {
+
+ my $addl_from = ' LEFT JOIN part_svc USING ( svcpart ) '.
+ ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
+ ' LEFT JOIN cust_main USING ( custnum ) ';
+
+ my @extra_sql = ();
+
+ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
+
+ $cgi->param('svcdb') =~ /^(svc_\w+)$/ or die "unknown svcdb";
+ push @extra_sql, "svcdb = '$1'";
+ $addl_from .= " LEFT JOIN $1 USING ( svcnum ) ";
+
+ push @extra_sql, 'pkgnum IS NULL'
+ if $cgi->param('magic') eq 'unlinked';
+
+ if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ my $sortby = $1;
+ $orderby = "ORDER BY $sortby";
+ }
+
+ } elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+
+ push @extra_sql, "svcpart = $1";
+
+ } else {
+ errorpage("No search term specified");
}
-} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
+ #here is the agent virtualization
+ push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
- push @extra_sql, "svcpart = $1";
+ my $extra_sql = ' WHERE '. join(' AND ', @extra_sql );
+
+ $sql_query = {
+ 'table' => 'cust_svc',
+ 'addl_from' => $addl_from,
+ 'hashref' => {},
+ 'extra_sql' => $extra_sql,
+ };
-} else {
- errorpage("No search term specified");
}
-#here is the agent virtualization
-push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
- 'null_right' => 'View/link unlinked services'
- );
-
-my $extra_sql = ' WHERE '. join(' AND ', @extra_sql );
-
-my $sql_query = {
- 'select' => join(', ',
- 'cust_svc.*',
- 'part_svc.*',
- 'cust_main.custnum',
- FS::UI::Web::cust_sql_fields(),
- ),
- 'table' => 'cust_svc',
- 'addl_from' => $addl_from,
- 'hashref' => {},
- 'extra_sql' => $extra_sql,
- 'order_by' => $orderby,
-};
+$sql_query->{'select'} = join(', ',
+ 'cust_svc.*',
+ 'part_svc.*',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields(),
+ );
+$sql_query->{'order_by'} = $orderby;
-my $count_query = "SELECT COUNT(*) FROM cust_svc $addl_from $extra_sql";
+my $count_query = "SELECT COUNT(*) FROM cust_svc ". $sql_query->{addl_from}.
+ ' '. $sql_query->{extra_sql};
my $link = sub {
my $cust_svc = shift;
diff --git a/httemplate/search/customer_accounting_summary.html b/httemplate/search/customer_accounting_summary.html
index 8da8914c8..72a00ed95 100644
--- a/httemplate/search/customer_accounting_summary.html
+++ b/httemplate/search/customer_accounting_summary.html
@@ -30,6 +30,19 @@ elsif ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
die "agentnum $agentnum not found!" unless $sel_agent;
}
my $title = $sel_agent ? $sel_agent->agent.' ' : '';
+
+my ($refnum,$sel_part_referral);
+#if ( $cgi->param('refnum') eq 'all' ) {
+# $refnum = 0;
+#} els
+if ( $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ $refnum = $1;
+ $sel_part_referral = qsearchs('part_referral', { 'refnum' => $refnum } );
+ die "refnum $refnum not found!" unless $sel_part_referral;
+}
+$title .= $sel_part_referral->referral.' '
+ if $sel_part_referral;
+
$title .= 'Customer Accounting Summary Report';
my @custs = ();
@@ -45,6 +58,7 @@ die "invalid status" unless $status =~ /^\w+|$/;
foreach my $cust_main ( @custs ) {
next unless ($status eq '' || $status eq $cust_main->status);
next unless ($agentnum == 0 || $cust_main->agentnum eq $agentnum);
+ next unless ($refnum == 0 || $cust_main->refnum eq $refnum);
push @items, 'netsales', 'cashflow';
diff --git a/httemplate/search/elements/cust_main_dayranges.html b/httemplate/search/elements/cust_main_dayranges.html
index cc1601455..eb7566494 100644
--- a/httemplate/search/elements/cust_main_dayranges.html
+++ b/httemplate/search/elements/cust_main_dayranges.html
@@ -108,13 +108,14 @@ my $ranges = $opt{'ranges'} ? delete($opt{'ranges'}) : [
my $range_sub = delete($opt{'range_sub'}); #or die
-my $offset = 0;
+my $as_of;
if($cgi->param('as_of')) {
- $offset = int((time - parse_datetime($cgi->param('as_of'))) / 86400);
- $opt{'title'} .= ' ('.$cgi->param('as_of').')' if $offset > 0;
+ $as_of = parse_datetime($cgi->param('as_of')) || '';
+ $opt{'title'} .= ' ('.$cgi->param('as_of').')' if $as_of;
}
-my $range_cols = join(',', map call_range_sub($range_sub, @$_, 'offset' => $offset ), @$ranges );
+my $range_cols = join(',',
+ map call_range_sub($range_sub, @$_, 'as_of' => $as_of ), @$ranges );
my $select_count_pkgs = FS::cust_main->select_count_pkgs_sql;
@@ -144,7 +145,7 @@ unless ( $cgi->param('all_customers') ) {
my $negative = $cgi->param('negative') || 0;
push @where,
- call_range_sub($range_sub, $days, 0, 'offset' => $offset, 'no_as'=>1).
+ call_range_sub($range_sub, $days, 0, 'as_of' => $as_of, 'no_as'=>1).
($negative ? ' != 0' : ' > 0');
}
@@ -186,7 +187,9 @@ my $sql_query = {
my $total_sql =
"SELECT ".
- join(',', map call_range_sub( $range_sub, @$_, 'offset' => $offset, 'sum'=>1 ), @$ranges).
+ join(',',
+ map call_range_sub( $range_sub, @$_, 'as_of' => $as_of, 'sum'=>1 ),
+ @$ranges).
" FROM cust_main $where";
my $total_sth = dbh->prepare($total_sql) or die dbh->errstr;
@@ -251,10 +254,11 @@ sub call_range_sub {
my $as = $opt{'no_as'} ? '' : " AS rangecol_${startdays}_$enddays";
- my $offset = $opt{'offset'} || 0;
- # Always use $offset - 1day + 1sec = the last second of that day
- my $cutoff = DateTime->now->set(hour => 23, minute => 59, second => 59);
- $cutoff->subtract(days => $offset);
+ my $as_of = $opt{'as_of'} || time;
+ my $cutoff = DateTime->from_epoch(epoch => $as_of, time_zone => 'local');
+ $cutoff->truncate(to => 'day'); # local midnight on the report day
+ $cutoff->add(days => 1); # the day after that
+ $cutoff->subtract(seconds => 1); # the last second of the report day
my $start = $cutoff->clone;
$start->subtract(days => $startdays);
@@ -262,7 +266,7 @@ sub call_range_sub {
my $end = $cutoff->clone;
$end->subtract(days => $enddays);
- #warn "offset $offset (".$cutoff->epoch."), range $startdays-$enddays (".$start->epoch . '-' . ($enddays ? $end->epoch : '').")\n";
+ #warn "cutoff ".$cutoff->epoch.", range $startdays-$enddays (".$start->epoch . '-' . ($enddays ? $end->epoch : '').")\n";
my $sql = &{$range_sub}( $start->epoch,
$enddays ? $end->epoch : '',
$cutoff->epoch ); #%opt?
diff --git a/httemplate/search/elements/cust_pay_batch_top.html b/httemplate/search/elements/cust_pay_batch_top.html
index ce0ee9ed4..005b76182 100644
--- a/httemplate/search/elements/cust_pay_batch_top.html
+++ b/httemplate/search/elements/cust_pay_batch_top.html
@@ -14,7 +14,8 @@ Download batch in format <SELECT NAME="format">
% foreach ( keys %download_formats ) {
<OPTION VALUE="<%$_%>"><% $download_formats{$_} %></OPTION>
% }
-</SELECT>
+</SELECT>
+<& .select_gateway &>
% }
<INPUT TYPE="submit" VALUE="Download"></FORM><BR><BR></TR>
% } # end of download
@@ -31,7 +32,7 @@ Download batch in format <SELECT NAME="format">
'name' => 'FileUpload',
'action' => "${p}misc/upload-batch.cgi",
'num_files' => 1,
- 'fields' => [ 'batchnum', 'format' ],
+ 'fields' => [ 'batchnum', 'format', 'gatewaynum' ],
'message' => 'Batch results uploaded.',
) %>
Upload results<BR></TR>
@@ -45,20 +46,22 @@ Upload results<BR></TR>
<BR></TR>
% if ( $fixed ) {
% if ( $fixed eq 'td_eft1464' ) { # special case
-<TR>Format <SELECT NAME="format">
+<TR>Upload in format <SELECT NAME="format">
<OPTION VALUE="td_eftack264">TD EFT Acknowledgement</OPTION>
<OPTION VALUE="td_eftret80">TD EFT Returned Items</OPTION>
-</SELECT></TR>
+</SELECT> </TR>
% }
% else {
<INPUT TYPE="hidden" NAME="format" VALUE="<% $fixed %>">
% }
% }
% else {
-<TR>Format <SELECT NAME="format">
+<TR>Upload in format <SELECT NAME="format">
% foreach ( keys(%upload_formats) ) {
<OPTION VALUE="<%$_%>"><% $upload_formats{$_} %></OPTION>
% }
+</SELECT>
+<& .select_gateway &>
% } # if $fixed
<TR><INPUT TYPE="submit" VALUE="Upload"></TR>
</FORM><BR>
@@ -82,6 +85,26 @@ Batch is <% $statustext{$status} %><BR>
<%$count%> payments batched<BR>
<%$money_char%><%$total%> total in batch<BR>
+<%def .select_gateway>
+% if ( $show_gateways ) {
+ or from gateway
+<& /elements/select-table.html,
+ empty_label => ' ',
+ field => 'gatewaynum',
+ table => 'payment_gateway',
+ name_col => 'label',
+ value_col => 'gatewaynum',
+ order_by => 'ORDER BY gatewaynum',
+ hashref => {
+ 'gateway_namespace' => 'Business::BatchPayment',
+ 'disabled' => '',
+ }
+&>
+% }
+</%def>
+<%shared>
+my $show_gateways = FS::payment_gateway->count("gateway_namespace = 'Business::BatchPayment'");
+</%shared>
<%init>
my %opt = @_;
my $pay_batch = $opt{'pay_batch'} or return;
@@ -91,7 +114,7 @@ my $payby = $pay_batch->payby;
my $status = $pay_batch->status;
my $curuser = $FS::CurrentUser::CurrentUser;
my $batchnum = $pay_batch->batchnum;
-
+
my $fixed = $conf->config("batch-fixed_format-$payby");
tie my %download_formats, 'Tie::IxHash', (
diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html
index 002b1a4c2..dc3cb2a99 100755
--- a/httemplate/search/elements/cust_pay_or_refund.html
+++ b/httemplate/search/elements/cust_pay_or_refund.html
@@ -232,6 +232,13 @@ if ( $cgi->param('magic') ) {
$title = $agent->agent. " $title";
}
+ if ( $cgi->param('refnum') && $cgi->param('refnum') =~ /^(\d+)$/ ) {
+ push @search, "refnum = $1";
+ my $part_referral = qsearchs('part_referral', { 'refnum' => $1 } );
+ die "unknown refnum $1" unless $part_referral;
+ $title = $part_referral->referral. " $title";
+ }
+
if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
push @search, "custnum = $1";
}
diff --git a/httemplate/search/elements/search-html.html b/httemplate/search/elements/search-html.html
index af0c8fc09..53167c26e 100644
--- a/httemplate/search/elements/search-html.html
+++ b/httemplate/search/elements/search-html.html
@@ -130,7 +130,9 @@
</TD>
-% unless ( $opt{'disable_download'} || $type eq 'html-print' ) {
+% if ( $curuser->access_right('Download report data')
+% and !$opt{'disable_download'}
+% and $type ne 'html-print' ) {
<TD ALIGN="right">
@@ -470,6 +472,8 @@
% }
<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+
my %args = @_;
my $type = $args{'type'};
my $header = $args{'header'};
diff --git a/httemplate/search/elements/search-xls.html b/httemplate/search/elements/search-xls.html
index 0b5636c0e..a3a8226c5 100644
--- a/httemplate/search/elements/search-xls.html
+++ b/httemplate/search/elements/search-xls.html
@@ -42,14 +42,18 @@ my $default_format = $workbook->add_format(locked => 0);
my %money_format;
my $money_char = FS::Conf->new->config('money_char') || '$';
+my %date_format;
+xl_parse_date_init();
+
my $writer = sub {
# Wrapper for $worksheet->write.
# Do any massaging of the value/format here.
my ($r, $c, $value, $format) = @_;
- if ( $value =~ /^\Q$money_char\E(\d+\.?\d*)$/ ) {
+ if ( $value =~ /^\Q$money_char\E(-?\d+\.?\d*)$/ ) {
# Currency: strip the symbol, clone the requested format,
# and format it for currency
$value = $1;
+# warn "formatting $value as money\n";
if ( !exists($money_format{$format}) ) {
$money_format{$format} = $workbook->add_format();
$money_format{$format}->copy($format);
@@ -57,6 +61,22 @@ my $writer = sub {
}
$format = $money_format{$format};
}
+ elsif ( $value =~ /^([A-Z][a-z]{2}) (\d{2}) (\d{4})$/ ) {
+ # Date: convert the value to an Excel date number and set
+ # the format
+ $value = xl_parse_date($value);
+# warn "formatting $value as date\n";
+ if ( !exists($date_format{$format}) ) {
+ $date_format{$format} = $workbook->add_format();
+ $date_format{$format}->copy($format);
+ $date_format{$format}->set_num_format('mmm dd yyyy');
+ }
+ $format = $date_format{$format};
+ }
+ else {
+ # String: replace line breaks with newlines
+ $value =~ s/<BR>/\n/gi;
+ }
$worksheet->write($r, $c, $value, $format);
};
diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html
index 81ec4d082..9bc66b6fa 100644
--- a/httemplate/search/elements/search.html
+++ b/httemplate/search/elements/search.html
@@ -170,7 +170,6 @@ Example:
%
<% include('search-csv.html', header=>$header, rows=>$rows, opt=>\%opt ) %>
%
-% #} elsif ( $type eq 'excel' ) {
% } elsif ( $type =~ /\.xls$/ ) {
%
<% include('search-xls.html', header=>$header, rows=>$rows, opt=>\%opt ) %>
@@ -179,7 +178,7 @@ Example:
%
<% include('search-xml.html', rows=>$rows, opt=>\%opt ) %>
%
-% } else { # regular HTML
+% } else {
%
<% include('search-html.html',
type => $type,
@@ -205,6 +204,11 @@ my $curuser = $FS::CurrentUser::CurrentUser;
my $type = $cgi->param('_type') =~ /^(csv|\w*\.xls|xml|select|html(-print)?)$/
? $1 : 'html' ;
+if ( !$curuser->access_right('Download report data') ) {
+ $opt{'disable_download'} = 1;
+ $type = 'html';
+}
+
my %align = (
'l' => 'left',
'r' => 'right',
@@ -363,6 +367,8 @@ unless ( $type =~ /^(csv|\w*.xls)$/) {
$maxrecords ||= $confmax;
}
+ $opt{'disable_maxselect'} ||= $conf->exists('disable_maxselect');
+
$limit = $maxrecords ? "LIMIT $maxrecords" : '';
$offset = $cgi->param('offset') =~ /^(\d+)$/ ? $1 : 0;
diff --git a/httemplate/search/pay_batch.cgi b/httemplate/search/pay_batch.cgi
index b2a15ef3d..05415f36e 100755
--- a/httemplate/search/pay_batch.cgi
+++ b/httemplate/search/pay_batch.cgi
@@ -14,7 +14,8 @@
'Type',
'First Download',
'Last Upload',
- 'Item Count',
+ 'Items',
+ 'Unresolved',
'Amount',
'Status',
],
@@ -46,13 +47,16 @@
}
},
sub {
- my $st = "SELECT COUNT(*) from cust_pay_batch WHERE batchnum=" . shift->batchnum;
- my $sth = dbh->prepare($st)
- or die dbh->errstr. "doing $st";
- $sth->execute
- or die "Error executing \"$st\": ". $sth->errstr;
- $sth->fetchrow_arrayref->[0];
- },
+ FS::cust_pay_batch->count(
+ 'batchnum = '.$_[0]->batchnum
+ )
+ },
+ sub {
+ FS::cust_pay_batch->count(
+ 'status is null and batchnum = '.
+ $_[0]->batchnum
+ )
+ },
sub {
my $st = "SELECT SUM(amount) from cust_pay_batch WHERE batchnum=" . shift->batchnum;
my $sth = dbh->prepare($st)
diff --git a/httemplate/search/report_prepaid_income.cgi b/httemplate/search/prepaid_income.html
index 2fe5b6f10..ebac5a2a9 100644
--- a/httemplate/search/report_prepaid_income.cgi
+++ b/httemplate/search/prepaid_income.html
@@ -56,9 +56,11 @@
<BR>
<% $actual_label %><% $actual_label ? 'u' : 'U' %>nearned revenue
-is the amount of unearned revenue
+is the as-yet-unearned portion of revenue
<% $actual_label ? 'Freeside has actually' : '' %>
-invoiced for packages with longer-than monthly terms.
+invoiced for packages with
+<% $cgi->param('include_monthly') ? 'terms extending into the future.'
+ : 'longer-than monthly terms.' %>
% if ( $legacy ) {
<BR><BR>
@@ -82,13 +84,25 @@ my $actual_label = $legacy ? 'Actual ' : '';
#doesn't yet deal with daily/weekly packages
+my $mode = $cgi->param('mode');
+
my $time = time;
my $now = $cgi->param('date') && parse_datetime($cgi->param('date')) || $time;
$now =~ /^(\d+)$/ or die "unparsable date?";
$now = $1;
-my $link = "cust_bill_pkg.cgi?nottax=1;unearned_now=$now";
+my $dt = DateTime->from_epoch(epoch => $now, time_zone => 'local');
+$dt->truncate(to => 'day'); # local midnight on the report day
+$dt->add(days => 1); # the day after that
+$dt->subtract(seconds => 1); # the last second of the report day
+$now = $dt->epoch;
+
+my $link = "unearned_detail.html?date=$now;mode=$mode";
+
+if ( $cgi->param('include_monthly') ) {
+ $link .= ';include_monthly=1';
+}
my $curuser = $FS::CurrentUser::CurrentUser;
@@ -108,6 +122,13 @@ my @where = ();
#here is the agent virtualization
push @where, $curuser->agentnums_sql( 'table'=>'cust_main' );
+my $status = '';
+if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
+ $status = $1;
+ $link .= ";status=$status";
+ push @where, FS::cust_main->cust_status_sql . " = '$status'";
+}
+
my %total = ();
my %total_legacy = ();
foreach my $agentnum (@agentnums) {
@@ -117,66 +138,54 @@ foreach my $agentnum (@agentnums) {
my( $total, $total_legacy ) = ( 0, 0 );
- # my @cust_bill_pkg =
- # grep { $_->cust_pkg && $_->cust_pkg->part_pkg->freq !~ /^([01]|\d+[hdw])$/ }
- # qsearch({
- # 'select' => 'cust_bill_pkg.*',
- # 'table' => 'cust_bill_pkg',
- # 'addl_from' => ' LEFT JOIN cust_bill USING ( invnum ) '.
- # ' LEFT JOIN cust_main USING ( custnum ) ',
- # 'hashref' => {
- # 'recur' => { op=>'!=', value=>0 },
- # 'sdate' => { op=>'<', value=>$now },
- # 'edate' => { op=>'>', value=>$now },
- # },
- # 'extra_sql' => $where,
- # });
- #
- # foreach my $cust_bill_pkg ( @cust_bill_pkg) {
- # my $period = $cust_bill_pkg->edate - $cust_bill_pkg->sdate;
- #
- # my $elapsed = $now - $cust_bill_pkg->sdate;
- # $elapsed = 0 if $elapsed < 0;
- #
- # my $remaining = 1 - $elapsed/$period;
- #
- # my $unearned = $remaining * $cust_bill_pkg->recur;
- # $total += $unearned;
- #
- # }
-
- #re-written in sql:
-
- #false laziness w/cust_bill_pkg.cgi
-
- my $float = 'REAL'; #'DOUBLE PRECISION';
-
- my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS $float)";
- my $elapsed = "(CASE WHEN cust_bill_pkg.sdate > $now
- THEN 0
- ELSE ($now - cust_bill_pkg.sdate)
- END)";
- #my $elapsed = "CAST($unearned - cust_bill_pkg.sdate AS $float)";
-
- my $remaining = "(1 - $elapsed/$period)";
-
- my $select = "SUM($remaining * cust_bill_pkg.recur)";
-
- #[...]
-
- my $sql = "SELECT $select FROM cust_bill_pkg
- LEFT JOIN cust_pkg USING ( pkgnum )
- LEFT JOIN part_pkg USING ( pkgpart )
- LEFT JOIN cust_main USING ( custnum )
- WHERE pkgpart > 0
- AND sdate < $now
- AND edate > $now
- AND cust_bill_pkg.recur != 0
- AND part_pkg.freq != '0'
+ my @opt = ($now, '', setuprecur => 'recur', no_usage => 1);
+ # balance owed, recurring only, not including usage charges
+ my $unearned_base;
+ if ( $mode eq 'billed' ) {
+ $unearned_base = '( ' .
+ FS::cust_bill_pkg->charged_sql(@opt) . ' - ' .
+ FS::cust_bill_pkg->credited_sql(@opt) . ' )';
+ } elsif ( $mode eq 'paid' ) {
+ $unearned_base = FS::cust_bill_pkg->paid_sql(@opt);
+ }
+
+ my $edate_zero = midnight_sql('edate');
+ my $sdate_zero = midnight_sql('sdate');
+ my $period = "CAST( ($edate_zero - $sdate_zero) / 86400.0 AS DECIMAL(10,0) )";
+ my $remaining = "GREATEST(
+ CAST( ($edate_zero - $now) / 86400.0 AS DECIMAL(10,0) ),
+ 0)";
+ my $fraction = "$remaining / $period";
+
+ my $unearned_sql = "CAST(
+ GREATEST( $unearned_base * $fraction, 0 )
+ AS DECIMAL(10,2)
+ )";
+
+ my $select = "SUM( $unearned_sql )";
+
+ if ( !$cgi->param('include_monthly') ) {
+ # all except freq != 0; one-time charges should never be included
+ $where .= "
AND part_pkg.freq != '1'
AND part_pkg.freq NOT LIKE '%h'
AND part_pkg.freq NOT LIKE '%d'
- AND part_pkg.freq NOT LIKE '%w'
+ AND part_pkg.freq NOT LIKE '%w'";
+ }
+
+ # $mode actually doesn't matter here, since unpaid invoices have zero
+ # unearned revenue
+
+ my $sql =
+ "SELECT $select FROM cust_bill_pkg
+ LEFT JOIN cust_pkg ON (cust_bill_pkg.pkgnum = cust_pkg.pkgnum)
+ LEFT JOIN part_pkg USING ( pkgpart )
+ LEFT JOIN cust_main ON (cust_pkg.custnum = cust_main.custnum)
+ WHERE pkgpart > 0
+ AND cust_bill_pkg.sdate < $now
+ AND cust_bill_pkg.edate > $now
+ AND cust_bill_pkg.recur != 0
+ AND part_pkg.freq != '0'
$where
";
diff --git a/httemplate/search/report_cust_bill_pkg_referral.html b/httemplate/search/report_cust_bill_pkg_referral.html
new file mode 100644
index 000000000..1fbb13d4f
--- /dev/null
+++ b/httemplate/search/report_cust_bill_pkg_referral.html
@@ -0,0 +1,55 @@
+<% include('/elements/header.html', 'Sales Report with Advertising Source' ) %>
+
+<FORM ACTION="cust_bill_pkg_referral.html" METHOD="GET">
+
+<TABLE>
+
+<& /elements/tr-input-beginning_ending.html &>
+
+<& /elements/tr-select-agent.html,
+ 'label' => 'For agent: ',
+ 'disable_empty' => 0,
+ 'empty_label' => 'all',
+&>
+
+<& /elements/tr-select-pkg_class.html,
+ 'pre_options' => [ '' => 'all', '0' => '(empty class)' ],
+ 'disable_empty' => 1,
+&>
+
+<& /elements/tr-select-table.html,
+ 'label' => 'Report classes',
+ 'table' => 'part_pkg_report_option',
+ 'name_col' => 'name',
+ 'hashref' => { disabled => '' },
+ 'element_name' => 'report_option',
+ 'multiple' => 1,
+&>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="setup" VALUE="1" CHECKED></TD>
+ <TD>Show setup/one-time fees</TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="recur" VALUE="1" CHECKED></TD>
+ <TD>Show recurring fees</TD>
+</TR>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="usage" VALUE="1" CHECKED></TD>
+ <TD>Show usage charges</TD>
+</TR>
+
+</TABLE>
+
+<BR><INPUT TYPE="submit" VALUE="Display">
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+</%init>
diff --git a/httemplate/search/report_cust_main.html b/httemplate/search/report_cust_main.html
index 0ef5a5196..39cf695d8 100755
--- a/httemplate/search/report_cust_main.html
+++ b/httemplate/search/report_cust_main.html
@@ -25,6 +25,12 @@
'all_selected' => 1,
&>
+ <& /elements/tr-select-part_referral.html,
+ 'label' => emt('Advertising Source'),
+ 'multiple' => 1,
+ 'all_selected' => 1,
+ &>
+
<TR>
<TD ALIGN="right" VALIGN="center"><% mt('Address') |h %></TD>
<TD><INPUT TYPE="text" NAME="address" SIZE=54></TD>
@@ -42,6 +48,34 @@
</TD>
</TR>
+% if ( $conf->exists('cust_main-enable_birthdate') ) {
+ <TR>
+ <TD ALIGN="right" VALIGN="center"><% mt('Date of Birth') |h %></TD>
+ <TD>
+ <TABLE>
+ <& /elements/tr-input-beginning_ending.html,
+ prefix => 'birthdate',
+ layout => 'horiz',
+ &>
+ </TABLE>
+ </TD>
+ </TR>
+% }
+
+% if ( $conf->exists('cust_main-enable_spouse_birthdate') ) {
+ <TR>
+ <TD ALIGN="right" VALIGN="center"><% mt('Spouse Date of Birth') |h %></TD>
+ <TD>
+ <TABLE>
+ <& /elements/tr-input-beginning_ending.html,
+ prefix => 'spouse_birthdate',
+ layout => 'horiz',
+ &>
+ </TABLE>
+ </TD>
+ </TR>
+% }
+
<& /elements/tr-select-cust_tag.html,
'cgi' => $cgi,
'is_report' => 1,
@@ -101,11 +135,6 @@
&>
<TR>
- <TD ALIGN="right" VALIGN="center"><% mt('Include cancelled packages') |h %></TD>
- <TD><INPUT TYPE="checkbox" NAME="cancelled_pkgs"></TD>
- </TR>
-
- <TR>
<TD ALIGN="right" VALIGN="center"><% mt('Without census tract') |h %></TD>
<TD><INPUT TYPE="checkbox" NAME="no_censustract"></TD>
</TR>
@@ -132,6 +161,12 @@
<TD ALIGN="right" VALIGN="center"><% mt('Add package columns') |h %></TD>
<TD><INPUT TYPE="checkbox" NAME="flattened_pkgs"></TD>
</TR>
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center"><% mt('Include cancelled packages') |h %></TD>
+ <TD><INPUT TYPE="checkbox" NAME="cancelled_pkgs"></TD>
+ </TR>
+
</TABLE>
<BR>
@@ -143,9 +178,7 @@
<%init>
die "access denied"
- unless ( $FS::CurrentUser::CurrentUser->access_right('List customers') &&
- $FS::CurrentUser::CurrentUser->access_right('List packages')
- );
+ unless $FS::CurrentUser::CurrentUser->access_right('Advanced customer search');
my $conf = new FS::Conf;
diff --git a/httemplate/search/report_cust_pkg.html b/httemplate/search/report_cust_pkg.html
index 3da59c2ac..e47bbb1e5 100755
--- a/httemplate/search/report_cust_pkg.html
+++ b/httemplate/search/report_cust_pkg.html
@@ -88,23 +88,35 @@
%>
% }
-
+ <TR>
+ <TD COLSPAN=2>
+ <TABLE>
+ <TR>
+ <TD></TD>
+ <TD>From date <i>(m/d/y)</i></TD>
+ <TD>To date <i>(m/d/y)</i></TD>
+ </TR>
+% my $noinit = 0;
% foreach my $field (@date_fields) {
- <TR>
- <TD ALIGN="right" VALIGN="center"><% $label{$field} %></TD>
- <TD>
- <TABLE>
- <% include( '/elements/tr-input-beginning_ending.html',
- prefix => $field,
- layout => 'horiz',
- )
- %>
- </TABLE>
- </TD>
- </TR>
-
-% }
+ <TR>
+ <TD ALIGN="right" VALIGN="center"><% $label{$field} %></TD>
+% foreach (qw(beginning ending)) {
+ <TD>
+ <& /elements/input-date-field.html, {
+ 'name' => $field.'_'.$_,
+ 'value' => '',
+ 'noinit' => $noinit,
+ 'format' => '%m/%d/%Y',
+ } &>
+ </TD>
+% $noinit = 1;
+% }
+ </TR>
+% } #foreach $field
+ </TABLE>
+ </TD>
+ </TR>
<SCRIPT TYPE="text/javascript">
@@ -186,6 +198,7 @@ tie my %label, 'Tie::IxHash',
'dundate' => 'Suspension delayed until',
'expire' => 'Expires',
'contract_end' => 'Contract ends',
+ 'change_date' => 'Changed from other package',
'cancel' => 'Cancelled',
;
my @date_fields = keys %label;
diff --git a/httemplate/search/report_customer_accounting_summary.html b/httemplate/search/report_customer_accounting_summary.html
index d20f756c8..f2a13a27b 100755
--- a/httemplate/search/report_customer_accounting_summary.html
+++ b/httemplate/search/report_customer_accounting_summary.html
@@ -11,6 +11,14 @@
)
%>
+ <% include( '/elements/tr-select-part_referral.html',
+ 'curr_value' => scalar( $cgi->param('refnum') ),
+ 'label' => 'Advertising source ',
+ 'disable_empty' => 0,
+ 'empty_label' => 'all',
+ )
+ %>
+
<% include('/elements/tr-select-from_to.html' ) %>
<% include( '/elements/tr-select-cust_main-status.html',
diff --git a/httemplate/search/report_prepaid_income.html b/httemplate/search/report_prepaid_income.html
index 061b24c68..4743e2d21 100644
--- a/httemplate/search/report_prepaid_income.html
+++ b/httemplate/search/report_prepaid_income.html
@@ -2,7 +2,7 @@
<% include('/elements/init_calendar.html') %>
-<FORM ACTION="report_prepaid_income.cgi" METHOD="GET">
+<FORM ACTION="prepaid_income.html" METHOD="GET">
<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
@@ -13,7 +13,7 @@
</TR>
<TR>
- <TD>As of </TD>
+ <TD ALIGN="right">As of </TD>
<TD>
<INPUT TYPE="text" NAME="date" ID="date_text" VALUE="now">
<IMG SRC="../images/calendar.png" ID="date_button" STYLE="cursor: pointer" TITLE="Select date">
@@ -30,7 +30,20 @@
</TR>
<% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %>
-
+
+ <& /elements/tr-select-cust_main-status.html,
+ label => mt('Customer Status') &>
+ <& /elements/tr-select.html,
+ label => 'Invoice Status',
+ field => 'mode',
+ options => [ qw(billed paid) ] &>
+ <TR>
+ <TD ALIGN="right">
+ <INPUT TYPE="checkbox" NAME="include_monthly" VALUE=1>
+ </TD>
+ <TD ALIGN="left"><% mt('Include packages with period &le; 1 month') %>
+ </TD>
+ </TR>
<TR>
<TD COLSPAN=2>&nbsp;</TD>
</TR>
diff --git a/httemplate/search/report_receivables.html b/httemplate/search/report_receivables.html
index 19f863525..5cff0f4fc 100755
--- a/httemplate/search/report_receivables.html
+++ b/httemplate/search/report_receivables.html
@@ -52,7 +52,7 @@ function toggle(obj) {
<& /elements/tr-input-date-field.html, {
'name' => 'as_of',
'value' => time,
- 'label' => emt('As of date '),
+ 'label' => emt('At the end of date '),
'format' => FS::Conf->new->config('date_format') || '%m/%d/%Y',
}
&>
diff --git a/httemplate/search/report_rt_ticket.html b/httemplate/search/report_rt_ticket.html
index 79a601b4b..f0d7a4200 100644
--- a/httemplate/search/report_rt_ticket.html
+++ b/httemplate/search/report_rt_ticket.html
@@ -6,10 +6,20 @@
<% include ( '/elements/tr-input-beginning_ending.html' ) %>
+ <& /elements/tr-td-label.html, label => 'Time category:' &>
+ <TD>
+ <& /elements/select-rt-customfield.html,
+ name => 'cfname',
+ lookuptype => 'RT::Transaction',
+ valuetype => 'TimeValue',
+ empty_label => 'Worked',
+ &>
+ </TD></TR>
+
<% include ( '/elements/tr-select-otaker.html' ) %>
<TR>
- <TD>Account</TD>
+ <TD ALIGN="right">Account:</TD>
<TD>
<SELECT NAME="svcnum">
<OPTION VALUE="">(all)
@@ -48,4 +58,24 @@ if ( @pkgparts ) {
});
}
+# get a list of TimeValue-type custom fields
+RT::Init();
+my $CurrentUser = RT::CurrentUser->new();
+$CurrentUser->LoadByName($FS::CurrentUser::CurrentUser->username);
+die "RT not configured" unless $CurrentUser->id;
+my $CFs = RT::CustomFields->new($CurrentUser);
+
+$CFs->Limit(FIELD => 'LookupType',
+ OPERATOR => 'ENDSWITH',
+ VALUE => 'RT::Transaction');
+
+$CFs->Limit(FIELD => 'Type',
+ VALUE => 'TimeValue');
+
+my @time_fields = ('', 'Worked');
+while (my $CF = $CFs->Next) {
+ push @time_fields, $CF->Name, ($CF->Description || $CF->Name);
+}
+
+
</%init>
diff --git a/httemplate/search/report_rt_transaction.html b/httemplate/search/report_rt_transaction.html
index 0232b8070..b8454d968 100644
--- a/httemplate/search/report_rt_transaction.html
+++ b/httemplate/search/report_rt_transaction.html
@@ -6,6 +6,17 @@
<% include ( '/elements/tr-input-beginning_ending.html' ) %>
+ <& /elements/tr-td-label.html, label => 'Time category:' &>
+ <TD>
+ <& /elements/select-rt-customfield.html,
+ name => 'cfname',
+ lookuptype => 'RT::Transaction',
+ valuetype => 'TimeValue',
+ empty_label => 'Worked',
+ &>
+ </TD></TR>
+
+
<% include ( '/elements/tr-select-otaker.html' ) %>
<% include ( '/elements/tr-input-text.html',
@@ -15,7 +26,7 @@
%>
<TR>
- <TD>Account</TD>
+ <TD ALIGN="right">Account:</TD>
<TD>
<SELECT NAME="svcnum">
<OPTION VALUE="">(all)
diff --git a/httemplate/search/report_svc_acct.html b/httemplate/search/report_svc_acct.html
index 14c284fb7..74bf5538e 100755
--- a/httemplate/search/report_svc_acct.html
+++ b/httemplate/search/report_svc_acct.html
@@ -114,7 +114,7 @@
<%init>
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('List packages'); #?
+ unless $FS::CurrentUser::CurrentUser->access_right('Services: Accounts: Advanced search'); #?
my $title = emt('Account Report');
diff --git a/httemplate/search/report_svc_broadband.html b/httemplate/search/report_svc_broadband.html
index 37f21b79f..d7422eeae 100755
--- a/httemplate/search/report_svc_broadband.html
+++ b/httemplate/search/report_svc_broadband.html
@@ -76,7 +76,7 @@
<%init>
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('List packages'); #?
+ unless $FS::CurrentUser::CurrentUser->access_right('Services: Wireless broadband services: Advanced search');
my $title = 'Broadband Service Report';
my $routernum = [ $cgi->param('routernum') || '' ];
diff --git a/httemplate/search/report_svc_hardware.html b/httemplate/search/report_svc_hardware.html
index 61ba4ab8a..b0bfc08c7 100755
--- a/httemplate/search/report_svc_hardware.html
+++ b/httemplate/search/report_svc_hardware.html
@@ -61,7 +61,7 @@ OR (SELECT COUNT(*) FROM svc_hardware
<%init>
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('List packages'); #?
+ unless $FS::CurrentUser::CurrentUser->access_right('Services: Hardware: Advanced search');
my $title = 'Hardware Service Report';
diff --git a/httemplate/search/report_tax.cgi b/httemplate/search/report_tax.cgi
index 0cd652d83..2786f571b 100755
--- a/httemplate/search/report_tax.cgi
+++ b/httemplate/search/report_tax.cgi
@@ -239,6 +239,8 @@ as <A HREF="<% $p.'search/report_tax-xls.cgi?'.$cgi->query_string%>">Excel sprea
<%init>
+my $DEBUG = $cgi->param('debug') || 0;
+
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
@@ -252,15 +254,19 @@ my $join_cust = ' JOIN cust_bill USING ( invnum )
LEFT JOIN cust_main USING ( custnum ) ';
my $join_cust_pkg = $join_cust.
' LEFT JOIN cust_pkg USING ( pkgnum )
- LEFT JOIN part_pkg USING ( pkgpart ) ';
-$join_cust_pkg .= ' LEFT JOIN cust_location USING ( locationnum )'
- if $conf->exists('tax-pkg_address');
+ LEFT JOIN part_pkg USING ( pkgpart )
+ LEFT JOIN cust_location
+ ON ( cust_location.locationnum = ' .
+ FS::cust_pkg->tax_locationnum_sql . ' )';
my $from_join_cust_pkg = " FROM cust_bill_pkg $join_cust_pkg ";
my $where = "WHERE _date >= $beginning AND _date <= $ending ";
-my( $location_sql, @base_param ) = FS::cust_pkg->location_sql;
+# this query will be run once per cust_main_county,
+# or maybe once per country/state/city tuple,
+# or maybe once per country/state...it's hard to say.
+my ($location_sql, @base_param) = FS::cust_location->in_county_sql(param => 1);
$where .= " AND $location_sql ";
my $agentname = '';
@@ -275,7 +281,10 @@ sub gotcust {
my $table = shift;
my $prefix = @_ ? shift : '';
"
- ( $table.${prefix}city = cust_main_county.city
+ ( $table.district = cust_main_county.district
+ OR cust_main_county.district = ''
+ OR cust_main_county.district IS NULL )
+ AND ( $table.${prefix}city = cust_main_county.city
OR cust_main_county.city = ''
OR cust_main_county.city IS NULL )
AND ( $table.${prefix}county = cust_main_county.county
@@ -288,59 +297,29 @@ sub gotcust {
";
}
-my $gotcust;
-if ( $conf->exists('tax-ship_address') ) {
-
- $gotcust = "
- ( cust_main_county.country = cust_main.country
- OR cust_main_county.country = cust_main.ship_country
- )
-
- AND
-
- (
- ( ( ship_last IS NULL OR ship_last = '' )
- AND ". gotcust('cust_main'). "
- )
- OR
- ( ship_last IS NOT NULL AND ship_last != ''
- AND ". gotcust('cust_main', 'ship_'). "
- )
- )
- ";
-
-} else {
-
- $gotcust = gotcust('cust_main');
-
-}
-if ( $conf->exists('tax-pkg_address') ) {
- $gotcust = "
- ( cust_pkg.locationnum IS NULL AND $gotcust)
- OR ( cust_pkg.locationnum IS NOT NULL AND ". gotcust('cust_location'). " )";
- $gotcust =
- "WHERE 0 < ( SELECT COUNT(*) FROM cust_pkg
- LEFT JOIN cust_main USING ( custnum )
- LEFT JOIN cust_location USING ( locationnum )
- WHERE $gotcust
- LIMIT 1
- )
- ";
-} else {
- $gotcust =
- "WHERE 0 < ( SELECT COUNT(*) FROM cust_main WHERE $gotcust LIMIT 1 )";
-}
+#non-parameterized form
+my $location_in_county = FS::cust_location->in_county_sql;
+my $gotcust = "WHERE EXISTS(
+ SELECT 1 FROM cust_location WHERE $location_in_county AND disabled IS NULL
+)";
my $out = 'Out of taxable region(s)';
+# these are actually tax labels, not regions
my %regions = ();
+# Phase 1: Taxable and exempt sales
+# Collect for each cust_main_county, and assign to a bin based on label.
+# Note that "label" includes city if show_cities is on, and taxclass if
+# show_taxclasses is on.
foreach my $r ( qsearch({ 'table' => 'cust_main_county',
'extra_sql' => $gotcust,
+ 'debug' => $DEBUG,
})
)
{
- #warn $r->county. ' '. $r->state. ' '. $r->country. "\n";
+ warn $r->county. ' '. $r->state. ' '. $r->country. "\n" if $DEBUG > 1;
+ # set up a %regions entry for this region's tax label
my $label = getlabel($r);
$regions{$label}->{'label'} = $label;
@@ -366,6 +345,7 @@ foreach my $r ( qsearch({ 'table' => 'cust_main_county',
} else {
+ # SQL for "taxclass doesn't match any other tax in the region"
my $same_sql = $r->sql_taxclass_sameregion;
$mywhere .= " AND $same_sql" if $same_sql;
@@ -375,42 +355,24 @@ foreach my $r ( qsearch({ 'table' => 'cust_main_county',
}
+ # FROM cust_bill_pkg JOIN (whatever is needed to determine tax location)
+ # WHERE (matches tax location and agentnum and taxclass)
+ # takes parameters in @base_param, plus taxclass if there is one
my $fromwhere = "$from_join_cust_pkg $mywhere"; # AND payby != 'COMP' ";
-# my $label = getlabel($r);
-# $regions{$label}->{'label'} = $label;
-
my $nottax = 'pkgnum != 0';
- ## calculate total for this region
+ ## calculate total of sales (non-tax line items) for this region
my $t_sql =
"SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur) $fromwhere AND $nottax";
my $t = scalar_sql($r, \@param, $t_sql);
$regions{$label}->{'total'} += $t;
- #if ( $label eq $out ) # && $t ) {
- # warn "adding $t for ".
- # join('/', map $r->$_, qw( taxclass county state country ) ). "\n";
- # #warn $t_sql if $r->state eq 'FL';
- #}
+ #$regions{$label}->{subtotals}->{$r->taxnum} = $t; #useful debug
## calculate customer-exemption for this region
-## my $taxable = $t;
-
-# my($taxable, $x_cust) = (0, 0);
-# foreach my $e ( grep { $r->get($_.'tax') !~ /^Y/i }
-# qw( cust_bill_pkg.setup cust_bill_pkg.recur ) ) {
-# $taxable += scalar_sql($r, \@param,
-# "SELECT SUM($e) $fromwhere AND $nottax AND ( tax != 'Y' OR tax IS NULL )"
-# );
-#
-# $x_cust += scalar_sql($r, \@param,
-# "SELECT SUM($e) $fromwhere AND $nottax AND tax = 'Y'"
-# );
-# }
-
#false laziness -ish w/report_tax.cgi
my $cust_exempt;
if ( $r->taxname ) {
@@ -486,10 +448,12 @@ foreach my $r ( qsearch({ 'table' => 'cust_main_county',
} else {
$regions{$label}->{'rate'} = $r->tax.'%';
}
-
}
+warn Dumper(\%regions) if $DEBUG > 1;
+# $regions{$label} now contains 'total', 'exempt_cust', 'exempt_pkg',
+# 'exempt_monthly', summed over each set of regions with the same label.
-my $distinct = "country, state, county, city,
+my $distinct = "country, state, county, city, district,
CASE WHEN taxname IS NULL THEN '' ELSE taxname END AS taxname";
my $taxclass_distinct =
#a little bit unsure of this part... test?
@@ -501,38 +465,44 @@ my $taxclass_distinct =
)." AS taxclass";
+# Phase 2: invoiced/credited tax items
+# Collect this data for each country/state/city/district/taxname(/taxclass).
my %qsearch = (
'select' => "DISTINCT $distinct, $taxclass_distinct",
'table' => 'cust_main_county',
'hashref' => {},
'extra_sql' => $gotcust,
+ 'debug' => $DEBUG,
);
-my $taxfromwhere = " FROM cust_bill_pkg $join_cust ";
+# Join to cust_main the same as before (we need agentnum)
+# but not to cust_pkg (because tax line items don't have a package)
+# and then to cust_location via cust_bill_pkg_tax_location
+my $taxfromwhere = "FROM cust_bill_pkg $join_cust
+ LEFT JOIN cust_bill_pkg_tax_location USING ( billpkgnum )
+ LEFT JOIN cust_location USING ( locationnum )
+ ";
my $taxwhere = $where;
-if ( $conf->exists('tax-pkg_address') ) {
-
- $taxfromwhere .= 'LEFT JOIN cust_bill_pkg_tax_location USING ( billpkgnum )
- LEFT JOIN cust_location USING ( locationnum ) ';
- #quelle kludge
- $taxwhere =~ s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g;
-
-}
my $creditfromwhere = $taxfromwhere.
- " JOIN cust_credit_bill_pkg USING (billpkgnum";
-$creditfromwhere .= " ,billpkgtaxlocationnum"
- if $conf->exists('tax-pkg_address');
-$creditfromwhere .= ")";
+ " JOIN cust_credit_bill_pkg USING (billpkgnum, billpkgtaxlocationnum)";
$taxfromwhere .= " $taxwhere "; #AND payby != 'COMP' ";
$creditfromwhere .= " $taxwhere AND billpkgtaxratelocationnum IS NULL"; #AND payby != 'COMP' ";
-my @taxparam = @base_param;
+#should i be a cust_main_county method or something
+# yes. yes, you should.
+
+# $taxfromwhere: Most of a query to find cust_bill_pkg records linked to a
+# customer matching a given state/county/city/district (and within the date
+# range for the report).
+# @base_param: A list of the fields from cust_main_county to use as parameters.
+# $_taxamount_sub: Takes a cust_main_county and returns the sum of taxes billed
+# within the report period for all customers located in that county. If
+# the cust_main_county has a taxname, limits to taxes with that name; otherwise
+# includes all line items with pkgnum = 0 and description either 'Tax' or empty.
-#should i be a cust_main_county method or something
-#need to pass in $taxfromwhere & @taxparam???
my $_taxamount_sub = sub {
my $r = shift;
@@ -545,9 +515,11 @@ my $_taxamount_sub = sub {
my $sql = "SELECT SUM(cust_bill_pkg.setup+cust_bill_pkg.recur) ".
" $taxfromwhere AND cust_bill_pkg.pkgnum = 0 $named_tax";
- scalar_sql($r, \@taxparam, $sql );
+ scalar_sql($r, [ @base_param ], $sql );
};
+# $_creditamount_sub: As above, but returns the sum of credits applied
+
my $_creditamount_sub = sub {
my $r = shift;
@@ -560,7 +532,7 @@ my $_creditamount_sub = sub {
my $sql = "SELECT SUM(cust_credit_bill_pkg.amount) ".
" $creditfromwhere AND cust_bill_pkg.pkgnum = 0 $named_tax";
- scalar_sql($r, \@taxparam, $sql );
+ scalar_sql($r, [ @base_param ], $sql );
};
#tax-report_groups filtering
@@ -611,6 +583,10 @@ foreach my $r ( qsearch(\%qsearch) ) {
}
+# Phase 3: Non-taxclassed totals for invoiced/credited tax
+# (If show_taxclasses is not in use, this was phase 2, but it
+# displays somewhere different.)
+# Don't filter by report_groups.
my %base_regions = ();
if ( $cgi->param('show_taxclasses') ) {
@@ -734,7 +710,8 @@ sub getlabel {
my $label;
if (
$r->tax == 0
- && ! scalar( qsearch('cust_main_county', { 'city' => $r->city,
+ && ! scalar( qsearch('cust_main_county', { 'district'=> $r->district,
+ 'city' => $r->city,
'county' => $r->county,
'state' => $r->state,
'country' => $r->country,
@@ -747,10 +724,6 @@ sub getlabel {
#kludge to avoid "will not stay shared" warning
my $out = 'Out of taxable region(s)';
$label = $out;
-# } elsif ( $r->taxname && count_taxname($r->taxname) == 1 ) {
-# $label = $r->taxname;
-## $regions{$label}->{'taxname'} = $label;
-## push @{$regions{$label}->{$_}}, $r->$_() foreach qw( county state country );
} else {
$label = $r->country;
$label = $r->state.", $label" if $r->state;
diff --git a/httemplate/search/rt_ticket.html b/httemplate/search/rt_ticket.html
index abe13157d..1ed5a3883 100644
--- a/httemplate/search/rt_ticket.html
+++ b/httemplate/search/rt_ticket.html
@@ -3,25 +3,27 @@
'name_singular' => 'ticket',
'query' => $query,
'count_query' => $count_query,
- 'count_addl' => [ $format_seconds_sub, $format_seconds_sub, ],
+ 'count_addl' => [ $format_seconds_sub,
+ $applied_time ? $format_seconds_sub : () ],
'header' => [ 'Ticket #',
'Ticket',
'Time',
- 'Applied',
+ $applied_time ? 'Applied' : (),
],
'fields' => [ 'ticketid',
sub { encode_entities(shift->get('subject')) },
- sub { my $seconds = shift->get('transaction_time');
+ sub { my $seconds = shift->get('ticket_time');
&{ $format_seconds_sub }( $seconds );
},
- sub { my $seconds = shift->get('support');
+ ($applied_time ?
+ sub { my $seconds = shift->get('applied_time');
&{ $format_seconds_sub }( $seconds );
- },
+ } : () ),
],
'sort_fields' => [ 'ticketid',
'subject',
'transaction_time',
- 'support_time',
+ $applied_time ? 'applied_time' : (),
],
'links' => [
$link,
@@ -48,34 +50,87 @@ $seconds)%3600)/60)."m";
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+local $FS::Record::nowarn_classload = 1;
+
#some amount of false laziness w/timeworked.html...
-my $transactiontime = "
- CASE transactions.type when 'Set'
- THEN (to_number(newvalue,'999999')-to_number(oldvalue, '999999')) * 60
- ELSE timetaken*60
- END
-";
+my @select = (
+ 'Tickets.Id AS ticketid',
+ 'Tickets.Subject'
+);
+my @select_total = ( 'COUNT(*)' );
+my ($transaction_time, $applied_time);
my $join = 'JOIN Users ON Transactions.Creator = Users.Id '; #.
-# 'LEFT JOIN acct_rt_transaction '.
-# ' ON Transactions.Id = acct_rt_transaction.transaction_id';
my $twhere = "
- WHERE objecttype='RT::Ticket'
- AND Transactions.ObjectId = Tickets.Id
+ WHERE Transactions.ObjectType = 'RT::Ticket'
+ AND Transactions.ObjectId = Tickets.Id
+";
+
+my $cfname = '';
+if ( $cgi->param('cfname') =~ /^\w(\w|\s)*$/ ) {
+
+ $cfname = $cgi->param('cfname');
+
+ $transaction_time = "(CASE Transactions.Type
+ WHEN 'CustomField' THEN
+ ( coalesce(to_number(ocfv_new.Content,'999999'),0)
+ - coalesce(to_number(ocfv_old.Content,'999999'),0) )
+ ELSE ( to_number(ocfv_main.Content,'999999') )
+ END) * 60";
+
+ $join .= "
+ LEFT JOIN ObjectCustomFieldValues ocfv_new
+ ON ( ocfv_new.Id = Transactions.NewReference )
+ LEFT JOIN ObjectCustomFieldValues ocfv_old
+ ON ( ocfv_old.Id = Transactions.OldReference )
+ LEFT JOIN ObjectCustomFieldValues ocfv_main
+ ON ( ocfv_main.ObjectType = 'RT::Transaction'
+ AND ocfv_main.ObjectId = Transactions.Id )
+ JOIN CustomFields
+ ON ( ( CustomFields.LookupType = 'RT::Queue-RT::Ticket-RT::Transaction'
+ AND CustomFields.Id = ocfv_main.CustomField
+ AND ocfv_main.Id IS NOT NULL
+ )
+ OR
+ ( CustomFields.LookupType = 'RT::Queue-RT::Ticket'
+ AND (CustomFields.Id = ocfv_new.CustomField OR ocfv_new.Id IS NULL)
+ AND (CustomFields.Id = ocfv_old.CustomField OR ocfv_old.Id IS NULL)
+ AND ocfv_main.Id IS NULL
+ ) )
+ ";
+
+ $twhere .= " AND CustomFields.Name = '$cfname'
+ AND (ocfv_new.Id IS NOT NULL OR ocfv_old.Id IS NOT NULL OR ocfv_main.Id IS NOT NULL)";
+
+}
+else {
+ $transaction_time = "
+ CASE transactions.type when 'Set'
+ THEN (to_number(newvalue,'999999')-to_number(oldvalue, '999999')) * 60
+ ELSE timetaken*60
+ END";
+
+ my $applied = '';
+ if ( $cgi->param('svcnum') =~ /^\s*(\d+)\s*$/ ) {
+ $twhere .= " AND EXISTS( SELECT 1 FROM acct_rt_transaction WHERE acct_rt_transaction.transaction_id = Transactions.id AND svcnum = $1 )";
+ $applied = "AND svcnum = $1";
+ }
+
+ $twhere .= "
AND ( ( Transactions.Type = 'Set'
AND Transactions.Field = 'TimeWorked'
AND Transactions.NewValue != Transactions.OldValue )
OR ( ( Transactions.Type='Create' OR Transactions.Type='Comment' OR Transactions.Type='Correspond' OR Transactions.Type='Touch' )
AND Transactions.TimeTaken > 0
)
- )
-";
-#AND transaction_time != 0
-#AND $wheretimeleft
+ )";
+
+ $applied_time = "( SELECT SUM(support) FROM acct_rt_transaction LEFT JOIN Transactions ON ( transaction_id = Id ) $twhere $applied )";
+
+}
-my $support = '';
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
# TIMESTAMP is Pg-specific... ?
@@ -92,25 +147,21 @@ if ( $cgi->param('otaker') && $cgi->param('otaker') =~ /^([\w\.\-]+)$/ ) {
$twhere .= " AND Users.name = '$1' ";
}
-if ( $cgi->param('svcnum') =~ /^\s*(\d+)\s*$/ ) {
- $twhere .= " AND EXISTS( SELECT 1 FROM acct_rt_transaction WHERE acct_rt_transaction.transaction_id = Transactions.id AND svcnum = $1 )";
- $support = "AND svcnum = $1";
-}
-
my $transactions = "FROM Transactions $join $twhere";
my $where = "WHERE EXISTS ( SELECT 1 $transactions )";
-my $transaction_time = "( SELECT SUM($transactiontime) $transactions )";
-my $support_time = "( SELECT SUM(support) FROM acct_rt_transaction LEFT JOIN Transactions ON ( transaction_id = Id ) $twhere $support )";
+my $ticket_time = "( SELECT SUM($transaction_time) $transactions )";
+push @select, "$ticket_time AS ticket_time";
+push @select_total, "SUM($ticket_time)";
+
+if ( $applied_time) {
+ push @select, "$applied_time AS applied_time";
+ push @select_total, "SUM($applied_time)";
+}
my $query = {
- 'select' => join(', ',
- 'Tickets.Id AS ticketid',
- 'Tickets.Subject',
- "$transaction_time AS transaction_time",
- "$support_time AS support",
- ),
+ 'select' => join(', ', @select),
'table' => 'tickets', #Pg-ism
#'table' => 'Tickets',
'addl_from' => '', #$join,
@@ -118,13 +169,9 @@ my $query = {
'order by' => 'ORDER BY Created',
};
-my $count_query =
+my $count_query = "SELECT ".join(', ', @select_total)." FROM Tickets $where";
#"SELECT COUNT(*), SUM($transactiontime), SUM(acct_rt_transaction.support) FROM Transactions $join $where";
#"SELECT COUNT(*), ( SUM($transactiontime) $transactions ) FROM Tickets"; # $join $where";
- "SELECT COUNT(*),
- SUM( $transaction_time ),
- SUM( $support_time )
- FROM Tickets $where"; # $join $where";
my $link = [ "${p}rt/Ticket/Display.html?id=", sub { shift->get('ticketid'); } ];
diff --git a/httemplate/search/rt_transaction.html b/httemplate/search/rt_transaction.html
index be9cd0bc9..1ae607be1 100644
--- a/httemplate/search/rt_transaction.html
+++ b/httemplate/search/rt_transaction.html
@@ -3,12 +3,13 @@
'name_singular' => 'transaction',
'query' => $query,
'count_query' => $count_query,
- 'count_addl' => [ $format_seconds_sub, $format_seconds_sub, ],
+ 'count_addl' => [ $format_seconds_sub,
+ $applied_time ? $format_seconds_sub : () ],
'header' => [ 'Ticket #',
'Ticket',
'Date',
'Time',
- 'Applied',
+ $applied_time ? 'Applied' : (),
],
'fields' => [ 'ticketid',
sub { encode_entities(shift->get('subject')) },
@@ -16,9 +17,10 @@
sub { my $seconds = shift->get('transaction_time');
&{ $format_seconds_sub }( $seconds );
},
- sub { my $seconds = shift->get('support');
+ ($applied_time ?
+ sub { my $seconds = shift->get('applied_time');
&{ $format_seconds_sub }( $seconds );
- },
+ } : () ),
],
'links' => [
$link,
@@ -44,21 +46,81 @@ $seconds)%3600)/60)."m";
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
+local $FS::Record::nowarn_classload = 1;
#some amount of false laziness w/timeworked.html...
-my $transactiontime = "
- CASE Transactions.Type when 'Set'
- THEN (to_number(NewValue,'999999')-to_number(OldValue, '999999')) * 60
- ELSE TimeTaken*60
- END
-";
+my @select = (
+ 'Transactions.*',
+ 'Tickets.Id AS ticketid',
+ 'Tickets.Subject',
+ 'Users.name AS otaker',
+);
+my @select_total = ( 'COUNT(*)' );
+my ($transaction_time, $applied_time);
my $join = 'JOIN Tickets ON Transactions.ObjectId = Tickets.Id '.
'JOIN Users ON Transactions.Creator = Users.Id '; #.
-# 'LEFT JOIN acct_rt_transaction '.
-# ' ON Transactions.Id = acct_rt_transaction.transaction_id';
-my $where = "
- WHERE objecttype='RT::Ticket'
+
+my $where = "WHERE Transactions.ObjectType = 'RT::Ticket'";
+
+my $cfname = '';
+if ( $cgi->param('cfname') =~ /^\w(\w|\s)*$/ ) {
+
+ # a TimeValue-type custom field
+ $cfname = $cgi->param('cfname');
+
+ $transaction_time = "(CASE Transactions.Type
+ WHEN 'CustomField' THEN
+ ( coalesce(to_number(ocfv_new.Content,'999999'),0)
+ - coalesce(to_number(ocfv_old.Content,'999999'),0) )
+ ELSE ( to_number(ocfv_main.Content,'999999') )
+ END) * 60";
+
+ # complicated because we have to deal with the case of editing the
+ # ticket custom field directly (OldReference/NewReference) as well as
+ # entering a transaction with a custom field value (ObjectId)
+ $join .= "
+ LEFT JOIN ObjectCustomFieldValues ocfv_new
+ ON ( ocfv_new.Id = Transactions.NewReference )
+ LEFT JOIN ObjectCustomFieldValues ocfv_old
+ ON ( ocfv_old.Id = Transactions.OldReference )
+ LEFT JOIN ObjectCustomFieldValues ocfv_main
+ ON ( ocfv_main.ObjectType = 'RT::Transaction'
+ AND ocfv_main.ObjectId = Transactions.Id )
+ JOIN CustomFields
+ ON ( ( CustomFields.LookupType = 'RT::Queue-RT::Ticket-RT::Transaction'
+ AND CustomFields.Id = ocfv_main.CustomField
+ AND ocfv_main.Id IS NOT NULL
+ )
+ OR
+ ( CustomFields.LookupType = 'RT::Queue-RT::Ticket'
+ AND (CustomFields.Id = ocfv_new.CustomField OR ocfv_new.Id IS NULL)
+ AND (CustomFields.Id = ocfv_old.CustomField OR ocfv_old.Id IS NULL)
+ AND ocfv_main.Id IS NULL
+ ) )
+ ";
+
+ $where .= " AND CustomFields.Name = '$cfname'
+ AND (ocfv_new.Id IS NOT NULL OR ocfv_old.Id IS NOT NULL OR ocfv_main.Id IS NOT NULL)";
+
+}
+else {
+
+ # the intrinsic TimeWorked/TimeTaken fields
+ $transaction_time = "CASE Transactions.Type when 'Set'
+ THEN (to_number(NewValue,'999999')-to_number(OldValue, '999999')) * 60
+ ELSE TimeTaken*60
+ END";
+
+ my $applied = '';
+ if ( $cgi->param('svcnum') =~ /^\s*(\d+)\s*$/ ) {
+ $where .= " AND EXISTS( SELECT 1 FROM acct_rt_transaction WHERE acct_rt_transaction.transaction_id = Transactions.id AND svcnum = $1 )";
+ $applied = "AND svcnum = $1";
+ }
+
+ $applied_time = "( SELECT SUM(support) from acct_rt_transaction where transaction_id = Transactions.id $applied )";
+
+ $where .= "
AND ( ( Transactions.Type = 'Set'
AND Transactions.Field = 'TimeWorked'
AND Transactions.NewValue != Transactions.OldValue )
@@ -66,11 +128,17 @@ my $where = "
AND Transactions.TimeTaken > 0
)
)
-";
+ ";
+
+}
#AND transaction_time != 0
#AND $wheretimeleft
-
-my $support = '';
+push @select, "($transaction_time) AS transaction_time";
+push @select_total, "SUM($transaction_time)";
+if ( $applied_time ) {
+ push @select, "($applied_time) AS applied_time";
+ push @select_total, "SUM($applied_time)";
+}
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
# TIMESTAMP is Pg-specific... ?
@@ -91,22 +159,8 @@ if ( $cgi->param('ticketid') =~ /^\s*(\d+)\s*$/ ) {
$where .= " AND Tickets.Id = $1";
}
-if ( $cgi->param('svcnum') =~ /^\s*(\d+)\s*$/ ) {
- $where .= " AND EXISTS( SELECT 1 FROM acct_rt_transaction WHERE acct_rt_transaction.transaction_id = Transactions.id AND svcnum = $1 )";
- $support = "AND svcnum = $1";
-}
-
-my $support_time = "( SELECT SUM(support) from acct_rt_transaction where transaction_id = Transactions.id $support )";
-
my $query = {
- 'select' => join(', ',
- 'Transactions.*',
- 'Tickets.Id AS ticketid',
- 'Tickets.Subject',
- 'Users.name AS otaker',
- "$transactiontime AS transaction_time",
- "$support_time AS support",
- ),
+ 'select' => join(', ', @select),
'table' => 'transactions', #Pg-ism
#'table' => 'Transactions',
'addl_from' => $join,
@@ -114,12 +168,7 @@ my $query = {
'order by' => 'ORDER BY Created',
};
-my $count_query =
- #"SELECT COUNT(*), SUM($transactiontime), SUM(acct_rt_transaction.support) FROM Transactions $join $where";
- "SELECT COUNT(*),
- SUM($transactiontime),
- SUM($support_time)
- FROM Transactions $join $where";
+my $count_query = 'SELECT '.join(', ', @select_total). " FROM Transactions $join $where";
my $link = [ "${p}rt/Ticket/Display.html?id=", sub { shift->get('ticketid'); } ];
diff --git a/httemplate/search/unearned_detail.html b/httemplate/search/unearned_detail.html
new file mode 100644
index 000000000..02d514cbe
--- /dev/null
+++ b/httemplate/search/unearned_detail.html
@@ -0,0 +1,257 @@
+<& elements/search.html,
+ 'title' => emt("Unearned revenue - ".ucfirst($unearned_mode)) . ' (' .
+ time2str('%b %d %Y', $unearned) . ')',
+ 'name' => emt('line items'),
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ $money_char. '%.2f total',
+ $money_char. '%.2f unearned revenue'
+ ],
+ 'header' => [ map( {emt $_}
+ 'Description',
+ 'Unearned', # depends on mode
+ 'Recurring charge', #recur - usage
+ 'Owed', #recur - usage - credits - payments
+ 'Paid', #payments
+ 'Payment date', #of last payment
+ 'Credit date', #of last credit
+ 'Charge start',
+ 'Charge end',
+ 'Invoice',
+ 'Date'
+ ),
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ #Description
+ sub { $_[0]->pkgnum > 0
+ ? $_[0]->get('pkg') # possibly use override.pkg
+ : $_[0]->get('itemdesc') # but i think this correct
+ },
+ #Unearned
+ money_sub('unearned_revenue'),
+ #Recurring charge
+ money_sub('recur_no_usage'),
+ #Owed
+ money_sub('owed_no_usage'),
+ #Paid
+ money_sub('paid_no_usage'),
+ #Payment date
+ date_sub('last_pay'),
+ #Credit date
+ date_sub('last_credit'),
+ #Charge start
+ date_sub('sdate'),
+ #Charge end, minus most of a day
+ date_sub('before_edate'),
+ #Invoice
+ 'invnum',
+ #Invoice date
+ date_sub('_date'),
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'sort_fields' => [
+ 'pkg',
+ # SQL expressions work as sort keys...
+ 'unearned_revenue',
+ 'recur_no_usage',
+ 'owed_no_usage',
+ 'paid_no_usage',
+ 'last_pay',
+ 'last_credit',
+ 'sdate',
+ 'edate',
+ 'invnum',
+ '_date',
+ ],
+ 'links' => [
+ ('' x 9),
+ $ilink,
+ $ilink,
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'lrrcrccrc'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ ('' x 11),
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ ('' x 11),
+ FS::UI::Web::cust_styles(),
+ ],
+&>
+<%init>
+
+# Separated from cust_bill_pkg.cgi to simplify things.
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+my $unearned = '';
+my $unearned_mode = '';
+my $unearned_base = '';
+my $unearned_sql = '';
+
+my @select = ( 'cust_bill_pkg.*', 'cust_bill._date' );
+my ($join_cust, $join_pkg ) = ('', '');
+
+#here is the agent virtualization
+my $agentnums_sql =
+ $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my @where = ( $agentnums_sql );
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+
+if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
+ push @where, FS::cust_main->cust_status_sql . " = '$1'";
+}
+
+push @where, "cust_bill._date >= $beginning",
+ "cust_bill._date <= $ending";
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.agentnum = $1";
+}
+
+# no pkgclass, no taxclass, no tax location...
+
+# unearned revenue mode
+$cgi->param('date') =~ /^(\d+)$/
+ or die "date required";
+
+$unearned = $1;
+$unearned_mode = $cgi->param('mode');
+
+push @where, "cust_bill_pkg.sdate < $unearned",
+ "cust_bill_pkg.edate > $unearned",
+ "cust_bill_pkg.recur != 0",
+ "part_pkg.freq != '0'";
+
+if ( !$cgi->param('include_monthly') ) {
+ push @where,
+ "part_pkg.freq != '1'",
+ "part_pkg.freq NOT LIKE '%h'",
+ "part_pkg.freq NOT LIKE '%d'",
+ "part_pkg.freq NOT LIKE '%w'";
+}
+
+my @opt = (
+ $unearned, #before this date
+ '', #after this date
+ setuprecur => 'recur',
+ no_usage => 1
+);
+
+my $charged = FS::cust_bill_pkg->charged_sql(@opt);
+push @select, "($charged) AS recur_no_usage";
+
+my $owed_sql = FS::cust_bill_pkg->owed_sql(@opt);
+push @select, "($owed_sql) AS owed_no_usage";
+
+my $paid_sql = FS::cust_bill_pkg->paid_sql(@opt);
+push @select, "$paid_sql AS paid_no_usage";
+
+if ( $unearned_mode eq 'paid' ) {
+ # then use the amount paid, minus usage charges
+ $unearned_base = $paid_sql;
+}
+else {
+ # use the amount billed, minus usage charges and credits
+ $unearned_base = "( $charged - " .
+ FS::cust_bill_pkg->credited_sql(@opt) . ' )';
+}
+# whatever we're using as the base, only show rows where it's positive
+push @where, "$unearned_base > 0";
+
+my $edate_zero = midnight_sql('edate');
+my $sdate_zero = midnight_sql('sdate');
+# $unearned is one second before midnight on the date requested for the report.
+
+# suppress partial days for more accounting-like behavior
+my $period = "CAST( ($edate_zero - $sdate_zero) / 86400.0 AS DECIMAL(10,0) )";
+
+my $remaining = "GREATEST(
+ CAST( ($edate_zero - $unearned) / 86400.0 AS DECIMAL(10,0) ),
+ 0)";
+my $fraction = "$remaining / $period";
+
+$unearned_sql = "CAST( $unearned_base * $fraction AS DECIMAL(10,2) )";
+push @select, "$unearned_sql AS unearned_revenue";
+
+# last payment/credit date
+my %t = (pay => 'cust_bill_pay', credit => 'cust_credit_bill');
+foreach my $x (qw(pay credit)) {
+ my $table = $t{$x};
+ my $link = $table.'_pkg';
+ my $pkey = dbdef->table($table)->primary_key;
+ my $last_date_sql = "SELECT MAX(_date)
+ FROM $table JOIN $link USING ($pkey)
+ WHERE $link.billpkgnum = cust_bill_pkg.billpkgnum
+ AND $table._date <= $unearned";
+ push @select, "($last_date_sql) AS last_$x";
+}
+
+push @select, '(edate - 82799) AS before_edate';
+
+#no itemdesc
+#no tax report group kludge
+#no tax exemption
+#usage always excluded
+
+# always 'nottax', not 'istax'
+$join_cust = ' JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum ) ';
+
+$join_pkg .= ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart )
+ LEFT JOIN part_pkg AS override
+ ON pkgpart_override = override.pkgpart ';
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+my $count_query = "SELECT COUNT(DISTINCT billpkgnum),
+ SUM( $unearned_base ), SUM( $unearned_sql )
+ FROM cust_bill_pkg $join_cust $join_pkg $where";
+
+push @select, 'part_pkg.pkg',
+ 'part_pkg.freq',
+ 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields();
+
+my $query = {
+ 'table' => 'cust_bill_pkg',
+ 'addl_from' => "$join_cust $join_pkg",
+ 'hashref' => {},
+ 'select' => join(",\n", @select ),
+ 'extra_sql' => $where,
+ 'order_by' => 'ORDER BY cust_bill._date, billpkgnum',
+};
+
+my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $money_char;
+
+sub money_sub {
+ $conf ||= new FS::Conf;
+ $money_char ||= $conf->config('money_char') || '$';
+ my $field = shift;
+ sub {
+ $money_char . sprintf('%.2f', $_[0]->get($field));
+ };
+}
+
+sub date_sub {
+ my $field = shift;
+ sub {
+ my $value = $_[0]->get($field);
+ $value ? time2str('%b %d %Y', $value) : '';
+ };
+};
+
+</%init>
diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi
index fda4db0d9..ec3191971 100755
--- a/httemplate/view/cust_main.cgi
+++ b/httemplate/view/cust_main.cgi
@@ -46,10 +46,39 @@ function areyousure(href, message) {
<A HREF="<% $p %>edit/cust_main.cgi?<% $custnum %>"><% mt('Edit this customer') |h %></A> |
% }
-% if ( $curuser->access_right('Cancel customer')
-% && $cust_main->ncancelled_pkgs
+% if ( $curuser->access_right('Suspend customer')
+% && scalar($cust_main->unsuspended_pkgs)
+% ) {
+ <& /elements/popup_link-cust_main.html,
+ { 'action' => $p. 'misc/suspend_cust.html',
+ 'label' => emt('Suspend this customer'),
+ 'actionlabel' => emt('Confirm Suspension'),
+ 'color' => '#ff9900',
+ 'cust_main' => $cust_main,
+ 'width' => 616, #make room for reasons
+ 'height' => 366,
+ }
+ &> |
+% }
+
+% if ( $curuser->access_right('Unsuspend customer')
+% && scalar($cust_main->suspended_pkgs)
% ) {
+ <& /elements/popup_link-cust_main.html,
+ { 'action' => $p. 'misc/unsuspend_cust.html',
+ 'label' => emt('Unsuspend this customer'),
+ 'actionlabel' => emt('Confirm Unsuspension'),
+ #'color' => '#ff9900',
+ 'cust_main' => $cust_main,
+ #'width' => 616, #make room for reasons
+ #'height' => 366,
+ }
+ &> |
+% }
+% if ( $curuser->access_right('Cancel customer')
+% && scalar($cust_main->ncancelled_pkgs)
+% ) {
<& /elements/popup_link-cust_main.html,
{ 'action' => $p. 'misc/cancel_cust.html',
'label' => emt('Cancel this customer'),
@@ -60,11 +89,9 @@ function areyousure(href, message) {
'height' => 366,
}
&> |
-
% }
% if ( $curuser->access_right('Merge customer') ) {
-
<& /elements/popup_link-cust_main.html,
{ 'action' => $p. 'misc/merge_cust.html',
'label' => emt('Merge this customer'),
@@ -74,7 +101,6 @@ function areyousure(href, message) {
'height' => 192,
}
&> |
-
% }
% if ( $conf->exists('deletecustomers')
@@ -232,9 +258,9 @@ function areyousure(href, message) {
% }
% if ( $view eq 'jumbo' ) {
- <BR><BR>
- <A NAME="tickets"><FONT SIZE="+2"><% mt('Tickets') |h %></FONT></A><BR>
+ <BR>
% }
+<BR>
% if ( $view eq 'tickets' || $view eq 'jumbo' ) {
@@ -277,7 +303,13 @@ function areyousure(href, message) {
% }
% if ( $view eq 'custom' ) {
+% if ( $conf->config('cust_main-custom_link') ) {
<& cust_main/custom.html, $cust_main &>
+% } elsif ( $conf->config('cust_main-custom_content') ) {
+ <& cust_main/custom_content.html, $cust_main &>
+% #} else {
+% # warn "custom view without cust_main-custom_link or -custom_content?";
+% }
% }
</DIV>
@@ -326,7 +358,8 @@ $views{emt('Payment History')} = 'payment_history'
$views{emt('Change History')} = 'change_history'
if $curuser->access_right('View customer history');
$views{$conf->config('cust_main-custom_title') || emt('Custom')} = 'custom'
- if $conf->config('cust_main-custom_link');
+ if $conf->config('cust_main-custom_link')
+ || $conf->config('cust_main-custom_content');
$views{emt('Jumbo')} = 'jumbo';
my %viewname = reverse %views;
diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html
index 522c6db86..b2a0efdef 100644
--- a/httemplate/view/cust_main/billing.html
+++ b/httemplate/view/cust_main/billing.html
@@ -146,7 +146,7 @@
% if ( $cust_main->payinfo ) {
<TR>
- <TD ALIGN="right"<% mt('P.O.') |h %></TD>
+ <TD ALIGN="right"><% mt('P.O.') |h %></TD>
<TD BGCOLOR="#ffffff"><% $cust_main->payinfo %></TD>
</TR>
% }
@@ -189,15 +189,25 @@
% my $no = emt('no');
% my @exempt_groups = grep /\S/, $conf->config('tax-cust_exempt-groups');
-<TR>
- <TD ALIGN="right"><% mt('Tax exempt') |h %><% @exempt_groups ? ' ('.emt('all taxes').')' : '' %></TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->tax ? $yes : $no %></TD>
-</TR>
+
+% unless ( $conf->exists('cust_class-tax_exempt')
+% || $conf->exists('tax-cust_exempt-groups-require_individual_nums')
+% )
+% {
+ <TR>
+ <TD ALIGN="right"><% mt('Tax exempt') |h %><% @exempt_groups ? ' ('.emt('all taxes').')' : '' %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->tax ? $yes : $no %></TD>
+ </TR>
+% }
+
% foreach my $exempt_group ( @exempt_groups ) {
-<TR>
- <TD ALIGN="right"><% mt('Tax exempt') |h %> (<% $exempt_group %> taxes)</TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->tax_exemption($exempt_group) ? $yes : $no %></TD>
-</TR>
+% my $cust_main_exemption = $cust_main->tax_exemption($exempt_group);
+ <TR>
+ <TD ALIGN="right"><% mt('Tax exempt') |h %> (<% $exempt_group %> taxes)</TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main_exemption ? $yes : $no %>
+ <% $cust_main_exemption ? $cust_main_exemption->exempt_number : '' |h %>
+ </TD>
+ </TR>
% }
% if ( $conf->exists('enable_taxproducts') ) {
@@ -207,21 +217,27 @@
</TR>
% }
<TR>
- <TD ALIGN="right"><% mt('Postal invoices') |h %></TD>
+ <TD ALIGN="right"><% mt('Postal mail invoices') |h %></TD>
<TD BGCOLOR="#ffffff">
<% ( grep { $_ eq 'POST' } @invoicing_list ) ? $yes : $no %>
</TD>
</TR>
<TR>
- <TD ALIGN="right"><% mt('FAX invoices') |h %></TD>
+ <TD ALIGN="right"><% mt('Fax invoices') |h %></TD>
<TD BGCOLOR="#ffffff">
<% ( grep { $_ eq 'FAX' } @invoicing_list ) ? $yes : $no %>
</TD>
</TR>
-% unless ( $conf->exists('cust-email-high-visibility')) {
<TR>
<TD ALIGN="right"><% mt('Email invoices') |h %></TD>
<TD BGCOLOR="#ffffff">
+ <% $cust_main->invoice_noemail ? $no : $yes %>
+ </TD>
+</TR>
+% unless ( $conf->exists('cust-email-high-visibility')) {
+<TR>
+ <TD ALIGN="right"><% mt('Email address(es)') |h %></TD>
+ <TD BGCOLOR="#ffffff">
<% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) || $no %>
</TD>
</TR>
diff --git a/httemplate/view/cust_main/contacts.html b/httemplate/view/cust_main/contacts.html
index b3e52b556..9c6069182 100644
--- a/httemplate/view/cust_main/contacts.html
+++ b/httemplate/view/cust_main/contacts.html
@@ -1,122 +1,128 @@
-% my %which = (
-% '' => emt('Billing'),
-% 'ship_' => emt('Service'),
-% );
-% foreach my $which ( '', 'ship_' ) {
-% my $pre = $cust_main->get("${which}last") ? $which : '';
-
-<FONT CLASS="fsinnerbox-title"><% $which{$which} %> <% mt('address') |h %></FONT>
+% my %addr_label = ('bill' => 'Billing address', 'ship' => 'Service address');
+
+%# Locations (possibly break this out)
+% my @which = ('bill');
+% push @which, 'ship' if $cust_main->has_ship_address;
+% while (@which) {
+% my $this = shift @which;
+% my $method = $this.'_location';
+% my $location = $cust_main->$method;
+<FONT CLASS="fsinnerbox-title"><% mt( $addr_label{$this} ) |h %></FONT>
<TABLE CLASS="fsinnerbox">
-<TR>
- <TD ALIGN="right"><% mt('Contact name') |h %></TD>
- <TD COLSPAN=5 BGCOLOR="#ffffff">
- <% $cust_main->get("${pre}last"). ', '. $cust_main->get("${pre}first") |h %>
- </TD>
-% if ( $which eq '' && $conf->exists('show_ss') ) {
- <TD ALIGN="right"><% mt('SS#') |h %></TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->masked('ss') || '&nbsp' %></TD>
-% }
-</TR>
-% if ( $conf->exists('cust-email-high-visibility') && $which eq '') {
+% if ( $this eq 'bill' ) {
+% #billing contact fields
+ <TR>
+ <TD ALIGN="right"><% mt('Contact name') |h %></TD>
+ <TD COLSPAN=5 BGCOLOR="#ffffff"><% $cust_main->contact |h %></TD>
+% if ( $conf->exists('show_ss') ) {
+ <TD ALIGN="right"><% mt('SS#') |h %></TD>
+ <TD BGCOLOR="#ffffff"><% $conf->exists('unmask_ss')
+ ? $cust_main->ss
+ : $cust_main->masked('ss') || '&nbsp;' %></TD>
+% }
+ </TR>
+% if ( $conf->exists('cust-email-high-visibility') ) {
<TR>
- <TD ALIGN="right"><% mt('Email invoices') |h %></TD>
+ <TD ALIGN="right"><% mt('Email address(es)') |h %></TD>
<TD BGCOLOR="#ffff00">
- <% join(', ', grep { $_ !~ /^(POST|FAX)$/ } @invoicing_list ) || $no %>
+ <% $cust_main->invoicing_list_emailonly_scalar || $no %>
</TD>
</TR>
-% }
-
-% if ( $cust_main->get("${pre}company") ) {
+% }
+% if ( $cust_main->company ) {
<TR>
<TD ALIGN="right"><% mt('Company') |h %></TD>
- <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}company") |h %></TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->company |h %></TD>
</TR>
-% }
-
+% }
+% } # if $this eq 'bill'
+% # now the actual address
<TR>
<TD ALIGN="right"><% mt('Address') |h %></TD>
- <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address1") |h %></TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $location->address1 |h %></TD>
</TR>
-% if ( $cust_main->get("${pre}address2") ) {
-% my $address2_label =
-% ( $conf->exists('cust_main-require_address2')
-% && ! ( $pre xor $cust_main->has_ship_address )
-% )
-% ? emt('Unit #')
-% : ' ';
+% if ( $location->get('address2') ) {
+% my $address2_label = $conf->exists('cust_main-require_address2')
+% ? emt('Unit #')
+% : ' ';
- <TR>
- <TD ALIGN="right"><% $address2_label %></TD>
- <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address2") |h %></TD>
- </TR>
+<TR>
+ <TD ALIGN="right"><% $address2_label %></TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $location->address2 |h %></TD>
+</TR>
% }
<TR>
<TD ALIGN="right"><% mt('City') |h %></TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}city") |h %></TD>
-% if ( $cust_main->get("${pre}county") ) {
+ <TD BGCOLOR="#ffffff"><% $location->city |h %></TD>
+% if ( $location->county ) {
<TD ALIGN="right"><% mt('County') |h %></TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}county") |h %></TD>
+ <TD BGCOLOR="#ffffff"><% $location->county |h %></TD>
% }
<TD ALIGN="right"><% mt('State') |h %></TD>
- <TD BGCOLOR="#ffffff"><% state_label( $cust_main->get("${pre}state"), $cust_main->get("${pre}country") ) |h %></TD>
+ <TD BGCOLOR="#ffffff"><% state_label( $location->state, $location->country ) |h %></TD>
<TD ALIGN="right"><% mt('Zip') |h %></TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}zip") %></TD>
+ <TD BGCOLOR="#ffffff"><% $location->zip %></TD>
</TR>
<TR>
<TD ALIGN="right"><% mt('Country') |h %></TD>
- <TD BGCOLOR="#ffffff"><% code2country( $cust_main->get("${pre}country") ) %></TD>
+ <TD BGCOLOR="#ffffff"><% code2country( $location->country ) %></TD>
</TR>
-% if ( $cust_main->get($pre.'latitude') && $cust_main->get($pre.'longitude') ) {
- <& /elements/tr-coords.html, $cust_main->get($pre.'latitude'),
- $cust_main->get($pre.'longitude'),
+% if ( $location->latitude && $location->longitude ) {
+ <& /elements/tr-coords.html, $location->latitude,
+ $location->longitude,
$cust_main->name_short,
$cust_main->agentnum,
&>
% }
+
+% if ( $this eq 'bill' ) {
+% # billing contact phone numbers
+% foreach my $phone (qw(daytime night mobile)) {
+% next if !$cust_main->get($phone);
+<TR>
+ <TD ALIGN="right"><% $phone_label{$phone} %></TD>
+ <TD COLSPAN=3 BGCOLOR="#ffffff">
+ <& /elements/phonenumber.html,
+ $cust_main->get($phone),
+ callable => 1,
+ calling_list_exempt => $cust_main->calling_list_exempt,
+ &>
+ </TD>
+</TR>
-% foreach my $phone (grep $cust_main->get($pre.$_), qw( daytime night mobile )){
-
- <TR>
- <TD ALIGN="right"><% $phone_label{$phone} %></TD>
- <TD COLSPAN=3 BGCOLOR="#ffffff">
- <& /elements/phonenumber.html,
- $cust_main->get($pre.$phone),
- 'callable'=>1,
- 'calling_list_exempt'=>$cust_main->calling_list_exempt,
- &>
- </TD>
- </TR>
-
-% }
+% } #foreach $phone
+% if ( $cust_main->get('fax') ) {
-% if ( $cust_main->get("${pre}fax") ) {
<TR>
<TD ALIGN="right"><% mt('Fax') |h %></TD>
<TD COLSPAN=3 BGCOLOR="#ffffff">
- <% $cust_main->get("${pre}fax") || '&nbsp' %>
+ <% $cust_main->get('fax') || '&nbsp;' %>
</TD>
</TR>
-% }
-% if ( $which eq '' && $conf->exists('show_stateid') ) {
- <TR>
+% }
+%
+% if ( $conf->exists('show_stateid') ) {
+
+<TR>
<TD ALIGN="right"><% $stateid_label %></TD>
<TD BGCOLOR="#ffffff"><% $cust_main->masked('stateid') || '&nbsp' %></TD>
<TD ALIGN="right"><% $stateid_state_label %></TD>
<TD BGCOLOR="#ffffff"><% $cust_main->stateid_state || '&nbsp' %></TD>
</TR>
-% }
+% }
+% } #if $this eq 'bill'
</TABLE>
-% if ( $which ne 'ship_' ) {
+% if ( @which ) {
<BR>
% }
-% }
+% } #while @which
<%once>
my %phone_label = (
@@ -147,7 +153,7 @@ my $stateid_state_label = FS::Msgcat::_gettext('stateid_state') =~ /^(stateid_st
</%once>
<%init>
-my( $cust_main ) = @_;
+my $cust_main = shift;
my $conf = new FS::Conf;
my @invoicing_list = $cust_main->invoicing_list;
my $no = emt('no');
diff --git a/httemplate/view/cust_main/custom_content.html b/httemplate/view/cust_main/custom_content.html
new file mode 100644
index 000000000..dd3d1f0d4
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content.html
@@ -0,0 +1,31 @@
+% foreach my $item (@content) {
+%
+% if ( $item =~ /^\s*$/ ) {
+ <BR>
+% next;
+% }
+%
+% if ( $items{$item} ) {
+ <& "custom_content/$item.html", $cust_main &>
+% } else {
+ Unknown item <% $item |h %><BR>
+% }
+% }
+<%init>
+
+my($cust_main) = @_;
+
+my $conf = new FS::Conf;
+
+my @content = $conf->config('cust_main-custom_content');
+
+my %items = map { $_=>1 } qw(
+ small_custview
+ birthdate
+ spouse_birthdate
+ svc_acct
+ svc_phone
+ svc_hardware
+);
+
+</%init>
diff --git a/httemplate/view/cust_main/custom_content/.birthdate.html.swp b/httemplate/view/cust_main/custom_content/.birthdate.html.swp
new file mode 100644
index 000000000..9571d22cf
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/.birthdate.html.swp
Binary files differ
diff --git a/httemplate/view/cust_main/custom_content/.small_custview.html.swp b/httemplate/view/cust_main/custom_content/.small_custview.html.swp
new file mode 100644
index 000000000..a39f52dde
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/.small_custview.html.swp
Binary files differ
diff --git a/httemplate/view/cust_main/custom_content/.spouse_birthdate.html.swp b/httemplate/view/cust_main/custom_content/.spouse_birthdate.html.swp
new file mode 100644
index 000000000..0042012f7
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/.spouse_birthdate.html.swp
Binary files differ
diff --git a/httemplate/view/cust_main/custom_content/.svc_Common.html.swp b/httemplate/view/cust_main/custom_content/.svc_Common.html.swp
new file mode 100644
index 000000000..15591b96d
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/.svc_Common.html.swp
Binary files differ
diff --git a/httemplate/view/cust_main/custom_content/.svc_acct.html.swp b/httemplate/view/cust_main/custom_content/.svc_acct.html.swp
new file mode 100644
index 000000000..e2db6d5d1
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/.svc_acct.html.swp
Binary files differ
diff --git a/httemplate/view/cust_main/custom_content/.svc_hardware.html.swp b/httemplate/view/cust_main/custom_content/.svc_hardware.html.swp
new file mode 100644
index 000000000..1106f9ed5
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/.svc_hardware.html.swp
Binary files differ
diff --git a/httemplate/view/cust_main/custom_content/.svc_phone.html.swp b/httemplate/view/cust_main/custom_content/.svc_phone.html.swp
new file mode 100644
index 000000000..79b8185e1
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/.svc_phone.html.swp
Binary files differ
diff --git a/httemplate/view/cust_main/custom_content/birthdate.html b/httemplate/view/cust_main/custom_content/birthdate.html
new file mode 100644
index 000000000..1f16963af
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/birthdate.html
@@ -0,0 +1,15 @@
+<TABLE CLASS="fsinnerbox">
+ <& /elements/tr-td-label.html, 'label' => mt('Date of Birth') &>
+ <TD BGCOLOR="#ffffff"><% $dt ? $dt->strftime($date_format) : '' %></TD>
+</TR>
+</TABLE>
+<%init>
+my($cust_main) = @_;
+my $conf = new FS::Conf;
+my $date_format = ($conf->config('date_format') || "%m/%d/%Y");
+my $dt = $cust_main->birthdate ne ''
+ ? DateTime->from_epoch( 'epoch' => $cust_main->birthdate,
+ 'time_zone' =>'floating',
+ )
+ : '';
+</%init>
diff --git a/httemplate/view/cust_main/custom_content/small_custview.html b/httemplate/view/cust_main/custom_content/small_custview.html
new file mode 100644
index 000000000..7c724c78f
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/small_custview.html
@@ -0,0 +1,4 @@
+<& /elements/small_custview.html, $cust_main &>
+<%init>
+my($cust_main) = @_;
+</%init>
diff --git a/httemplate/view/cust_main/custom_content/spouse_birthdate.html b/httemplate/view/cust_main/custom_content/spouse_birthdate.html
new file mode 100644
index 000000000..c78fd26a5
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/spouse_birthdate.html
@@ -0,0 +1,15 @@
+<TABLE CLASS="fsinnerbox">
+ <& /elements/tr-td-label.html, 'label' => mt('Spouse Date of Birth') &>
+ <TD BGCOLOR="#ffffff"><% $dt ? $dt->strftime($date_format) : '' %></TD>
+</TR>
+</TABLE>
+<%init>
+my($cust_main) = @_;
+my $conf = new FS::Conf;
+my $date_format = ($conf->config('date_format') || "%m/%d/%Y");
+my $dt = $cust_main->spouse_birthdate ne ''
+ ? DateTime->from_epoch( 'epoch' => $cust_main->spouse_birthdate,
+ 'time_zone' =>'floating',
+ )
+ : '';
+</%init>
diff --git a/httemplate/view/cust_main/custom_content/svc_Common.html b/httemplate/view/cust_main/custom_content/svc_Common.html
new file mode 100644
index 000000000..bddb8bf16
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/svc_Common.html
@@ -0,0 +1,40 @@
+% foreach my $cust_svc (@cust_svc) {
+% my $svc_x = $cust_svc->svc_x;
+<TABLE CLASS="fsinnerbox">
+ <TR>
+ <TH COLSPAN=2><% $cust_svc->part_svc->svc |h %></TH>
+ </TR>
+% foreach my $field ( grep $svc_x->$_(), @{ $opt{fields} } ) {
+ <& /elements/tr-td-label.html, 'label' => $labels{$field} &>
+ <TD BGCOLOR="#ffffff"><% $svc_x->$field() |h %></TD>
+ </TR>
+% }
+</TABLE>
+% }
+<%init>
+my($cust_main, %opt) = @_;
+
+my $table = $opt{table};
+my @cust_svc = ();
+foreach my $cust_pkg (
+ grep $_->num_cust_svc( 'svcdb'=>$table ),
+ $cust_main->all_pkgs
+) {
+ my @wtf = $cust_pkg->cust_svc( 'svcdb'=>$table );
+ push @cust_svc, $cust_pkg->cust_svc( 'svcdb'=>$table );
+}
+
+my %labels;
+if ( UNIVERSAL::can("FS::$table", 'table_info') ) {
+# $opt{'name'} = "FS::$table"->table_info->{'name'};
+
+ my $fields = "FS::$table"->table_info->{'fields'};
+ %labels = map { $_ => ( ref($fields->{$_})
+ ? $fields->{$_}{'label'}
+ : $fields->{$_}
+ );
+ }
+ keys %$fields;
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/custom_content/svc_acct.html b/httemplate/view/cust_main/custom_content/svc_acct.html
new file mode 100644
index 000000000..49b97986d
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/svc_acct.html
@@ -0,0 +1,7 @@
+<& svc_Common.html, $cust_main,
+ 'table' => 'svc_acct',
+ 'fields' => [qw( username _password )],
+&>
+<%init>
+my($cust_main) = @_;
+</%init>
diff --git a/httemplate/view/cust_main/custom_content/svc_hardware.html b/httemplate/view/cust_main/custom_content/svc_hardware.html
new file mode 100644
index 000000000..f5d53a251
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/svc_hardware.html
@@ -0,0 +1,7 @@
+<& svc_Common.html, $cust_main,
+ 'table' => 'svc_hardware',
+ 'fields' => [qw( ip_addr hw_addr serial )],
+&>
+<%init>
+my($cust_main) = @_;
+</%init>
diff --git a/httemplate/view/cust_main/custom_content/svc_phone.html b/httemplate/view/cust_main/custom_content/svc_phone.html
new file mode 100644
index 000000000..46ec476bc
--- /dev/null
+++ b/httemplate/view/cust_main/custom_content/svc_phone.html
@@ -0,0 +1,7 @@
+<& svc_Common.html, $cust_main,
+ 'table' => 'svc_phone',
+ 'fields' => [qw( phonenum )],
+&>
+<%init>
+my($cust_main) = @_;
+</%init>
diff --git a/httemplate/view/cust_main/locations.html b/httemplate/view/cust_main/locations.html
index 98c933645..b29d0ce4d 100755
--- a/httemplate/view/cust_main/locations.html
+++ b/httemplate/view/cust_main/locations.html
@@ -5,12 +5,17 @@ span.loclabel {
background-color: #cccccc;
border: 1px solid black
}
+table.location {
+ width: 100%;
+ padding: 1px;
+ border-spacing: 0px;
+}
</STYLE>
% foreach my $locationnum (@sorted) {
% my $packages = $packages_in{$locationnum};
% my $loc = $locations{$locationnum};
% next if $loc->disabled and scalar(@$packages) == 0;
-<& /elements/table-grid.html &>
+<TABLE CLASS="grid location">
<TR><TH COLSPAN=3 ALIGN="left" VALIGN="bottom"
STYLE="padding-bottom: 0px;
padding-left: 0px;
@@ -18,10 +23,7 @@ STYLE="padding-bottom: 0px;
border-bottom-color: black;
border-bottom-width: 1px;">
<SPAN CLASS="loclabel">
-% if (! $locationnum) {
-<% mt('Default service location:') |h %>
-% }
-% elsif ( $loc->disabled ) {
+% if ( $loc->disabled ) {
<FONT COLOR="#808080"><I>
% }
<% $loc->location_label %></SPAN>
@@ -49,8 +51,7 @@ my %locations = map { $_->locationnum => $_ } qsearch({
'order_by' => 'ORDER BY country, state, city, address1, locationnum',
});
my @sections = keys %locations;
-$locations{''} = $cust_main;
-my %packages_in = map { $_ => [] } ('', @sections);
+my %packages_in = map { $_ => [] } (@sections);
my %active = (); # groups with non-canceled packages
foreach my $cust_pkg ( @$all_packages ) {
@@ -58,10 +59,13 @@ foreach my $cust_pkg ( @$all_packages ) {
push @{ $packages_in{$key} }, $cust_pkg;
$active{$key} = 1 if !$cust_pkg->getfield('cancel');
}
+# prevent disabling these
+$active{$cust_main->ship_locationnum} = 1;
+$active{$cust_main->bill_locationnum} = 1;
my @sorted = (
- '',
- grep ( { $active{$_} } @sections),
+ $cust_main->ship_locationnum,
+ grep ( { $active{$_} && $_ != $cust_main->ship_locationnum } @sections),
grep ( { !$active{$_} } @sections),
);
diff --git a/httemplate/view/cust_main/misc.html b/httemplate/view/cust_main/misc.html
index 295328721..a0ab403e8 100644
--- a/httemplate/view/cust_main/misc.html
+++ b/httemplate/view/cust_main/misc.html
@@ -116,13 +116,27 @@
% }
+% if ( $conf->exists('cust_main-enable_spouse_birthdate') ) {
+% my $dt = $cust_main->spouse_birthdate ne ''
+% ? DateTime->from_epoch( 'epoch' => $cust_main->spouse_birthdate,
+% 'time_zone' =>'floating',
+% )
+% : '';
+
+ <TR>
+ <TD ALIGN="right"><% mt('Spouse Date of Birth') |h %></TD>
+ <TD BGCOLOR="#ffffff"><% $dt ? $dt->strftime($date_format) : '' %></TD>
+ </TR>
+
+% }
+
% if ( $conf->exists('cust_main-require_censustract') ) {
<TR>
<TD ALIGN="right">
- <% mt('Census tract ([_1])', $cust_main->censusyear) |h %>
+ <% mt('Census tract ([_1])', $cust_main->ship_location->censusyear) |h %>
</TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->censustract %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->ship_location->censustract %></TD>
</TR>
% }
@@ -131,7 +145,7 @@
<TR>
<TD ALIGN="right"><% mt('Tax district') |h %></TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->district %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->ship_location->district %></TD>
</TR>
% }
diff --git a/httemplate/view/cust_main/order_pkg_link.html b/httemplate/view/cust_main/order_pkg_link.html
deleted file mode 100644
index 7bc5eef31..000000000
--- a/httemplate/view/cust_main/order_pkg_link.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<& /elements/popup_link-cust_main.html,
- 'action' => $p. 'misc/order_pkg.html',
- 'label' => $opt{'label'} || emt('Order new package'),
- 'actionlabel' => emt('Order new package'),
- 'color' => '#333399',
- 'cust_main' => $cust_main,
- 'closetext' => emt('Close'),
- 'width' => 960, #763,
- 'height' => $height,
- %optional,
-&>
-<%init>
-
-my($cust_main, %opt) = @_;
-
-my %optional = map { $_ => $opt{$_} }
- grep $opt{$_},
- qw( lock_pkgpart lock_locationnum qualnum svcpart );
-
-my $height = $opt{'lock_locationnum'} ? 336 : 576;
-
-</%init>
diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html
index e16b2c1d4..7d7930634 100755
--- a/httemplate/view/cust_main/packages.html
+++ b/httemplate/view/cust_main/packages.html
@@ -7,7 +7,7 @@
% if ( $curuser->access_right('Order customer package') ) {
<% $s++ ? ' | ' : '' %>
- <& order_pkg_link.html, $cust_main &>
+ <& /elements/order_pkg_link.html, 'cust_main'=>$cust_main &>
% }
% if ( $curuser->access_right('One-time charge')
diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html
index 5f458e635..c0a56d0f3 100644
--- a/httemplate/view/cust_main/packages/services.html
+++ b/httemplate/view/cust_main/packages/services.html
@@ -3,7 +3,7 @@
% ###
<TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
- <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
+ <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=2 WIDTH="100%">
<SCRIPT TYPE="text/javascript">
function clearhint_search_cust_svc(obj, str) {
if (obj.value == str) obj.value = '';
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index 47ef1bcb7..e9017745b 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -32,7 +32,20 @@
% }
%
-% } else {
+% if ( $part_pkg->freq ) { #?
+
+ <TR>
+ <TD COLSPAN=<%$colspan%>>
+ <FONT SIZE=-1>
+% if ( $curuser->access_right('Un-cancel customer package') ) {
+ (&nbsp;<% pkg_uncancel_link($cust_pkg) %>&nbsp;)
+% }
+ <FONT>
+ </TD>
+ </TR>
+% }
+%
+% } else {
%
% if ( $cust_pkg->get('susp') ) { #status: suspended
% my $cpr = $cust_pkg->last_cust_pkg_reason('susp');
@@ -56,6 +69,8 @@
<% pkg_status_row($cust_pkg, emt('Setup'), 'setup', %opt ) %>
% }
+ <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
<% pkg_status_row_changed( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
<% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
% if ( $cust_pkg->option('suspend_bill', 1)
@@ -104,6 +119,8 @@
)
%>
+ <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
<TR>
<TD COLSPAN=<%$colspan%>>
<FONT SIZE=-1>
@@ -123,6 +140,7 @@
<% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
<% pkg_status_row_if($cust_pkg, emt('Start billing'), 'start_date', %opt) %>
+ <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
% }
%
@@ -138,6 +156,8 @@
<% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
+ <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
% } else {
%
% my $num_cust_svc = $cust_pkg->num_cust_svc;
@@ -171,6 +191,8 @@
<% pkg_status_row($cust_pkg, emt('Setup'), 'setup', %opt) %>
+ <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
+
% }
%
% }
@@ -472,6 +494,16 @@ sub pkg_cancel_link {
)
}
+sub pkg_uncancel_link {
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p. 'misc/cancel_pkg.html?method=uncancel',
+ 'label' => emt('Un-cancel'),
+ 'actionlabel' => emt('Un-cancel'),
+ #'color' => #?
+ 'cust_pkg' => shift,
+ )
+}
+
sub pkg_expire_link {
include( '/elements/popup_link-cust_pkg.html',
'action' => $p. 'misc/cancel_pkg.html?method=expire',
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index b5b716199..9e08c0c5d 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -13,6 +13,7 @@
'cust_main' => $cust_main,
'actionlabel' => emt('Enter check payment'),
'width' => 392,
+ 'height' => 392,
&>
% }
@@ -24,6 +25,7 @@
'cust_main' => $cust_main,
'actionlabel' => emt('Enter cash payment'),
'width' => 392,
+ 'height' => 392,
&>
% }
@@ -489,7 +491,7 @@ foreach my $cust_credit ($cust_main->cust_credit) {
foreach my $cust_refund ($cust_main->cust_refund) {
push @history, {
'date' => $cust_refund->_date,
- 'desc' => include('payment_history/refund.html', $cust_refund),
+ 'desc' => include('payment_history/refund.html', $cust_refund, %opt),
'refund' => $cust_refund->refund,
};
@@ -498,14 +500,15 @@ foreach my $cust_refund ($cust_main->cust_refund) {
sub translate_payby {
my ($payby,$payinfo) = (shift,shift);
my %payby = (
+ FS::payby->payby2shortname,
BILL => $payinfo ? emt('Check #') : '',
CHEK => emt('Electronic check '),
PREP => emt('Prepaid card '),
CARD => emt('Credit card #'),
COMP => emt('Complimentary by '),
- CASH => emt('Cash'),
- WEST => emt('Western Union'),
- MCRD => emt('Manual credit card'),
+ #CASH => emt('Cash'),
+ #WEST => emt('Western Union'),
+ #MCRD => emt('Manual credit card'),
);
$payby = (exists $payby{$payby}) ? $payby{$payby} : $payby;
$payby;
@@ -514,6 +517,7 @@ sub translate_payby {
sub translate_payby_refund {
my ($payby,$payinfo) = (shift,shift);
my %payby = (
+ FS::payby->payby2shortname,
BILL => $payinfo ? emt('Check #') : emt('Check'),
CHEK => emt('Electronic check '),
CARD => emt('Credit card #'),
diff --git a/httemplate/view/cust_main/tickets.html b/httemplate/view/cust_main/tickets.html
index adfaead6e..f076fcc92 100644
--- a/httemplate/view/cust_main/tickets.html
+++ b/httemplate/view/cust_main/tickets.html
@@ -1,163 +1,2 @@
-<FORM METHOD="GET" NAME="CreateTicketForm" STYLE="display:inline">
-<SCRIPT TYPE="text/javascript">
-function updateTicketLink() {
- var link = document.getElementById('CreateTicketLink');
- var selector = document.getElementById('Queue')
- link.href = "<% $new_base.'?'.
- join(';', map(
- { ($_ eq 'Queue') ? () : "$_=$new_param{$_}"}
- keys %new_param),'Queue=') %>" + selector.options[selector.selectedIndex].value;
-}
-</SCRIPT>
-<A id="CreateTicketLink" HREF="<% $new_link %>"><% mt('Create new ticket') |h %></A>
- <% mt('in queue') |h %>
-%# fetch list of queues in which the user can create tickets
-% my %queues = FS::TicketSystem->queues('', 'CreateTicket');
-% if( $conf->exists('ticket_system-force_default_queueid') ) {
-<B><% $queues{$new_param{'Queue'}} %></B>
-<INPUT TYPE="hidden" NAME="Queue" VALUE="<% $new_param{'Queue'} %>">
-% }
-% else {
-<SELECT NAME="Queue" id="Queue" onchange="updateTicketLink()">
-% foreach my $queueid ( sort { $queues{$a} cmp $queues{$b} } keys %queues ) {
- <OPTION VALUE="<% $queueid %>"
- <% $queueid == $new_param{'Queue'} ? 'SELECTED' : '' %>
- ><% $queues{$queueid} |h %>
-% }
-</SELECT>
-<SCRIPT DEFER TYPE="text/javascript">updateTicketLink();</SCRIPT>
-% }
-</FORM>
-<BR>
-
-(<A HREF="<% $open_link %>"><% mt("View $openlabel tickets for this customer") |h %></A>)
-(<A HREF="<% $res_link %>"><% mt('View resolved tickets for this customer') |h %></A>)
-<BR><BR>
-
-<& /elements/table-grid.html &>
-% my $bgcolor1 = '#eeeeee';
-% my $bgcolor2 = '#ffffff';
-% my $bgcolor = '';
-
-<TR>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('#') |h %></TH>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Subject') |h %></TH>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Status') |h %></TH>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Queue') |h %></TH>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Owner') |h %></TH>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Due') |h %></TH>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Estimated Time') |h %></TH>
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Priority') |h %></TH>
-% if ( $ss_priority ) {
- <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Customer Priority') |h %></TH>
-% }
-</TR>
-
-% foreach my $ticket ( @tickets ) {
-% my $href = FS::TicketSystem->href_ticket($ticket->{id});
-% if ( $bgcolor eq $bgcolor1 ) {
-% $bgcolor = $bgcolor2;
-% } else {
-% $bgcolor = $bgcolor1;
-% }
-
- <TR>
-
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <A HREF=<%$href%>><% $ticket->{id} %></A>
- </TD>
-
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <A HREF=<%$href%>><% $ticket->{subject} %></A>
- </TD>
-
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <% $ticket->{status} %>
- </TD>
-
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <% $ticket->{queue} %>
- </TD>
-
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <% $ticket->{owner} %>
- </TD>
-
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <% $date_formatter->($ticket->{due}) %>
- </TD>
-
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <% $ticket->{timeestimated} %>
- </TD>
-
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <% $ticket->{content}
- ? $ticket->{content}.' ('.$ticket->{priority}.')'
- : $ticket->{priority}
- %>
- </TD>
-
-% if ( $ss_priority ) {
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <% $ticket->{"CF.{$ss_priority}"} %>
- </TD>
-% }
-
- </TR>
-
-% }
-
-</TABLE>
-
-<%init>
-use Date::Format 'time2str';
-use Date::Parse 'str2time';
-
-my( $conf ) = new FS::Conf;
-my( $cust_main ) = @_;
-my( @tickets ) = $cust_main->tickets;
-
-my $open_link = FS::TicketSystem->href_customer_tickets($cust_main->custnum);
-my $openlabel = join('/', FS::TicketSystem->statuses );
-
-my $res_link = FS::TicketSystem->href_customer_tickets(
- $cust_main->custnum,
- { 'statuses' => [ 'resolved' ] }
- );
-
-my( $new_base, %new_param ) =
- FS::TicketSystem->href_params_new_ticket( $cust_main );
-
-my $new_link = FS::TicketSystem->href_new_ticket( $cust_main );
-
-my $ss_priority = FS::TicketSystem->selfservice_priority;
-if ( $ss_priority ) {
- my $dir = $conf->exists('ticket_system-priority_reverse') ? -1 : 1;
- use sort 'stable';
- # sort in the following way:
- @tickets = sort {
- # within a severity level...
- ( $a->{'content'} eq $b->{'content'} ) ? (
- # no-priority tickets sort last
- (
- ($a->{'_selfservice_priority'} eq '') <=>
- ($b->{'_selfservice_priority'} eq '')
- ) ||
- # otherwise obey ticket_system-priority_reverse
- ( $dir *
- ($b->{'_selfservice_priority'} <=> $a->{'_selfservice_priority'})
- )
- ) : 0; # but don't rearrange between severity levels
- } @tickets;
-}
-
-my $format = $conf->config('date_format') || '%Y-%m-%d';
-
-my $date_formatter = sub {
- my $time = str2time($_[0], 'GMT');
- # exclude times within 24 hours of zero
- ($time > 86400) ? time2str($format, $time) : '';
-};
-
-</%init>
+% my $cust_main = shift;
+<& /elements/table-tickets.html, object => $cust_main &>
diff --git a/httemplate/view/cust_msg.html b/httemplate/view/cust_msg.html
index ec584605d..67ceef799 100755
--- a/httemplate/view/cust_msg.html
+++ b/httemplate/view/cust_msg.html
@@ -22,7 +22,7 @@ function toggle_display(obj) {
<INPUT type="radio" name="what_to_show" onchange="toggle_display(this)" value="body"> Body
</FORM>
</TR>
-<TR><TD colspan=2 style="text-align:center">
+<TR><TD colspan=2 style="text-align:left">
<TEXTAREA id="content-header" style="font-family:monospace"
readonly=1 cols=80 rows=20>
<% encode_entities($cust_msg->header) %>
diff --git a/httemplate/view/cust_pay.html b/httemplate/view/cust_pay.html
index d02f1543d..f9c8bc19c 100644
--- a/httemplate/view/cust_pay.html
+++ b/httemplate/view/cust_pay.html
@@ -98,6 +98,28 @@
% }
+% if ( $cust_pay->payby eq 'CASH' && $cust_pay->payinfo ) {
+ <TR>
+ <TD ALIGN="right"><% mt('Bank') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->bank %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% mt('Teller #') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->teller %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% mt('Depositor') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->depositor %></B></TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% mt('Account #') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->account %></B></TD>
+ </TR>
+% }
+
% if ( $conf->exists('pkg-balances') && $cust_pay->pkgnum ) {
% my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum' => $cust_pay->pkgnum } );
<TR>
diff --git a/httemplate/view/directions.html b/httemplate/view/directions.html
index 599d049c2..f14a11a07 100644
--- a/httemplate/view/directions.html
+++ b/httemplate/view/directions.html
@@ -22,7 +22,6 @@ body { height: 100%; margin: 0px; padding: 0px }
#map_canvas {
height: 100%;
- margin-right: 320px;
}
#directions_panel {
diff --git a/httemplate/view/elements/svc_Common.html b/httemplate/view/elements/svc_Common.html
index 3130c73a0..04d2b2962 100644
--- a/httemplate/view/elements/svc_Common.html
+++ b/httemplate/view/elements/svc_Common.html
@@ -114,6 +114,10 @@ function areyousure(href) {
% }
+% if ( $conf->config('ticket_system') ) {
+<& /elements/table-tickets.html, object => $cust_svc &>
+% }
+
<% joblisting({'svcnum'=>$svcnum}, 1) %>
<% include('/elements/footer.html') %>
diff --git a/httemplate/view/logo-agent.cgi b/httemplate/view/logo-agent.cgi
new file mode 100755
index 000000000..0f654140c
--- /dev/null
+++ b/httemplate/view/logo-agent.cgi
@@ -0,0 +1,10 @@
+<% $conf->config_binary("logo.png", $agentnum) %>
+<%init>
+
+my $conf = new FS::Conf;
+
+my $agentnum = $cgi->param('agentnum');
+
+http_header('Content-Type' => 'image/png' );
+
+</%init>
diff --git a/httemplate/view/prospect_main.html b/httemplate/view/prospect_main.html
index 9e85348af..6c4595dbe 100644
--- a/httemplate/view/prospect_main.html
+++ b/httemplate/view/prospect_main.html
@@ -64,6 +64,38 @@
<BR>
+% if ( $curuser->access_right('Generate quotation') ) {
+ <FONT CLASS="fsinnerbox-title"><% mt( 'Quotations' ) |h %></FONT>
+ <A HREF="<%$p%>edit/quotation.html?prospectnum=<% $prospectnum %>">New quotation</A>
+% my @quotations = $prospect_main->quotation;
+% if ( @quotations ) {
+ <& /elements/table-grid.html &>
+% my $bgcolor1 = '#eeeeee';
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = '';
+ <TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc">#</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% mt('Date') |h %></TH>
+ </TR>
+% foreach my $quotation (@quotations) {
+% if ( $bgcolor eq $bgcolor1 ) {
+% $bgcolor = $bgcolor2;
+% } else {
+% $bgcolor = $bgcolor1;
+% }
+% my $a = qq(<A HREF="$p/view/quotation.html?quotationnum=). #"
+% $quotation->quotationnum. '">';
+ <TR>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $a %><% $quotation->quotationnum %></A></TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $a %><% time2str($date_format, $quotation->_date) |h %></A></TD>
+ </TR>
+% }
+ </TABLE>
+% }
+ <BR><BR>
+% }
+
+
% if ( $curuser->access_right('Qualify service') ) {
<% include( '/elements/popup_link-prospect_main.html',
'action' => $p. 'misc/qual.html',
@@ -80,6 +112,7 @@
<BR><BR>
% }
+<!--
<% ntable("#cccccc") %>
<TR>
@@ -87,6 +120,7 @@
</TR>
</TABLE>
+-->
<%init>
@@ -95,6 +129,10 @@ my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
unless $curuser->access_right('View prospect');
+my $conf = new FS::Conf;
+
+my $date_format = $conf->config('date_format') || '%m/%d/%Y';
+
my $prospectnum;
if ( $cgi->param('prospectnum') =~ /^(\d+)$/ ) {
$prospectnum = $1;
diff --git a/httemplate/view/qual.cgi b/httemplate/view/qual.cgi
index 5c15ec0c3..56c2b61df 100644
--- a/httemplate/view/qual.cgi
+++ b/httemplate/view/qual.cgi
@@ -59,8 +59,10 @@
% $opt{'svcpart'} = $pkg_svc->svcpart if $pkg_svc;
% }
- <% include('/view/cust_main/order_pkg_link.html',
- $cust_or_prospect, %opt) %>
+ <& /elements/order_pkg_link.html,
+ 'cust_main' => $cust_or_prospect,
+ %opt
+ &>
% } elsif ($cust_or_prospect->prospectnum) {
diff --git a/httemplate/view/quotation.html b/httemplate/view/quotation.html
new file mode 100755
index 000000000..461b5dfb6
--- /dev/null
+++ b/httemplate/view/quotation.html
@@ -0,0 +1,93 @@
+<& /elements/header.html, mt('Quotation View'), $menubar &>
+
+%#XXX link to order...
+
+<%doc>
+
+XXX resending quotations
+
+% if ( $curuser->access_right('Resend invoices') ) {
+
+ <A HREF="<% $p %>misc/send-invoice.cgi?method=print;<% $link %>"><% mt('Re-print this invoice') |h %></A>
+
+% if ( grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ) {
+ | <A HREF="<% $p %>misc/send-invoice.cgi?method=email;<% $link %>"><% mt('Re-email this invoice') |h %></A>
+% }
+
+% if ( $conf->exists('hylafax') && length($cust_bill->cust_main->fax) ) {
+ | <A HREF="<% $p %>misc/send-invoice.cgi?method=fax;<% $link %>"><% mt('Re-fax this invoice') |h %></A>
+% }
+
+ <BR><BR>
+
+% }
+
+</%doc>
+
+% if ( $curuser->access_right('Order customer package') ) {
+ <& /elements/order_pkg_link.html,
+ 'label' => emt('Add package'),
+ 'actionlabel' => emt('Add package'),
+ map { $_ => $quotation->$_ } qw( quotationnum custnum prospectnum )
+ &>
+% }
+
+% if ( $conf->exists('quotation_latex') ) {
+ | <A HREF="<% $p %>view/quotation-pdf.cgi?<% $link %>"><% mt('View typeset quotation PDF') |h %></A>
+ <BR><BR>
+% }
+
+% if ( $conf->exists('quotation_html') ) {
+ <% join('', $quotation->print_html() ) %>
+% } else {
+% die "quotation_html config missing";
+% }
+% #plaintext quotations? <PRE><% join('', $quotation->print_text() ) %></PRE>
+
+</%doc>
+
+<& /elements/footer.html &>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+#die "access denied"
+# unless $curuser->access_right('View quotations');
+
+my $quotationnum;
+my($query) = $cgi->keywords;
+if ( $query =~ /^(\d+)$/ ) {
+ $quotationnum = $1;
+} else {
+ $quotationnum = $cgi->param('quotationnum');
+}
+
+my $conf = new FS::Conf;
+
+my $quotation = qsearchs({
+ 'select' => 'quotation.*',
+ 'table' => 'quotation',
+ #'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'hashref' => { 'quotationnum' => $quotationnum },
+ #'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+die "Quotation #$quotationnum not found!" unless $quotation;
+
+my $menubar;
+if ( my $custnum = $quotation->custnum ) {
+ my $display_custnum = $quotation->cust_main->display_custnum;
+ $menubar = menubar(
+ emt("View this customer (#[_1])",$display_custnum) => "${p}view/cust_main.cgi?$custnum",
+ );
+} elsif ( my $prospectnum = $quotation->prospectnum ) {
+ $menubar = menubar(
+ emt("View this prospect (#[_1])",$prospectnum) => "${p}view/prospect_main.html?$prospectnum",
+ );
+}
+
+my $link = "quotationnum=$quotationnum";
+#$link .= ';template='. uri_escape($template) if $template;
+#$link .= ';notice_name='. $notice_name if $notice_name;
+
+
+</%init>
diff --git a/httemplate/view/svc_acct.cgi b/httemplate/view/svc_acct.cgi
index 441e4945d..3ad21bb4d 100755
--- a/httemplate/view/svc_acct.cgi
+++ b/httemplate/view/svc_acct.cgi
@@ -47,6 +47,7 @@
<& svc_acct/basics.html,
'svc_acct' => $svc_acct,
'part_svc' => $part_svc,
+ 'cust_svc' => $cust_svc,
%gopt,
&>
@@ -75,6 +76,11 @@
<& elements/svc_export_settings.html, $svc_acct &>
+% if ( $conf->config('ticket_system') ) {
+<& /elements/table-tickets.html, object => $cust_svc &>
+% }
+
+<BR>
<% joblisting({'svcnum'=>$svcnum}, 1) %>
<& /elements/footer.html &>
diff --git a/httemplate/view/svc_acct/basics.html b/httemplate/view/svc_acct/basics.html
index 8f180b648..bcd84696e 100644
--- a/httemplate/view/svc_acct/basics.html
+++ b/httemplate/view/svc_acct/basics.html
@@ -1,6 +1,9 @@
<% &ntable("#cccccc") %><TR><TD><% &ntable("#cccccc",2) %>
<& /view/elements/tr.html, label=>mt('Service'), value=>$part_svc->svc &>
+% if ( $opt{cust_svc}->agent_svcid ) {
+ <& /view/elements/tr.html, label=>mt('Legacy ID'), value=>$opt{cust_svc}->agent_svcid &>
+% }
<& /view/elements/tr.html, label=>mt('Username'), value=>$svc_acct->username &>
<& /view/elements/tr.html, label=>mt('Domain'), value=>$domain &>
diff --git a/httemplate/view/svc_domain.cgi b/httemplate/view/svc_domain.cgi
index 3938a3406..fcccd74b4 100755
--- a/httemplate/view/svc_domain.cgi
+++ b/httemplate/view/svc_domain.cgi
@@ -36,6 +36,10 @@
<% include('elements/svc_export_settings.html', $svc_domain) %>
+% if ( $conf->config('ticket_system') ) {
+<& /elements/table-tickets.html, object => $cust_svc &>
+% }
+
<% joblisting({'svcnum'=>$svcnum}, 1) %>
<% include('/elements/footer.html') %>
diff --git a/httemplate/view/svc_external.cgi b/httemplate/view/svc_external.cgi
index 77679d81c..72e553598 100644
--- a/httemplate/view/svc_external.cgi
+++ b/httemplate/view/svc_external.cgi
@@ -23,6 +23,11 @@
</TABLE></TD></TR></TABLE>
+
+% if ( $conf->config('ticket_system') ) {
+<& /elements/table-tickets.html, object => $cust_svc &>
+% }
+
<BR><% joblisting({'svcnum'=>$svcnum}, 1) %>
<% include('/elements/footer.html') %>
diff --git a/httemplate/view/svc_forward.cgi b/httemplate/view/svc_forward.cgi
index 15b5ae56f..2cb78ebd5 100755
--- a/httemplate/view/svc_forward.cgi
+++ b/httemplate/view/svc_forward.cgi
@@ -53,6 +53,10 @@
<% include('elements/svc_export_settings.html', $svc_forward) %>
+% if ( $conf->config('ticket_system') ) {
+<& /elements/table-tickets.html, object => $cust_svc &>
+% }
+
<% joblisting({'svcnum'=>$svcnum}, 1) %>
<% include('/elements/footer.html') %>
diff --git a/httemplate/view/svc_www.cgi b/httemplate/view/svc_www.cgi
index 935d139e9..fbb02a00f 100644
--- a/httemplate/view/svc_www.cgi
+++ b/httemplate/view/svc_www.cgi
@@ -49,6 +49,10 @@
</TABLE>
<BR>
+% if ( $conf->config('ticket_system') ) {
+<& /elements/table-tickets.html, object => $cust_svc &>
+% }
+
<% joblisting({'svcnum'=>$svcnum}, 1) %>
<% include('/elements/footer.html') %>