summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate')
-rw-r--r--httemplate/REST/1.0/phone_avail3
-rw-r--r--httemplate/browse/commission_schedule.html70
-rw-r--r--httemplate/browse/log_email.html2
-rwxr-xr-xhttemplate/browse/part_pkg_taxproduct/cch.html2
-rwxr-xr-xhttemplate/browse/part_pkg_taxproduct/suretax.html36
-rwxr-xr-xhttemplate/browse/part_svc.cgi50
-rw-r--r--httemplate/browse/tower.html41
-rw-r--r--httemplate/config/config-view.cgi74
-rw-r--r--httemplate/docs/about.html2
-rw-r--r--httemplate/docs/credits.html2
-rw-r--r--httemplate/edit/commission_schedule.html53
-rwxr-xr-xhttemplate/edit/cust_main.cgi11
-rwxr-xr-xhttemplate/edit/cust_pay.cgi4
-rw-r--r--httemplate/edit/cust_pay_pending.html19
-rwxr-xr-xhttemplate/edit/cust_refund.cgi4
-rw-r--r--httemplate/edit/elements/ApplicationCommon.html38
-rw-r--r--httemplate/edit/elements/edit.html2
-rw-r--r--httemplate/edit/elements/part_svc_column.html10
-rw-r--r--httemplate/edit/log_email.html15
-rw-r--r--httemplate/edit/msgcat.html2
-rw-r--r--httemplate/edit/part_event.html4
-rwxr-xr-xhttemplate/edit/part_pkg.cgi85
-rwxr-xr-xhttemplate/edit/part_referral.html2
-rwxr-xr-xhttemplate/edit/part_svc.cgi52
-rw-r--r--httemplate/edit/payment_gateway.html23
-rw-r--r--httemplate/edit/process/bulk-cust_main_county.html2
-rw-r--r--httemplate/edit/process/bulk-cust_svc-pkgnum.html2
-rw-r--r--httemplate/edit/process/cgp_rule-simplified.html2
-rw-r--r--httemplate/edit/process/change-cust_pkg.html2
-rw-r--r--httemplate/edit/process/commission_schedule.html36
-rw-r--r--httemplate/edit/process/credit-cust_bill_pkg.html2
-rwxr-xr-xhttemplate/edit/process/cust_credit-pkgnum.html2
-rwxr-xr-xhttemplate/edit/process/cust_credit.cgi2
-rw-r--r--httemplate/edit/process/cust_location-censustract.html2
-rw-r--r--httemplate/edit/process/cust_location.cgi2
-rwxr-xr-xhttemplate/edit/process/cust_main.cgi23
-rw-r--r--httemplate/edit/process/cust_main_attach.cgi2
-rwxr-xr-xhttemplate/edit/process/cust_main_county-add.cgi2
-rwxr-xr-xhttemplate/edit/process/cust_main_county-expand.cgi2
-rwxr-xr-xhttemplate/edit/process/cust_main_note.cgi2
-rw-r--r--httemplate/edit/process/cust_pay-no_auto_apply.cgi2
-rwxr-xr-xhttemplate/edit/process/cust_pay-pkgnum.html2
-rwxr-xr-xhttemplate/edit/process/cust_pay.cgi4
-rw-r--r--httemplate/edit/process/cust_pay_pending.html11
-rw-r--r--httemplate/edit/process/cust_pkg_detail.html2
-rw-r--r--httemplate/edit/process/cust_pkg_discount.html2
-rw-r--r--httemplate/edit/process/cust_pkg_quantity.html2
-rw-r--r--httemplate/edit/process/cust_pkg_salesnum.html2
-rwxr-xr-xhttemplate/edit/process/cust_refund.cgi2
-rw-r--r--httemplate/edit/process/cust_tax_adjustment.html2
-rw-r--r--httemplate/edit/process/detach-cust_pkg.html2
-rwxr-xr-xhttemplate/edit/process/domain_record.cgi2
-rw-r--r--httemplate/edit/process/elements/ApplicationCommon.html5
-rw-r--r--httemplate/edit/process/elements/process.html23
-rw-r--r--httemplate/edit/process/part_event.html4
-rwxr-xr-xhttemplate/edit/process/part_pkg.cgi5
-rw-r--r--httemplate/edit/process/prospect_main.html11
-rw-r--r--httemplate/edit/process/quick-charge.cgi14
-rw-r--r--httemplate/edit/process/quotation_convert.html7
-rw-r--r--httemplate/edit/process/quotation_pkg_detail.html10
-rw-r--r--httemplate/edit/process/svc_dsl.html2
-rw-r--r--httemplate/edit/process/tower.html1
-rw-r--r--httemplate/edit/prospect_main.html16
-rw-r--r--httemplate/edit/quick-charge.html28
-rw-r--r--httemplate/edit/quotation_pkg_detail.html7
-rwxr-xr-xhttemplate/edit/svc_acct.cgi32
-rw-r--r--httemplate/edit/svc_acct/communigate.html2
-rw-r--r--httemplate/edit/tower.html8
-rw-r--r--httemplate/elements/checkboxes.html6
-rw-r--r--httemplate/elements/commission_rate.html68
-rw-r--r--httemplate/elements/create_uri_query2
-rw-r--r--httemplate/elements/cust_payby.html15
-rw-r--r--httemplate/elements/error.html1
-rw-r--r--httemplate/elements/errorpage.html3
-rw-r--r--httemplate/elements/freeside-menu.css10
-rw-r--r--httemplate/elements/freeside.css4
-rw-r--r--httemplate/elements/header-full.html242
-rw-r--r--httemplate/elements/header-popup.html4
-rw-r--r--httemplate/elements/header.html232
-rw-r--r--httemplate/elements/location.html4
-rw-r--r--httemplate/elements/menu.html12
-rw-r--r--httemplate/elements/notify-tickets.html27
-rw-r--r--httemplate/elements/page_pref.js10
-rw-r--r--httemplate/elements/printtofit.js26
-rw-r--r--httemplate/elements/progress-init.html14
-rw-r--r--httemplate/elements/select-areacode.html10
-rw-r--r--httemplate/elements/select-exchange.html11
-rw-r--r--httemplate/elements/select-phonenum.html10
-rw-r--r--httemplate/elements/select-region.html10
-rw-r--r--httemplate/elements/select-rt-customfield.html34
-rw-r--r--httemplate/elements/select-rt-queue.html24
-rw-r--r--httemplate/elements/select-svc.html73
-rw-r--r--httemplate/elements/select-terms.html5
-rw-r--r--httemplate/elements/select.html2
-rw-r--r--httemplate/elements/selectlayersx.html248
-rw-r--r--httemplate/elements/standardize_locations.js2
-rw-r--r--httemplate/elements/table-tickets.html11
-rw-r--r--httemplate/elements/topreload.js5
-rw-r--r--httemplate/elements/tower_sector.html5
-rw-r--r--httemplate/elements/tr-cust_svc_cancel.html14
-rw-r--r--httemplate/elements/tr-freq.html2
-rw-r--r--httemplate/elements/tr-input-locale-text.html120
-rw-r--r--httemplate/elements/tr-select-cust-part_pkg.html2
-rwxr-xr-xhttemplate/elements/tr-select-reason.html3
-rw-r--r--httemplate/elements/tr-select-router_block_ip.html2
-rw-r--r--httemplate/elements/tr-select-rt-queue.html7
-rw-r--r--httemplate/elements/tr-selectlayersx.html25
-rw-r--r--httemplate/elements/xmlhttp.html2
-rw-r--r--httemplate/misc/areacodes.cgi11
-rwxr-xr-xhttemplate/misc/cancel_pkg.html33
-rwxr-xr-xhttemplate/misc/change_pkg_date.html (renamed from httemplate/misc/change_pkg_start.html)33
-rw-r--r--httemplate/misc/confirm-address_standardize.html90
-rw-r--r--httemplate/misc/cust_credit-import.html11
-rwxr-xr-xhttemplate/misc/cust_main-cancel.cgi2
-rwxr-xr-xhttemplate/misc/cust_main-suspend.cgi2
-rwxr-xr-xhttemplate/misc/cust_main-unsuspend.cgi2
-rw-r--r--httemplate/misc/delete-addr_range.html2
-rwxr-xr-xhttemplate/misc/delete-rate_detail.html2
-rw-r--r--httemplate/misc/did_order_confirmed.html2
-rwxr-xr-xhttemplate/misc/disable-cust_location.cgi2
-rw-r--r--httemplate/misc/disable-msg_template.cgi2
-rw-r--r--httemplate/misc/email-customers.html50
-rw-r--r--httemplate/misc/exchanges.cgi11
-rw-r--r--httemplate/misc/make_appointment.html32
-rw-r--r--httemplate/misc/phonenums.cgi11
-rw-r--r--httemplate/misc/post_fsinc-invoice.cgi43
-rwxr-xr-xhttemplate/misc/process/bulk_pkg_increment_bill.cgi2
-rwxr-xr-xhttemplate/misc/process/cancel_pkg.html12
-rw-r--r--httemplate/misc/process/change_pkg_contact.html2
-rwxr-xr-xhttemplate/misc/process/change_pkg_date.html (renamed from httemplate/misc/process/change_pkg_start.html)32
-rw-r--r--httemplate/misc/process/cust_bill-promised_date.html2
-rwxr-xr-xhttemplate/misc/process/delay_susp_pkg.html2
-rwxr-xr-xhttemplate/misc/process/enable_or_disable_tax.html2
-rw-r--r--httemplate/misc/process/nms-add_iface.html2
-rw-r--r--httemplate/misc/process/nms-add_router.html2
-rw-r--r--httemplate/misc/process/payment.cgi2
-rwxr-xr-xhttemplate/misc/process/recharge_svc.html2
-rw-r--r--httemplate/misc/process/set_page_pref.html12
-rwxr-xr-xhttemplate/misc/process/unhold_pkg.html2
-rwxr-xr-xhttemplate/misc/process/void-cust_bill.html2
-rw-r--r--httemplate/misc/reason-merge.html2
-rw-r--r--httemplate/misc/regions.cgi11
-rw-r--r--httemplate/misc/sector-create_map.html10
-rw-r--r--httemplate/misc/tax-fetch_and_import.cgi3
-rw-r--r--httemplate/misc/tax-fetch_and_replace.cgi3
-rw-r--r--httemplate/misc/tower-export.html36
-rwxr-xr-xhttemplate/misc/void-cust_credit.html2
-rw-r--r--httemplate/misc/xmlhttp-address_standardize.html8
-rw-r--r--httemplate/misc/xmlhttp-ticket-update.html23
-rw-r--r--httemplate/pref/pref-process.html1
-rw-r--r--httemplate/pref/pref.html21
-rw-r--r--httemplate/search/agent_credit_payment.html155
-rw-r--r--httemplate/search/contact.html12
-rw-r--r--httemplate/search/cust_bill_pay_pkg.html24
-rwxr-xr-xhttemplate/search/cust_credit.html11
-rw-r--r--httemplate/search/cust_credit_bill_pkg.html10
-rw-r--r--httemplate/search/cust_credit_source_bill_pkg.html2
-rwxr-xr-xhttemplate/search/cust_credit_void.html10
-rw-r--r--httemplate/search/cust_event.html120
-rwxr-xr-xhttemplate/search/cust_main.cgi53
-rwxr-xr-xhttemplate/search/cust_pay_pending.html2
-rw-r--r--httemplate/search/cust_pkg-date.html90
-rw-r--r--httemplate/search/cust_pkg_churn.html21
-rw-r--r--httemplate/search/cust_svc.html101
-rwxr-xr-xhttemplate/search/elements/cust_pay_or_refund.html219
-rw-r--r--httemplate/search/elements/gmap.html20
-rw-r--r--httemplate/search/elements/grouped-search/core14
-rw-r--r--httemplate/search/elements/grouped-search/html6
-rw-r--r--httemplate/search/elements/report_cust_pay_or_refund.html6
-rw-r--r--httemplate/search/elements/search.html13
-rw-r--r--httemplate/search/log.html39
-rwxr-xr-xhttemplate/search/report_agent_credit_payment.html30
-rw-r--r--httemplate/search/report_cust_bill_pay_pkg.html7
-rw-r--r--httemplate/search/report_cust_bill_void.html2
-rw-r--r--httemplate/search/report_cust_event.html34
-rw-r--r--httemplate/search/report_sqlradius_usage-custnum.html71
-rw-r--r--httemplate/search/report_sqlradius_usage.html1
-rwxr-xr-xhttemplate/search/report_tax-xls.cgi35
-rw-r--r--httemplate/search/report_tax.cgi77
-rwxr-xr-xhttemplate/search/report_tax.html29
-rwxr-xr-xhttemplate/search/report_unprovisioned_services.html1
-rw-r--r--httemplate/search/sector.html104
-rw-r--r--httemplate/search/sqlradius_usage.html93
-rwxr-xr-xhttemplate/search/svc_acct.cgi23
-rwxr-xr-xhttemplate/search/svc_broadband-map.html16
-rwxr-xr-xhttemplate/search/svc_broadband.cgi56
-rw-r--r--httemplate/search/svc_circuit.cgi21
-rwxr-xr-xhttemplate/search/svc_dish.cgi25
-rwxr-xr-xhttemplate/search/svc_domain.cgi25
-rwxr-xr-xhttemplate/search/svc_external.cgi25
-rw-r--r--httemplate/search/svc_fiber.html21
-rwxr-xr-xhttemplate/search/svc_forward.cgi25
-rw-r--r--httemplate/search/svc_hardware.cgi29
-rw-r--r--httemplate/search/svc_phone.cgi20
-rwxr-xr-xhttemplate/search/svc_www.cgi25
-rw-r--r--httemplate/search/tax_sales.cgi2
-rwxr-xr-xhttemplate/view/cust_bill.cgi20
-rwxr-xr-xhttemplate/view/cust_main.cgi20
-rw-r--r--httemplate/view/cust_main/billing.html73
-rw-r--r--httemplate/view/cust_main/change_history.html64
-rw-r--r--httemplate/view/cust_main/menu.html18
-rw-r--r--httemplate/view/cust_main/packages/package.html10
-rw-r--r--httemplate/view/cust_main/packages/services.html20
-rw-r--r--httemplate/view/cust_main/packages/status.html61
-rw-r--r--httemplate/view/cust_main/payment_history.html39
-rw-r--r--httemplate/view/cust_main/payment_history/pending_payment.html1
-rw-r--r--httemplate/view/cust_pay.html13
-rw-r--r--httemplate/view/elements/svc_edit_link.html10
-rwxr-xr-xhttemplate/view/quotation.html12
-rw-r--r--httemplate/view/sector_map-png.cgi8
-rw-r--r--httemplate/view/svc_acct/basics.html32
211 files changed, 3698 insertions, 1175 deletions
diff --git a/httemplate/REST/1.0/phone_avail b/httemplate/REST/1.0/phone_avail
index ef9d3e7f0..c6638f48f 100644
--- a/httemplate/REST/1.0/phone_avail
+++ b/httemplate/REST/1.0/phone_avail
@@ -17,7 +17,8 @@ if ( scalar(@exports) > 1 ) {
die "no DID providing export attached to svcpart $svcpart";
}
my $export = $exports[0];
-
+
+# XXX no error handling
my $phonenums = $export->get_dids( map { $_ => scalar($cgi->param($_)) }
qw( ratecenter state areacode exchange )
);
diff --git a/httemplate/browse/commission_schedule.html b/httemplate/browse/commission_schedule.html
new file mode 100644
index 000000000..5a4f9840e
--- /dev/null
+++ b/httemplate/browse/commission_schedule.html
@@ -0,0 +1,70 @@
+<& elements/browse.html,
+ 'title' => "Commission schedules",
+ 'name' => "commission schedules",
+ 'menubar' => [ 'Add a new schedule' =>
+ $p.'edit/commission_schedule.html'
+ ],
+ 'query' => { 'table' => 'commission_schedule', },
+ 'count_query' => 'SELECT COUNT(*) FROM commission_schedule',
+ 'header' => [ '#',
+ 'Name',
+ 'Rates',
+ ],
+ 'fields' => [ 'schedulenum',
+ 'schedulename',
+ $rates_sub,
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ ],
+ 'disable_total' => 1,
+&>
+<%init>
+
+my $money_char = FS::Conf->new->config('money_char') || '$';
+
+my $ordinal_sub = sub {
+ # correct from 1 to 12...
+ my $num = shift;
+ $num == 1 ? '1st' :
+ $num == 2 ? '2nd' :
+ $num == 3 ? '3rd' :
+ $num . 'th'
+};
+
+my $rates_sub = sub {
+ my $schedule = shift;
+ my @rates = sort { $a->cycle <=> $b->cycle } $schedule->commission_rate;
+ my @data;
+ my $basis = emt(lc( $FS::commission_schedule::basis_options{$schedule->basis} ));
+ foreach my $rate (@rates) {
+ my $desc = '';
+ if ( $rate->amount > 0 ) {
+ $desc = $money_char . sprintf('%.2f', $rate->amount);
+ }
+ if ( $rate->percent > 0 ) {
+ $desc .= ' + ' if $desc;
+ $desc .= $rate->percent . '% ' . emt('of') . ' ' . $basis;
+ }
+ next if !$desc;
+ $desc = &$ordinal_sub($rate->cycle) . ' ' . emt('invoice') .
+ ':&nbsp;' . $desc;
+
+ push @data,
+ [
+ {
+ 'data' => $desc,
+ 'align' => 'right',
+ }
+ ];
+ }
+ \@data;
+};
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $link = [ $p.'edit/commission_schedule.html?', 'schedulenum' ];
+
+</%init>
diff --git a/httemplate/browse/log_email.html b/httemplate/browse/log_email.html
index 0f64dd454..007ea6f74 100644
--- a/httemplate/browse/log_email.html
+++ b/httemplate/browse/log_email.html
@@ -21,7 +21,7 @@
],
'fields' => [ 'logemailnum',
sub { $_[0]->context || '(all)' },
- sub { $FS::Log::LEVELS[$_[0]->min_level] },
+ sub { $FS::Log::LEVELS{$_[0]->min_level} },
'msgname',
'to_addr',
$actions,
diff --git a/httemplate/browse/part_pkg_taxproduct/cch.html b/httemplate/browse/part_pkg_taxproduct/cch.html
index b901bad9f..1f4f53d5f 100755
--- a/httemplate/browse/part_pkg_taxproduct/cch.html
+++ b/httemplate/browse/part_pkg_taxproduct/cch.html
@@ -97,7 +97,7 @@ $cgi->delete('tax_customer');
if ( $tax_group || $tax_item || $tax_provider || $tax_customer ) {
- push @menubar, 'View all tax products' => $p.'browse/part_pkg_taxproduct.cgi';
+ push @menubar, 'View all tax products' => $p.'browse/part_pkg_taxproduct/cch.html';
}
$cgi->param('dummy', 1);
diff --git a/httemplate/browse/part_pkg_taxproduct/suretax.html b/httemplate/browse/part_pkg_taxproduct/suretax.html
index 667c07ee9..9c00c5c69 100755
--- a/httemplate/browse/part_pkg_taxproduct/suretax.html
+++ b/httemplate/browse/part_pkg_taxproduct/suretax.html
@@ -16,9 +16,6 @@
'nohtmlheader' => 1,
'disable_total' => 1,
&>
-<style>
-input { float: right}
-</style>
<script src="<% $fsurl %>elements/jquery.js"></script>
<script>
var category_labels = <% encode_json(\%category_labels) %>;
@@ -69,20 +66,31 @@ function select_taxproduct(taxproductnum, description) {
}
</script>
-<DIV STYLE="width: 50%">
+<BR>
<FORM NAME="myform">
- <label for="new_taxproduct">New tax product code</label>
- <input type="text" size="6" name="new_taxproduct" id="new_taxproduct">
- <br>
- <label for="new_category_desc">Category</label>
- <input type="text" name="new_category_desc" id="new_category_desc" disabled=1>
- <br>
- <label for="new_taxproduct_desc">Product</label>
- <input type="text" name="new_taxproduct_desc" id="new_taxproduct_desc">
- <br>
+ <FONT SIZE="+1"><B><% emt('Add tax product') %></B></FONT>
+ <% ntable('#cccccc', 2) %>
+ <& /elements/tr-input-text.html,
+ 'label' => emt('Product code'),
+ 'field' => 'new_taxproduct',
+ 'id' => 'new_taxproduct',
+ 'size' => 6,
+ 'maxlength' => 6,
+ &>
+ <& /elements/tr-input-text.html,
+ 'label' => emt('Category'),
+ 'field' => 'new_category_desc',
+ 'id' => 'new_category_desc',
+ 'disabled' => 1
+ &>
+ <& /elements/tr-input-text.html,
+ 'label' => emt('Product'),
+ 'field' => 'new_taxproduct_desc',
+ 'id' => 'new_taxproduct_desc',
+ &>
+ </table>
<input type="button" id="new_taxproduct_submit" disabled=1 value="Add">
</FORM>
-</DIV>
<%shared>
# populate dropdown
diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi
index 88f8d8d19..b9474636d 100755
--- a/httemplate/browse/part_svc.cgi
+++ b/httemplate/browse/part_svc.cgi
@@ -112,16 +112,36 @@ function part_export_areyousure(href) {
</TD>
% }
- <TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>"><A HREF="<% $url %>">
- <% $part_svc->svc %></A></TD>
+ <TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <A HREF="<% $url %>">
+ <% $part_svc->svc %>
+ </A>
+% # any alternate names of the service
+% my %msgcat = map { $_->locale => $_ } $part_svc->part_svc_msgcat;
+% my %labels = map { $_ => FS::Locales->description($_) } keys %msgcat;
+% my @locales = sort { $labels{$a} cmp $labels{$b} } keys %msgcat;
+% if ( @locales ) {
+ <BR>
+ <FONT SIZE="-1">
+% foreach my $locale (@locales) {
+ <% $labels{$locale} %>: <% $msgcat{$locale}->get('svc') %>
+ <BR>
+% }
+ </FONT>
+% }
+ </TD>
<TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>">
<% $svcdb %></TD>
<TD ROWSPAN=<% $rowspan %> CLASS="grid" BGCOLOR="<% $bgcolor %>">
- <FONT COLOR="#00CC00"><B><% $num_active_cust_svc{$part_svc->svcpart} %></B></FONT>&nbsp;<% $num_active_cust_svc{$part_svc->svcpart} ? svc_url( 'ahref' => 1, 'm' => $m, 'action' => 'search', 'part_svc' => $part_svc, 'query' => "svcpart=". $part_svc->svcpart ) : '<A NAME="zero">' %>active</A>
-
-% if ( $num_active_cust_svc{$part_svc->svcpart} ) {
+% my $svcurl_active = svc_url( 'ahref' => 1, 'm' => $m, 'action' => 'search', 'part_svc' => $part_svc, 'query' => "svcpart=". $part_svc->svcpart . "&cancelled=0");
+% my $svcurl_cancel = svc_url( 'ahref' => 1, 'm' => $m, 'action' => 'search', 'part_svc' => $part_svc, 'query' => "svcpart=". $part_svc->svcpart . "&cancelled=1");
+ <FONT COLOR="#00CC00"><B><% $num_cust_svc_active{$part_svc->svcpart} %></B></FONT>&nbsp;<% $num_cust_svc_active{$part_svc->svcpart} ? $svcurl_active : '' %>active<% $num_cust_svc_active{$part_svc->svcpart} ? '</A>' : '' %>
+% if ( $num_cust_svc_cancelled{$part_svc->svcpart} ) {
+ <BR><FONT COLOR="#FF0000"><B><% $num_cust_svc_cancelled{$part_svc->svcpart} %></B></FONT>&nbsp;<% $svcurl_cancel %>cancelled</A>
+% }
+% if ( $num_cust_svc{$part_svc->svcpart} ) {
<BR><FONT SIZE="-1">[ <A HREF="<%$p%>edit/bulk-cust_svc.html?svcpart=<% $part_svc->svcpart %>">change</A> ]</FONT>
% }
@@ -245,11 +265,25 @@ my @part_svc =
qsearch('part_svc', \%search );
my $total = scalar(@part_svc);
-my %num_active_cust_svc = map { $_->svcpart => $_->num_cust_svc } @part_svc;
+## The Active/Cancelled distinction is a bit awkward,
+## active currently includes unattached and suspended services,
+## but we've previously referred to EVERY existing cust_svc as "Active",
+## and we don't really want to know numbers by individual package status,
+## so for now the UI will distinguish these as "Active" and "Cancelled",
+## but please let's not go so far as to introduce the idea of "Service Status"
+
+my %num_cust_svc_active;
+my %num_cust_svc_cancelled;
+my %num_cust_svc;
+foreach my $part_svc (@part_svc) {
+ $num_cust_svc{$part_svc->svcpart} = $part_svc->num_cust_svc;
+ $num_cust_svc_cancelled{$part_svc->svcpart} = $part_svc->num_cust_svc_cancelled;
+ $num_cust_svc_active{$part_svc->svcpart} = $num_cust_svc{$part_svc->svcpart} - $num_cust_svc_cancelled{$part_svc->svcpart};
+}
if ( $cgi->param('orderby') eq 'active' ) {
- @part_svc = sort { $num_active_cust_svc{$b->svcpart} <=>
- $num_active_cust_svc{$a->svcpart} } @part_svc;
+ @part_svc = sort { $num_cust_svc{$b->svcpart} <=>
+ $num_cust_svc{$a->svcpart} } @part_svc;
} elsif ( $cgi->param('orderby') eq 'svc' ) {
@part_svc = sort { lc($a->svc) cmp lc($b->svc) } @part_svc;
}
diff --git a/httemplate/browse/tower.html b/httemplate/browse/tower.html
index e2f9fd0bd..16e44c6c0 100644
--- a/httemplate/browse/tower.html
+++ b/httemplate/browse/tower.html
@@ -1,22 +1,25 @@
-<% include( 'elements/browse.html',
- 'title' => 'Towers',
- 'name' => 'towers',
- 'menubar' => [ 'Add a new tower' =>
- $p.'edit/tower.html',
- ],
- 'query' => { 'table' => 'tower', },
- 'count_query' => 'SELECT COUNT(*) FROM tower',
- 'disableable' => 1,
- 'disabled_statuspos' => 1,
- 'header' => [ 'Name', 'Location', 'Sectors', ],
- 'fields' => [ $tower_sub,
- $coord_sub,
- $sector_sub,
- ],
- 'links' => [ ],
- 'cell_style' => [ $tagdesc_style ],
- )
-%>
+<& elements/browse.html,
+ 'title' => 'Towers',
+ 'name' => 'towers',
+ 'menubar' => [ 'Add a new tower' =>
+ $p.'edit/tower.html',
+ 'Sector coverage maps' =>
+ $p.'search/sector.html',
+ 'Download CSV for towercoverage.com' =>
+ $p.'misc/tower-export.html?format=tc'
+ ],
+ 'query' => { 'table' => 'tower', },
+ 'count_query' => 'SELECT COUNT(*) FROM tower',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 1,
+ 'header' => [ 'Name', 'Location', 'Sectors', ],
+ 'fields' => [ $tower_sub,
+ $coord_sub,
+ $sector_sub,
+ ],
+ 'links' => [ ],
+ 'cell_style' => [ $tagdesc_style ],
+&>
<%init>
die "access denied"
diff --git a/httemplate/config/config-view.cgi b/httemplate/config/config-view.cgi
index a2e908847..248af37cb 100644
--- a/httemplate/config/config-view.cgi
+++ b/httemplate/config/config-view.cgi
@@ -7,7 +7,7 @@ Click on a configuration value to change it.
%
% if ( $cgi->param('showagent') ) {
% $cgi->param('showagent', 0);
- ( <a href="<% $cgi->self_url %>">hide agent overrides</a> )
+ ( <a href="<% $cgi->self_url %>">show global configuration</a> )
% $cgi->param('showagent', 1);
% } else {
% $cgi->param('showagent', 1);
@@ -58,24 +58,28 @@ invoice language options:
% foreach my $section (@sections) {
- <A NAME="<% $section || 'unclassified' %>"></A>
+ <A NAME="<% $section || 'misc' %>"></A>
<FONT SIZE="-2">
% foreach my $nav_section (@sections) {
%
% if ( $section eq $nav_section ) {
- [<A NAME="not<% $nav_section || 'unclassified' %>" style="background-color: #cccccc"><% ucfirst($nav_section || 'unclassified') %></A>]
+ <A NAME="not<% $nav_section || 'misc' %>" style="background-color: #cccccc"><% section_title($nav_section) %></A>
% } else {
- [<A HREF="#<% $nav_section || 'unclassified' %>"><% ucfirst($nav_section || 'unclassified') %></A>]
+ <A HREF="#<% $nav_section || 'misc' %>"><% section_title($nav_section) %></A>
% }
%
+% unless ( $nav_section eq $sections[-1] ) {
+ |
+% }
+%
% }
</FONT><BR>
<TABLE BGCOLOR="#cccccc" BORDER=1 CELLSPACING=0 CELLPADDING=0 BORDERCOLOR="#999999">
<tr>
<th colspan="2" bgcolor="#dcdcdc">
- <% ucfirst($section || 'unclassified') %>
+ <% section_title($section) %>
% if ( $curuser->option('show_confitem_counts') ) {
(<% scalar( @{ $section_items{$section} } ) %> items)
% }
@@ -114,7 +118,10 @@ invoice language options:
% foreach my $agent ( @agents ) {
% my $agentnum = $agent ? $agent->agentnum : '';
%
-% next if $section eq 'deprecated' && ! $conf->exists($i->key, $agentnum);
+% next if $section eq 'deprecated'
+% && ( ! $conf->exists($i->key, $agentnum)
+% || $conf->config($i->key, $agentnum) eq ''
+% );
%
% my $label = $i->key;
% $label = '['. $agent->agent. "] $label"
@@ -125,16 +132,20 @@ invoice language options:
% ";agentnum=$agentnum" . ($locale ? ";locale=$locale" : '');
<tr>
- <td><% include('/elements/popup_link.html',
- 'action' => $action,
- 'width' => $width,
- 'height' => $height,
- 'actionlabel' => 'Enter configuration value',
- 'label' => "<b>$label</b>",
- 'aname' => $i->key, #agentnum
- # if $cgi->param('showagent')?
+% unless ( $cgi->param('showagent') ) {
+ <td><% include('/elements/popup_link.html',
+ 'action' => $action,
+ 'width' => $width,
+ 'height' => $height,
+ 'actionlabel' => 'Enter configuration value',
+ 'label' => "<b>$label</b>",
+ 'aname' => $i->key,
)
- %>: <% $i->description %>
+ %>:
+% } else {
+ <td><b><% $label %></b>:
+% }
+ <% $i->description %>
% if ( $agent && $cgi->param('showagent') ) {
% my $confnum = $conf->conf( $i->key, $agent->agentnum, 1 )->confnum;
(<A HREF="javascript:areyousure('delete this agent override', 'config-delete.cgi?confnum=<% $confnum %>;redirect=config_view_showagent')">delete agent override</A>)
@@ -292,7 +303,7 @@ invoice language options:
% if ( @add_agents ) {
<tr>
- <td>
+ <td COLSPAN=2>
<FORM>
Add <b><% $i->key %></b> override for
<% include('/elements/select-agent.html',
@@ -403,23 +414,36 @@ if ( $cgi->param('locale') =~ /^\w+_\w+$/ ) {
} elsif ($page_agent) {
$title = 'Agent Configuration for '. $page_agent->agent;
$title .= ", $locale_desc" if $locale;
+} elsif ( $cgi->param('showagent') ) {
+ $title = 'Agent Configuration Overrides'
} else {
$title = 'Global Configuration';
}
+my $show_over = $page_agent || $cgi->param('showagent');
+
my @config_items = grep { !defined($locale) or $_->per_locale }
- grep { $page_agent ? $_->per_agent : 1 }
- grep { $page_agent ? 1 : !$_->agentonly }
+ grep { $show_over ? $_->per_agent : 1 }
+ grep { $show_over ? 1 : !$_->agentonly }
$conf->config_items;
my @deleteable = qw( invoice_latexreturnaddress invoice_htmlreturnaddress );
my %deleteable = map { $_ => 1 } @deleteable;
my @sections = (qw(
- required billing taxation invoicing notification UI API self-service
- ticketing network_monitoring username password session shell BIND
- telephony
- ), '', 'deprecated'
+ important
+ billing payments payment_batching credit_cards e-checks taxation
+ packages suspension cancellation
+ printing print_services
+ invoicing invoice_email invoice_balances invoice_templates quotations
+ notification UI addresses customer_number customer_fields reporting
+ localization scalability backup
+ signup self-service self-service_skinning
+ API ticketing appointments network_monitoring
+ services
+ username password
+ telephony RADIUS wireless_broadband shell BIND hosting
+ ), '', qw( development deprecated )
);
my %section_items = ();
@@ -434,4 +458,10 @@ if ( $cgi->param('showagent') ) {
@all_agents = qsearch('agent', { 'disabled' => '' } );
}
+sub section_title {
+ my $sec = ucfirst(shift || 'misc');
+ $sec =~ s/_/ /;
+ $sec;
+}
+
</%init>
diff --git a/httemplate/docs/about.html b/httemplate/docs/about.html
index 4674af878..508648a19 100644
--- a/httemplate/docs/about.html
+++ b/httemplate/docs/about.html
@@ -56,7 +56,7 @@ GNU <b>Affero</b> General Public License.<BR>
% unless ( $agentnum ) {
<CENTER>
- <FONT SIZE="-3">"Half the world's a desert / Cannibals eat human brains for dessert" - D. Zero</FONT>
+ <FONT SIZE="-3">"I've heard it too many times to ignore it / Its's something that I'm supposed to be" - K. Frog</FONT>
</CENTER>
% }
diff --git a/httemplate/docs/credits.html b/httemplate/docs/credits.html
index 87cb08d7e..596c627e4 100644
--- a/httemplate/docs/credits.html
+++ b/httemplate/docs/credits.html
@@ -29,7 +29,6 @@
<BR>
<H3>Core Team</H3>
-Jeremy Davis<BR>
Nathan Kennedy<BR>
Ivan Kohler<BR>
Jonathan Prykop<BR>
@@ -41,6 +40,7 @@ Mark Wells<BR>
<H3>Core Emeritus</H3>
Peter Bowen<BR>
+Jeremy Davis<BR>
Jeff Finucane<BR>
Jason Hall<BR>
Kristian Hoffman<BR>
diff --git a/httemplate/edit/commission_schedule.html b/httemplate/edit/commission_schedule.html
new file mode 100644
index 000000000..c76a3618e
--- /dev/null
+++ b/httemplate/edit/commission_schedule.html
@@ -0,0 +1,53 @@
+<& elements/edit.html,
+ name_singular => 'schedule',
+ table => 'commission_schedule',
+ viewall_dir => 'browse',
+ fields => [ 'schedulename',
+ { field => 'reasonnum',
+ type => 'select-reason',
+ reason_class => 'R',
+ },
+ { field => 'basis',
+ type => 'select',
+ options => [ keys %FS::commission_schedule::basis_options ],
+ labels => { %FS::commission_schedule::basis_options },
+ },
+ { type => 'tablebreak-tr-title', value => 'Billing cycles' },
+ { field => 'commissionratenum',
+ type => 'commission_rate',
+ o2m_table => 'commission_rate',
+ m2_label => ' ',
+ m2_error_callback => $m2_error_callback,
+ colspan => 2,
+ },
+ ],
+ labels => { 'schedulenum' => '',
+ 'schedulename' => 'Name',
+ 'basis' => 'Based on',
+ 'commissionratenum' => '',
+ },
+&>
+<%init>
+
+my $m2_error_callback = sub {
+ my ($cgi, $object) = @_;
+
+ my @rates;
+ foreach my $k ( grep /^commissionratenum\d+/, $cgi->param ) {
+ my $num = $cgi->param($k);
+ my $cycle = $cgi->param($k.'_cycle');
+ my $amount = $cgi->param($k.'_amount');
+ my $percent = $cgi->param($k.'_percent');
+ if ($cycle > 0) {
+ push @rates, FS::commission_rate->new({
+ 'commissionratenum' => $num,
+ 'cycle' => $cycle,
+ 'amount' => $amount,
+ 'percent' => $percent,
+ });
+ }
+ }
+ @rates;
+};
+
+</%init>
diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi
index 39cddc021..b314d2d6e 100755
--- a/httemplate/edit/cust_main.cgi
+++ b/httemplate/edit/cust_main.cgi
@@ -203,12 +203,19 @@ if ( $cgi->param('error') ) {
my %locations;
for my $pre (qw(bill ship)) {
my %hash;
- foreach ( FS::cust_main->location_fields ) {
- $hash{$_} = scalar($cgi->param($pre.'_'.$_));
+ foreach my $locfield ( FS::cust_main->location_fields ) {
+ # don't search on lat/long, string values can cause qsearchs to die
+ next if grep {$_ eq $locfield} qw(latitude longitude);
+ $hash{$locfield} = scalar($cgi->param($pre.'_'.$locfield));
}
$hash{'custnum'} = $cgi->param('custnum');
$locations{$pre} = qsearchs('cust_location', \%hash)
|| FS::cust_location->new( \%hash );
+ # now set lat/long, for redisplay of entered values
+ foreach my $locfield ( qw(latitude longitude) ) {
+ my $locvalue = scalar($cgi->param($pre.'_'.$locfield));
+ $locations{$pre}->set($locfield,$locvalue);
+ }
}
if ( $same ) {
$locations{ship} = $locations{bill};
diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi
index 5d74365e7..116eeebd6 100755
--- a/httemplate/edit/cust_pay.cgi
+++ b/httemplate/edit/cust_pay.cgi
@@ -12,7 +12,7 @@
<% small_custview($custnum, $conf->config('countrydefault')) %>
% }
-<FORM NAME="PaymentForm" ACTION="<% popurl(1) %>process/cust_pay.cgi" METHOD=POST onSubmit="document.PaymentForm.submit.disabled=true">
+<FORM NAME="PaymentForm" ACTION="<% popurl(1) %>process/cust_pay.cgi" METHOD=POST onSubmit="document.PaymentForm.submitButton.disabled=true">
<INPUT TYPE="hidden" NAME="link" VALUE="<% $link %>">
<INPUT TYPE="hidden" NAME="linknum" VALUE="<% $linknum %>">
<INPUT TYPE="hidden" NAME="payby" VALUE="<% $payby %>">
@@ -114,7 +114,7 @@
</TABLE>
<BR>
-<INPUT TYPE="submit" VALUE="<% mt('Post payment') |h %>">
+<INPUT NAME="submitButton" TYPE="submit" VALUE="<% mt('Post payment') |h %>">
</FORM>
diff --git a/httemplate/edit/cust_pay_pending.html b/httemplate/edit/cust_pay_pending.html
index 0056bb925..7d480f319 100644
--- a/httemplate/edit/cust_pay_pending.html
+++ b/httemplate/edit/cust_pay_pending.html
@@ -4,6 +4,10 @@
<CENTER><FONT SIZE="+1"><B>Are you sure you want to delete this pending payment?</B></FONT></CENTER>
+% } elsif (( $action eq 'complete' ) and $authorized) {
+
+ <CENTER><FONT SIZE="+1"><B>Payment was authorized but not captured. Contact <% $cust_pay_pending->processor || 'the payment gateway' %> to establish the final disposition of this transaction.</B></FONT></CENTER>
+
% } elsif ( $action eq 'complete' ) {
<CENTER><FONT SIZE="+1"><B>No response was received from <% $cust_pay_pending->processor || 'the payment gateway' %> for this transaction. Check <% $cust_pay_pending->processor || 'the payment gateway' %>'s reporting and determine if this transaction completed successfully.</B></FONT></CENTER>
@@ -97,8 +101,6 @@
% } else {
-%# if ( $action eq 'complete' ) {
-
<INPUT TYPE="hidden" NAME="action" VALUE="">
<TR>
@@ -106,18 +108,25 @@
<BUTTON TYPE="button" onClick="document.pendingform.action.value = 'insert_cust_pay'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, transaction completed sucessfully.</BUTTON>
</TD>
-% if ( $action eq 'complete' ) {
+% if ( $action eq 'complete' ) {
<TD>&nbsp;&nbsp;&nbsp;</TD>
+% if ($authorized) {
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'reverse'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was reversed</BUTTON>
+ </TD>
+% } else {
<TD ALIGN="center">
<BUTTON TYPE="button" onClick="document.pendingform.action.value = 'decline'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was declined</BUTTON>
</TD>
+% }
<TD>&nbsp;&nbsp;&nbsp;</TD>
<TD ALIGN="center">
<BUTTON TYPE="button" onClick="document.pendingform.action.value = 'delete'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was not received</BUTTON>
</TD>
- </TR>
% }
+ </TR>
+
<TR><TD COLSPAN=5></TD></TR>
<TR>
@@ -156,6 +165,8 @@ my $cust_pay_pending =
})
or die 'unknown paypendingnum';
+my $authorized = ($cust_pay_pending->status eq 'authorized') ? 1 : 0;
+
my $conf = new FS::Conf;
my $money_char = $conf->config('money_char') || '$';
diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi
index bfcbfe725..32da4543e 100755
--- a/httemplate/edit/cust_refund.cgi
+++ b/httemplate/edit/cust_refund.cgi
@@ -10,7 +10,7 @@
<% small_custview($custnum, $conf->config('countrydefault')) %>
% }
-<FORM NAME="RefundForm" ACTION="<% $p1 %>process/cust_refund.cgi" METHOD=POST onSubmit="document.RefundForm.submit.disabled=true">
+<FORM NAME="RefundForm" ACTION="<% $p1 %>process/cust_refund.cgi" METHOD=POST onSubmit="document.RefundForm.submitButton.disabled=true">
<INPUT TYPE="hidden" NAME="popup" VALUE="<% $link %>">
<INPUT TYPE="hidden" NAME="refundnum" VALUE="">
<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
@@ -116,7 +116,7 @@
</TABLE>
<BR>
-<INPUT TYPE="submit" ID="confirm_refund_button" VALUE="<% mt('Post refund') |h %>" DISABLED>
+<INPUT TYPE="submit" NAME="submitButton" ID="confirm_refund_button" VALUE="<% mt('Post refund') |h %>" DISABLED>
</FORM>
diff --git a/httemplate/edit/elements/ApplicationCommon.html b/httemplate/edit/elements/ApplicationCommon.html
index acc3368b8..a531eaad4 100644
--- a/httemplate/edit/elements/ApplicationCommon.html
+++ b/httemplate/edit/elements/ApplicationCommon.html
@@ -3,46 +3,45 @@
Examples:
#cust_bill_pay
- include('elements/ApplicationCommon.html',
- 'form_action' => 'process/cust_bill_pay.cgi',
+ <& elements/ApplicationCommon.html,
+ 'form_action' => 'process/cust_bill_pay.cgi,
'src_table' => 'cust_pay',
'src_thing' => 'payment',
'dst_table' => 'cust_bill',
'dst_thing' => 'invoice',
- )
+ &>
#cust_credit_bill
- include('elements/ApplicationCommon.html',
- 'form_action' => 'process/cust_credit_bill.cgi',
+ <& elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_credit_bill.cgi,
'src_table' => 'cust_credit',
'src_thing' => 'credit',
'dst_table' => 'cust_bill',
'dst_thing' => 'invoice',
- )
+ &>
#cust_pay_refund
- include('elements/ApplicationCommon.html',
- 'form_action' => 'process/cust_pay_refund.cgi',
+ <& elements/ApplicationCommon.html',
+ 'form_action' => 'process/cust_pay_refund.cgi,
'src_table' => 'cust_pay',
'src_thing' => 'payment',
'dst_table' => 'cust_refund',
'dst_thing' => 'refund',
- )
+ &>
#cust_credit_refund
- include('elements/ApplicationCommon.html',
+ <& elements/ApplicationCommon.html,
'form_action' => 'process/cust_credit_refund.cgi',
'src_table' => 'cust_credit',
'src_thing' => 'credit',
'dst_table' => 'cust_refund',
'dst_thing' => 'refund',
- )
+ &>
</%doc>
+<& /elements/header-popup.html, "Apply $src_thing$to", '', 'onLoad="myOnLoadFunction();"' &>
-<% include('/elements/header-popup.html', "Apply $src_thing$to", '', 'onLoad="myOnLoadFunction();"') %>
-
-<% include('/elements/error.html') %>
+<& /elements/error.html &>
<P ID="ErrorMessage"></P>
<FORM ACTION="<% $p1. $opt{'form_action'} %>" NAME="ApplicationForm" ID="ApplicationForm" METHOD=POST>
@@ -158,6 +157,7 @@ function changed(what) {
% }
% $desc .= ' (default)';
% }
+% $total_owed = sprintf('%.2f', $total_owed + 0.00000001 ); #so 1.005 rounds to 1.01
% if ( $total_owed > 0 ) {
<% &{$row_generator}($key, $cbp, $desc, $total_owed, $amount, '') %>
% }
@@ -212,11 +212,10 @@ Apply to:
</TR>
</TABLE>
</CENTER>
-<% include( '/elements/xmlhttp.html',
+<& /elements/xmlhttp.html,
'url' => $p.'misc/xmlhttp-calculate_taxes.html',
'subs' => [ 'calculate_taxes' ],
- )
- %>
+&>
<SCRIPT TYPE="text/javascript">
function show_taxes(arg) {
@@ -392,8 +391,7 @@ function myOnLoadFunction () {
</SCRIPT>
-<% include('/elements/footer.html') %>
-
+<& /elements/footer-popup.html &>
<%init>
my %opt = @_;
@@ -427,6 +425,8 @@ $can_change_credit = 1
my $to = $dst_table eq 'cust_refund' ? ' to Refund' : '';
+$m->comp('/elements/handle_uri_query');
+
my($src_pkeyvalue, $amount, $dst_pkeyvalue, $src_amount);
if ( $cgi->param('error') ) {
$src_pkeyvalue = $cgi->param($src_pkey);
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index bbc9797dc..8dd15dcfb 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -650,7 +650,7 @@ Example:
var newrow = <% include(@layer_opt, html_only=>1) |js_string %>;
% #until the rest have html/js_only
-% if ( $type eq 'selectlayers' || $type =~ /^select-cgp_rule_/ ) {
+% if ( ($type eq 'selectlayers') || ($type eq 'selectlayersx') || ($type =~ /^select-cgp_rule_/) ) {
var newfunc = <% include(@layer_opt, js_only=>1) |js_string %>;
% } else {
var newfunc = '';
diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html
index bc679e577..816f3428b 100644
--- a/httemplate/edit/elements/part_svc_column.html
+++ b/httemplate/edit/elements/part_svc_column.html
@@ -132,7 +132,10 @@ my %communigate_fields = (
% } elsif ( $def->{'type'} eq 'select' ) {
%
% if ( $def->{'select_table'} ) {
- <& /elements/select-table.html,
+% # set the 'select_svc' flag to enable two-step selection of services
+% my $comp = '/elements/select-table.html';
+% $comp = '/elements/select-svc.html' if $def->{'select_svc'};
+ <& $comp,
'field' => $name,
'id' => $name.'_select',
'table' => $def->{'select_table'},
@@ -264,8 +267,9 @@ my %communigate_fields = (
<& /elements/progress-init.html,
$svcdb, #form name
[ # form fields to send
- qw(svc svcpart classnum selfservice_access disabled preserve exportnum),
- @fields
+ 'ALL'
+# qw(svc svcpart classnum selfservice_access disabled preserve exportnum),
+# @fields
],
'process/part_svc.cgi', # target
$p.'browse/part_svc.cgi', # redirect landing
diff --git a/httemplate/edit/log_email.html b/httemplate/edit/log_email.html
index 709a24069..b79aba986 100644
--- a/httemplate/edit/log_email.html
+++ b/httemplate/edit/log_email.html
@@ -8,10 +8,16 @@
'labels' => { '' => '(all)', map { $_ => $_ } @contexts },
'curr_value' => scalar($cgi->param('context')),
},
+ { 'field' => 'context_height',
+ 'type' => 'checkbox',
+ 'postfix' => 'Only match most specific context',
+ 'value' => 1,
+ 'curr_value' => scalar($cgi->param('context_height')),
+ },
{ 'field' => 'min_level',
'type' => 'select',
- 'options' => [ 0..7 ],
- 'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+ 'options' => [ &FS::Log::levelnums ],
+ 'labels' => { &FS::Log::levelmap },
'curr_value' => scalar($cgi->param('min_level')),
},
'to_addr',
@@ -24,6 +30,7 @@
],
'labels' => {
'context' => 'Context',
+ 'context_height' => '',
'min_level' => 'Min. Level',
'to_addr' => 'To',
'msgnum' => 'Message',
@@ -44,6 +51,10 @@ die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]);
my $msgnum = $cgi->param('msgnum');
+
+# XXX This attempt to set a default message isn't working, not sure why
+# $msgnum gets set correctly, but isn't selected in the popup window...fix later
+
unless ($msgnum) {
my ($msg_template) = qsearch('msg_template',{ msgname => 'System log' });
# doesn't seem worth having a config just for the default selected template
diff --git a/httemplate/edit/msgcat.html b/httemplate/edit/msgcat.html
index 4e2edd6d8..5ca118ad7 100644
--- a/httemplate/edit/msgcat.html
+++ b/httemplate/edit/msgcat.html
@@ -3,7 +3,7 @@
table => 'msgcat',
fields => [ { field=>'msgcode', type=>'fixed' },
{ field=>'locale', type=>'fixed' },
- 'msg',
+ { field => 'msg', type => 'text', size => 60 },
],
labels => { 'msgnum' => 'String',
'msgcode' => 'Code',
diff --git a/httemplate/edit/part_event.html b/httemplate/edit/part_event.html
index 47b8c1ac8..c8072e9f9 100644
--- a/httemplate/edit/part_event.html
+++ b/httemplate/edit/part_event.html
@@ -31,7 +31,7 @@
value => 'Event Conditions',
},
{ field => 'conditionname',
- type => 'selectlayers',
+ type => 'selectlayersx',
options => [ keys %all_conditions ],
labels => \%condition_labels,
onchange => 'condition_changed(what);',
@@ -51,7 +51,7 @@
value => 'Event Action',
},
{ field => 'action',
- type => 'selectlayers',
+ type => 'selectlayersx',
options => [ keys %all_actions ],
labels => \%action_labels,
onchange => 'action_changed(what);',
diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi
index 80a61f813..7fe659f94 100755
--- a/httemplate/edit/part_pkg.cgi
+++ b/httemplate/edit/part_pkg.cgi
@@ -92,7 +92,7 @@
{ type => 'columnstart' },
{ field => 'pkg',
- type => 'text',
+ type => 'input-locale-text',
size => 40, #32
maxlength => 50,
},
@@ -495,42 +495,6 @@ my $recur_show_zero_disabled = 1;
my $pkgpart = '';
-my $splice_locale_fields = sub {
- my( $fields, $pkey_value_callback, $pkg_value_callback ) = @_;
-
- my $n = 0;
- my @locale_fields = (
- map {
- my $pkey_value= $pkey_value_callback ? &$pkey_value_callback($_) : '';
- my $pkg_value = $pkg_value_callback
- ? $pkg_value_callback eq 'cgiparam'
- ? $cgi->param('pkgpartmsgnum'. $n. '_pkg')
- : &$pkg_value_callback($_)
- : '';
- (
- { field => 'pkgpartmsgnum'. $n,
- type => 'hidden',
- value => $pkey_value,
- },
- { field => 'pkgpartmsgnum'. $n. '_locale',
- type => 'hidden',
- value => $_,
- },
- { field => 'pkgpartmsgnum'. $n++. '_pkg',
- type => 'text',
- size => 40,
- #maxlength => 50,
- value => $pkg_value,
- },
- );
-
- }
- @locales
- );
- splice(@$fields, 7, 0, @locale_fields); #XXX 7 is arbitrary above
-
-};
-
my $error_callback = sub {
my($cgi, $object, $fields, $opt ) = @_;
@@ -579,16 +543,6 @@ my $error_callback = sub {
$pkgpart = $object->pkgpart;
- &$splice_locale_fields(
- $fields,
- sub {
- my $locale = shift;
- my $part_pkg_msgcat = $object->part_pkg_msgcat($locale);
- $part_pkg_msgcat ? $part_pkg_msgcat->pkgpartmsgnum : '';
- },
- 'cgiparam'
- );
-
if ( $cgi->param('error') =~ / is suggested with / ) {
#yeah, detection is a shitty kludge, but we don't have exception objects
$opt->{form_init} = '<INPUT TYPE="checkbox" NAME="part_pkg_restrict_soft_override" VALUE="Y"> Override suggestion<BR><BR>';
@@ -665,20 +619,6 @@ my $edit_callback = sub {
$pkgpart = $object->pkgpart;
- &$splice_locale_fields(
- $fields,
- sub {
- my $locale = shift;
- my $part_pkg_msgcat = $object->part_pkg_msgcat($locale);
- $part_pkg_msgcat ? $part_pkg_msgcat->pkgpartmsgnum : '';
- },
- sub {
- my $locale = shift;
- my $part_pkg_msgcat = $object->part_pkg_msgcat($locale);
- $part_pkg_msgcat ? $part_pkg_msgcat->pkg : '';
- }
- );
-
};
my $new_callback = sub {
@@ -692,8 +632,6 @@ my $new_callback = sub {
$options{'suspend_bill'}=1 if $conf->exists('part_pkg-default_suspend_bill');
- &$splice_locale_fields($fields, '', '');
-
};
my $clone_callback = sub {
@@ -732,17 +670,6 @@ my $clone_callback = sub {
foreach keys %part_pkg_currency;
}
- $recur_disabled = $object->freq ? 0 : 1;
-
- &$splice_locale_fields(
- $fields,
- '',
- sub {
- my $locale = shift;
- my $part_pkg_msgcat = $object->part_pkg_msgcat($locale);
- $part_pkg_msgcat ? $part_pkg_msgcat->pkg : '';
- }
- );
};
my $discount_error_callback = sub {
@@ -1061,6 +988,16 @@ my $html_bottom = sub {
: ''
). '>';
+ } elsif ( $href->{$field}{'type'} =~ /^select-rt-/ ) {
+
+ $html .= include('/elements/'.$href->{$field}{'type'}.'.html',
+ 'name' => $layer.'__'.$field,
+ 'curr_value' => $options{$field},
+ map { $_ => $href->{$field}{$_} }
+ grep { $_ !~ /^(name|type|parse)$/ }
+ keys %{ $href->{$field} }
+ );
+
} elsif ( $href->{$field}{'type'} eq 'select-rate' ) {
$html .= include('/elements/select-rate.html',
diff --git a/httemplate/edit/part_referral.html b/httemplate/edit/part_referral.html
index e9fd79452..04287d632 100755
--- a/httemplate/edit/part_referral.html
+++ b/httemplate/edit/part_referral.html
@@ -3,11 +3,13 @@
'table' => 'part_referral',
'fields' => [ 'referral',
{ field=>'agentnum', type=>'select-agent', },
+ 'title',
{ field=>'disabled', type=>'checkbox', value=>'Y' } ,
],
'labels' => { 'refnum' => 'Ad Source',
'referral' => 'Advertising source',
'agentnum' => 'Agent',
+ 'title' => 'External ID',
'disabled' => 'Disabled',
},
'viewall_dir' => 'browse',
diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi
index a07fc6005..fed21256f 100755
--- a/httemplate/edit/part_svc.cgi
+++ b/httemplate/edit/part_svc.cgi
@@ -36,9 +36,26 @@
}
</STYLE>
<SCRIPT TYPE="text/javascript">
+// copy all fields from the outer form (svc and its localizations, plus
+// preserve, selfservice_access, etc.) into the inner form, creating hidden
+// inputs if needed
function fixup_submit(layer) {
- document.forms[layer].submit.disabled = true;
- fixup(document.forms[layer]);
+ var layer_form = $(document.forms[layer]);
+ var main_form = $(document.forms['SvcEditMain']);
+ var data = main_form.serializeArray();
+ for (var i = 0; i < data.length; i++) {
+ var input = layer_form.children('[name=' + data[i].name + ']');
+ if (input[0]) {
+ input.prop('value', data[i].value);
+ } else {
+ $( '<input type="hidden">' )
+ .attr('name', data[i].name)
+ .prop('value', data[i].value)
+ .appendTo(layer_form);
+ }
+ }
+ layer_form[0]['submit'].disabled = true;
+ //fixup(document.forms[layer]);
window[layer+'process'].call();
}
@@ -141,19 +158,26 @@ window.onload = function() {
</SCRIPT>
-<FORM NAME="dummy">
+<FORM NAME="SvcEditMain">
<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-input-locale-text.html,
+ 'object' => $part_svc,
+ 'cgi' => $cgi,
+ 'field' => 'svc',
+ 'label' => 'Service',
+ 'curr_value' => $hashref->{svc},
+&>
+%#<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} &>
<TR>
- <TD ALIGN="right">Self-service access</TD>
+ <TH ALIGN="right">Self-service access</TD>
<TD>
<SELECT NAME="selfservice_access">
% tie my %selfservice_access, 'Tie::IxHash', #false laziness w/browse/part_svc
@@ -172,12 +196,12 @@ window.onload = function() {
<TR>
- <TD ALIGN="right">Disable new orders</TD>
+ <TH 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>
+ <TH 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>
@@ -240,12 +264,12 @@ my $widget = new HTML::Widgets::SelectLayers(
#'selected_layer' => $p_svcdb,
'selected_layer' => $hashref->{svcdb} || 'svc_acct',
'options' => \%svcdb,
- 'form_name' => 'dummy',
+ 'form_name' => 'SvcEditMain',
#'form_action' => 'process/part_svc.cgi',
'form_action' => 'part_svc.cgi', #self
- 'form_elements' => [qw( svc svcpart classnum selfservice_access
- disabled preserve
- )],
+# 'form_elements' => [qw( svc svcpart classnum selfservice_access
+# disabled preserve
+# )],
'html_between' => $help,
'layer_callback' => sub {
include('elements/part_svc_column.html',
diff --git a/httemplate/edit/payment_gateway.html b/httemplate/edit/payment_gateway.html
index a85ba57af..b44b31513 100644
--- a/httemplate/edit/payment_gateway.html
+++ b/httemplate/edit/payment_gateway.html
@@ -47,28 +47,36 @@ my %modules = (
'AuthorizeNet',
'BankOfAmerica', #deprecated?
'Beanstream',
+ 'Braintree',
'Capstone',
+ 'CardFortress',
'Cardstream',
'CashCow',
'CyberSource',
'eSec',
'eSelectPlus',
'ElavonVirtualMerchant',
+ 'eWay',
'Exact',
'FirstDataGlobalGateway',
+ 'GlobalPayments',
'iAuthorizer',
'Ingotz',
'InternetSecure',
- 'IPaymentTPG',
+ #'IPaymentTPG',
'IPPay',
'Iridium',
'Jettis',
'Jety',
'LinkPoint',
+ 'Litle',
'MerchantCommerce',
'Network1Financial',
+ 'NMI',
'OCV',
+ 'Ogone',
'OpenECHO',
+ 'PaperlessTrans',
'PayConnect',
'PayflowPro',
'PaymenTech',
@@ -84,26 +92,31 @@ my %modules = (
'Skipjack',
'StGeorge',
'SurePay',
+ 'SynapseGateway',
'TCLink',
'TransactionCentral',
'TransFirsteLink',
+ 'USAePay',
'Vanco',
'viaKLIX',
'VirtualNet',
+ 'vSecureProcessing',
'WesternACH',
'WorldPay',
],
'Business::OnlineThirdPartyPayment' => [
#'eWayShared', support currently broken
- #'Interswitchng',
- 'PayPal',
'FCMB',
+ #'Interswitchng', #incomplete?
+ 'PayPal',
],
'Business::BatchPayment' => [
+ 'BillBuddy',
+ 'CardFortress',
'KeyBank',
'Paymentech',
+ 'RBC',
'TD_EFT',
- 'BillBuddy',
],
);
@@ -132,7 +145,7 @@ my $fields = [ {
field => 'gateway_module',
type => 'select',
# does it even make sense to list all modules here?
- options => [ sort { lc($a) cmp lc ($b) }
+ options => [ sort { lc($a) cmp lc($b) }
map { @$_ } values %modules ],
},
'gateway_username',
diff --git a/httemplate/edit/process/bulk-cust_main_county.html b/httemplate/edit/process/bulk-cust_main_county.html
index b7ff40fa7..b5a0258b1 100644
--- a/httemplate/edit/process/bulk-cust_main_county.html
+++ b/httemplate/edit/process/bulk-cust_main_county.html
@@ -12,7 +12,7 @@
<% include('/elements/header-popup.html', "Taxes ${action}ed") %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
diff --git a/httemplate/edit/process/bulk-cust_svc-pkgnum.html b/httemplate/edit/process/bulk-cust_svc-pkgnum.html
index f5cf7dd07..3c273069a 100644
--- a/httemplate/edit/process/bulk-cust_svc-pkgnum.html
+++ b/httemplate/edit/process/bulk-cust_svc-pkgnum.html
@@ -7,7 +7,7 @@
<% header(emt("Services moved")) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/cgp_rule-simplified.html b/httemplate/edit/process/cgp_rule-simplified.html
index 60769d4e6..24515d551 100644
--- a/httemplate/edit/process/cgp_rule-simplified.html
+++ b/httemplate/edit/process/cgp_rule-simplified.html
@@ -4,7 +4,7 @@
% } else { #success XXX better msg talking about vacation vs. redirect all
<% include('/elements/header-popup.html', 'Rule updated') %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html
index 308ea8ffd..54cafbf18 100644
--- a/httemplate/edit/process/change-cust_pkg.html
+++ b/httemplate/edit/process/change-cust_pkg.html
@@ -5,7 +5,7 @@
<% header(emt("Package changed")) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/commission_schedule.html b/httemplate/edit/process/commission_schedule.html
new file mode 100644
index 000000000..50e0371da
--- /dev/null
+++ b/httemplate/edit/process/commission_schedule.html
@@ -0,0 +1,36 @@
+<& elements/process.html,
+ 'table' => 'commission_schedule',
+ 'viewall_dir' => 'browse',
+ 'process_o2m' => {
+ 'table' => 'commission_rate',
+ 'fields' => [qw( cycle amount percent )],
+ },
+ 'precheck_callback' => $precheck,
+ 'debug' => 1,
+&>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $precheck = sub {
+ my $cgi = shift;
+ $cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum";
+
+ my ($reasonnum, $error) = $m->comp('/misc/process/elements/reason');
+ if (!$reasonnum) {
+ $error ||= 'Reason required'
+ }
+ $cgi->param('reasonnum', $reasonnum) unless $error;
+
+ # remove rate entries with no cycle selected
+ foreach my $k (grep /^commissionratenum\d+$/, $cgi->param) {
+ if (! $cgi->param($k.'_cycle') ) {
+ $cgi->delete($k);
+ }
+ }
+
+ $error;
+};
+
+</%init>
diff --git a/httemplate/edit/process/credit-cust_bill_pkg.html b/httemplate/edit/process/credit-cust_bill_pkg.html
index 75900bde5..12b68c0f5 100644
--- a/httemplate/edit/process/credit-cust_bill_pkg.html
+++ b/httemplate/edit/process/credit-cust_bill_pkg.html
@@ -3,7 +3,7 @@
%} else {
<& /elements/header-popup.html, 'Credit successful' &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
diff --git a/httemplate/edit/process/cust_credit-pkgnum.html b/httemplate/edit/process/cust_credit-pkgnum.html
index 8941cbc73..56f7989a4 100755
--- a/httemplate/edit/process/cust_credit-pkgnum.html
+++ b/httemplate/edit/process/cust_credit-pkgnum.html
@@ -4,7 +4,7 @@
%} else {
<% header(emt('Credit package changed')) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
diff --git a/httemplate/edit/process/cust_credit.cgi b/httemplate/edit/process/cust_credit.cgi
index 39c6f1997..5d3028777 100755
--- a/httemplate/edit/process/cust_credit.cgi
+++ b/httemplate/edit/process/cust_credit.cgi
@@ -16,7 +16,7 @@
%
<% header(emt('Credit successful')) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
diff --git a/httemplate/edit/process/cust_location-censustract.html b/httemplate/edit/process/cust_location-censustract.html
index bc9cd4f31..6edaca3fd 100644
--- a/httemplate/edit/process/cust_location-censustract.html
+++ b/httemplate/edit/process/cust_location-censustract.html
@@ -5,7 +5,7 @@
<% header("Census tract changed") %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/cust_location.cgi b/httemplate/edit/process/cust_location.cgi
index fd1b8740e..3a2388111 100644
--- a/httemplate/edit/process/cust_location.cgi
+++ b/httemplate/edit/process/cust_location.cgi
@@ -5,7 +5,7 @@
<% header("Location changed") %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi
index d590fdef0..74f8f2382 100755
--- a/httemplate/edit/process/cust_main.cgi
+++ b/httemplate/edit/process/cust_main.cgi
@@ -1,5 +1,15 @@
% if ( $error ) {
% $cgi->param('error', $error);
+% # workaround for create_uri_query's mangling of unicode characters,
+% # false laziness with FS::Record::ut_coord
+% use charnames ':full';
+% for my $pre (qw(bill ship)) {
+% foreach (qw( latitude longitude)) {
+% my $coord = $cgi->param($pre.'_'.$_);
+% $coord =~ s/\N{DEGREE SIGN}\s*$//;
+% $cgi->param($pre.'_'.$_, $coord);
+% }
+% }
% my $query = $m->scomp('/elements/create_uri_query', 'secure'=>1);
<% $cgi->redirect(popurl(2). "cust_main.cgi?$query" ) %>
%
@@ -76,8 +86,8 @@ if ( ($cgi->param('same') || '') eq 'Y' ) {
# but explicitly avoid setting ship_ fields
my $new = new FS::cust_main ( {
- map { ( $_, scalar($cgi->param($_)) ) } (fields('cust_main')),
- map { ( "ship_$_", '' ) } (FS::cust_main->location_fields)
+ (map { ( $_, scalar($cgi->param($_)) ) } (fields('cust_main'))),
+ (map { ( "ship_$_", '' ) } (FS::cust_main->location_fields))
} );
warn Dumper( $new ) if $DEBUG > 1;
@@ -188,6 +198,15 @@ if ( $cgi->param('residential_commercial') eq 'Residential' ) {
}
+# kind of a hack, but some tax data vendors require a status and others
+# don't.
+my $vendor = $conf->config('tax_data_vendor');
+if ( $vendor eq 'avalara' or $vendor eq 'suretax' ) {
+ if ( ! $cgi->param('taxstatusnum') ) {
+ $error ||= 'Tax status required';
+ }
+}
+
#perhaps this stuff should go to cust_main.pm
if ( $new->custnum eq '' or $duplicate_of ) {
diff --git a/httemplate/edit/process/cust_main_attach.cgi b/httemplate/edit/process/cust_main_attach.cgi
index 09c18adcb..569500246 100644
--- a/httemplate/edit/process/cust_main_attach.cgi
+++ b/httemplate/edit/process/cust_main_attach.cgi
@@ -9,7 +9,7 @@
% $act = 'deleted' if($attachnum and $delete);
<% header('Attachment ' . $act ) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
diff --git a/httemplate/edit/process/cust_main_county-add.cgi b/httemplate/edit/process/cust_main_county-add.cgi
index fc8956b0c..fcc138f49 100755
--- a/httemplate/edit/process/cust_main_county-add.cgi
+++ b/httemplate/edit/process/cust_main_county-add.cgi
@@ -1,7 +1,7 @@
<% include('/elements/header-popup.html', 'Addition successful' ) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
diff --git a/httemplate/edit/process/cust_main_county-expand.cgi b/httemplate/edit/process/cust_main_county-expand.cgi
index a10827621..42e46734a 100755
--- a/httemplate/edit/process/cust_main_county-expand.cgi
+++ b/httemplate/edit/process/cust_main_county-expand.cgi
@@ -1,7 +1,7 @@
<% include('/elements/header-popup.html', 'Addition successful' ) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
diff --git a/httemplate/edit/process/cust_main_note.cgi b/httemplate/edit/process/cust_main_note.cgi
index 53e616a43..bb52db8f3 100755
--- a/httemplate/edit/process/cust_main_note.cgi
+++ b/httemplate/edit/process/cust_main_note.cgi
@@ -4,7 +4,7 @@
%} else {
<% header('Note ' . ($notenum ? 'updated' : 'added') ) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
diff --git a/httemplate/edit/process/cust_pay-no_auto_apply.cgi b/httemplate/edit/process/cust_pay-no_auto_apply.cgi
index ccbd2d7b5..4a5ee841a 100644
--- a/httemplate/edit/process/cust_pay-no_auto_apply.cgi
+++ b/httemplate/edit/process/cust_pay-no_auto_apply.cgi
@@ -15,7 +15,7 @@ Requires 'Apply payment' acl.
<P STYLE="font-weight: bold;"><% emt($message) %></P>
<P><% emt('Please wait while the page reloads.') %></P>
<SCRIPT TYPE="text/javascript">
-window.top.location.reload();
+topreload();
</SCRIPT>
% }
diff --git a/httemplate/edit/process/cust_pay-pkgnum.html b/httemplate/edit/process/cust_pay-pkgnum.html
index d9a92a1de..cefe970fe 100755
--- a/httemplate/edit/process/cust_pay-pkgnum.html
+++ b/httemplate/edit/process/cust_pay-pkgnum.html
@@ -4,7 +4,7 @@
%} else {
<% header(emt('Payment package changed')) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi
index 56d3f2ff1..15b26f9c6 100755
--- a/httemplate/edit/process/cust_pay.cgi
+++ b/httemplate/edit/process/cust_pay.cgi
@@ -14,7 +14,7 @@
%
<% header(emt('Payment entered')) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
@@ -69,6 +69,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/cust_pay_pending.html b/httemplate/edit/process/cust_pay_pending.html
index 1bad6cffe..80bd14aaf 100644
--- a/httemplate/edit/process/cust_pay_pending.html
+++ b/httemplate/edit/process/cust_pay_pending.html
@@ -3,7 +3,7 @@
<FONT SIZE="+1" COLOR="#ff0000">Error: <% $error |h %></FONT>
% } else {
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
% }
</BODY>
@@ -59,6 +59,15 @@ if ( $action eq 'delete' ) {
$title = 'Pending payment completed (decline)';
}
+} elsif ( $action eq 'reverse' ) {
+
+ $error = $cust_pay_pending->reverse;
+ if ( $error ) {
+ $title = 'Error reversing pending payment';
+ } else {
+ $title = 'Pending payment completed (reverse)';
+ }
+
} else {
die "unknown action $action";
diff --git a/httemplate/edit/process/cust_pkg_detail.html b/httemplate/edit/process/cust_pkg_detail.html
index 132ff63c5..25fabd930 100644
--- a/httemplate/edit/process/cust_pkg_detail.html
+++ b/httemplate/edit/process/cust_pkg_detail.html
@@ -6,7 +6,7 @@
% } else {
<% header($action) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
diff --git a/httemplate/edit/process/cust_pkg_discount.html b/httemplate/edit/process/cust_pkg_discount.html
index 143611ef9..963546363 100644
--- a/httemplate/edit/process/cust_pkg_discount.html
+++ b/httemplate/edit/process/cust_pkg_discount.html
@@ -5,7 +5,7 @@
<% header("Discount applied") %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/cust_pkg_quantity.html b/httemplate/edit/process/cust_pkg_quantity.html
index fb2657252..b60595583 100644
--- a/httemplate/edit/process/cust_pkg_quantity.html
+++ b/httemplate/edit/process/cust_pkg_quantity.html
@@ -5,7 +5,7 @@
<& /elements/header-popup.html, "Quantity changed" &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/cust_pkg_salesnum.html b/httemplate/edit/process/cust_pkg_salesnum.html
index aab37416a..c1cb26813 100644
--- a/httemplate/edit/process/cust_pkg_salesnum.html
+++ b/httemplate/edit/process/cust_pkg_salesnum.html
@@ -5,7 +5,7 @@
<& /elements/header-popup.html, "Sales Person changed" &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi
index 8977ced20..d4236bcdf 100755
--- a/httemplate/edit/process/cust_refund.cgi
+++ b/httemplate/edit/process/cust_refund.cgi
@@ -7,7 +7,7 @@
%
<% header('Refund entered') %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
diff --git a/httemplate/edit/process/cust_tax_adjustment.html b/httemplate/edit/process/cust_tax_adjustment.html
index 204b5b9f7..fe232757f 100644
--- a/httemplate/edit/process/cust_tax_adjustment.html
+++ b/httemplate/edit/process/cust_tax_adjustment.html
@@ -4,7 +4,7 @@
% } else {
<% header("Tax adjustment added") %>
<SCRIPT TYPE="text/javascript">
- //window.top.location.reload();
+ //topreload();
parent.cClick();
</SCRIPT>
</BODY></HTML>
diff --git a/httemplate/edit/process/detach-cust_pkg.html b/httemplate/edit/process/detach-cust_pkg.html
index 782ffa5e0..34c580560 100644
--- a/httemplate/edit/process/detach-cust_pkg.html
+++ b/httemplate/edit/process/detach-cust_pkg.html
@@ -5,7 +5,7 @@
<% header(emt("Package detached")) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/domain_record.cgi b/httemplate/edit/process/domain_record.cgi
index 8369f7114..9d869d547 100755
--- a/httemplate/edit/process/domain_record.cgi
+++ b/httemplate/edit/process/domain_record.cgi
@@ -3,7 +3,7 @@
%} elsif ( $recnum ) { #editing
<% header('Nameservice record changed') %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
%} else { #adding
diff --git a/httemplate/edit/process/elements/ApplicationCommon.html b/httemplate/edit/process/elements/ApplicationCommon.html
index c7bdd3ea2..67fa89196 100644
--- a/httemplate/edit/process/elements/ApplicationCommon.html
+++ b/httemplate/edit/process/elements/ApplicationCommon.html
@@ -21,11 +21,12 @@ Examples:
</%doc>
%if ( $error ) {
% $cgi->param('error', $error);
-<% $cgi->redirect(popurl(2). $opt{error_redirect}. '?'. $cgi->query_string ) %>
+% my $query = $m->scomp('/elements/create_uri_query');
+<% $cgi->redirect(popurl(2). $opt{error_redirect}. "?$query") %>
%} else {
<% header("$src_thing application$to sucessful") %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html
index fd12c61d9..76722c960 100644
--- a/httemplate/edit/process/elements/process.html
+++ b/httemplate/edit/process/elements/process.html
@@ -62,6 +62,8 @@ Example:
'fields' => [qw( fieldname fieldname2 )],
},
+ 'process_locale' => 'fieldname', # update entries in the _msgcat table
+
'process_upload' => {
'process' => 'misc/mytable-import.html',
# fields to pass to the back end job, besides the
@@ -186,7 +188,7 @@ process();
<% include('/elements/header-popup.html', $opt{'popup_reload'} ) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
@@ -363,12 +365,21 @@ foreach my $value ( @values ) {
}
- if ( !$error && $opt{'process_o2m'} ) {
-
- my @process_o2m = ref($opt{'process_o2m'}) eq 'ARRAY'
- ? @{ $opt{'process_o2m'} }
- : ( $opt{'process_o2m'} );
+ my @process_o2m;
+ if ( $opt{'process_o2m'} ) {
+ @process_o2m = ref($opt{'process_o2m'}) eq 'ARRAY'
+ ? @{ $opt{'process_o2m'} }
+ : ( $opt{'process_o2m'} );
+ }
+ if ( $opt{'process_locale'} ) {
+ push @process_o2m,
+ {
+ 'table' => $table . '_msgcat',
+ 'fields' => [ 'locale', $opt{'process_locale'} ],
+ };
+ }
+ if ( !$error ) {
foreach my $process_o2m (@process_o2m) {
diff --git a/httemplate/edit/process/part_event.html b/httemplate/edit/process/part_event.html
index bac69241c..0293af886 100644
--- a/httemplate/edit/process/part_event.html
+++ b/httemplate/edit/process/part_event.html
@@ -39,8 +39,8 @@
split(/\0/, $value)
};
} elsif ( $info->{'type'} eq 'freq' ) {
- $value = '0' if !length($value);
- $value .= $params->{$cgi_field.'_units'};
+ $value = '0' if !length($value) and !$info->{'allow_blank'};
+ $value .= $params->{$cgi_field.'_units'} if length($value);
}
#warn "value of $cgi_field is $value\n";
diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi
index b8042026a..c4d150ba1 100755
--- a/httemplate/edit/process/part_pkg.cgi
+++ b/httemplate/edit/process/part_pkg.cgi
@@ -9,6 +9,7 @@
'edit_ext' => 'cgi',
'precheck_callback' => $precheck_callback,
'args_callback' => $args_callback,
+ 'process_locale' => 'pkg',
'process_m2m' => \@process_m2m,
'process_o2m' => \@process_o2m,
)
@@ -310,10 +311,6 @@ foreach my $amount_param ( grep /^usagepricepart(\d+)_amount$/, $cgi->param ) {
my @process_o2m = (
{
- 'table' => 'part_pkg_msgcat',
- 'fields' => [qw( locale pkg )],
- },
- {
'table' => 'part_pkg_usageprice',
'fields' => [qw( price currency action target amount )],
diff --git a/httemplate/edit/process/prospect_main.html b/httemplate/edit/process/prospect_main.html
index 7c8cc276e..b2ae88eba 100644
--- a/httemplate/edit/process/prospect_main.html
+++ b/httemplate/edit/process/prospect_main.html
@@ -1,5 +1,6 @@
<% include('elements/process.html',
'table' => 'prospect_main',
+ 'precheck_callback' => $precheck,
'args_callback' => $args_callback,
'agent_virt' => 1,
'process_o2m' => {
@@ -11,6 +12,16 @@
%>
<%init>
+my $precheck = sub {
+ my $cgi = shift;
+ my $vendor = FS::Conf->new->config('tax_data_vendor');
+ if ( $vendor eq 'avalara' or $vendor eq 'suretax' ) {
+ if ( ! $cgi->param('taxstatusnum') ) {
+ return 'Tax status required';
+ }
+ }
+};
+
my $args_callback = sub {
my( $cgi, $object ) = @_;
diff --git a/httemplate/edit/process/quick-charge.cgi b/httemplate/edit/process/quick-charge.cgi
index 8ee182141..00d17c8b1 100644
--- a/httemplate/edit/process/quick-charge.cgi
+++ b/httemplate/edit/process/quick-charge.cgi
@@ -1,10 +1,10 @@
% if ( $error ) {
% $cgi->param('error', $error );
-<% $cgi->redirect($p.'quick-charge.html?'. $cgi->query_string) %>
+<% $cgi->redirect($redirect) %>
% } else {
<% header(emt($message)) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
@@ -157,4 +157,14 @@ if ( $param->{'pkgnum'} =~ /^(\d+)$/ ) { #modifying an existing one-time charge
}
+my $redirect;
+if ( $error ) {
+ $cgi->param('error', $error );
+ $redirect = $p.'quick-charge.html?'. $cgi->query_string;
+} elsif ( $quotation ) {
+ $redirect = $fsurl.'view/quotation.html?' . $quotation->quotationnum;
+} else {
+ $redirect = $fsurl.'view/cust_main.cgi?custnum=' . $cust_main->custnum . ';show=last';
+}
+
</%init>
diff --git a/httemplate/edit/process/quotation_convert.html b/httemplate/edit/process/quotation_convert.html
index dc00a88d3..26b5294f7 100644
--- a/httemplate/edit/process/quotation_convert.html
+++ b/httemplate/edit/process/quotation_convert.html
@@ -10,9 +10,12 @@ my $quotation = qsearchs( 'quotation' => {
quotationnum => scalar( $cgi->param('quotationnum') ),
} ) or die 'unknown quotationnum';
+my $params = {};
+$$params{'onhold'} = $cgi->param('onhold') ? 1 : 0;
+
my $cust_main = $quotation->cust_main;
if ( $cust_main ) {
- my $error = $quotation->order;
+ my $error = $quotation->order(undef,$params);
errorpage($error) if $error;
#i should be part of the order transaction
@@ -20,7 +23,7 @@ if ( $cust_main ) {
$quotation->replace;
} else {
- $cust_main = $quotation->convert_cust_main;
+ $cust_main = $quotation->convert_cust_main( $params );
errorpage($cust_main) unless ref($cust_main);# eq 'FS::cust_main';
}
diff --git a/httemplate/edit/process/quotation_pkg_detail.html b/httemplate/edit/process/quotation_pkg_detail.html
index 9e4ac3222..5728832b2 100644
--- a/httemplate/edit/process/quotation_pkg_detail.html
+++ b/httemplate/edit/process/quotation_pkg_detail.html
@@ -6,7 +6,7 @@
% } else {
<% header($action) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
@@ -26,8 +26,16 @@ my $quotation_pkg = qsearchs({
'LEFT JOIN cust_main USING ( custnum )',
'hashref' => { 'quotationpkgnum' => $pkgnum },
'extra_sql' => ' AND '. $curuser->agentnums_sql,
+})
+|| qsearchs({
+ 'table' => 'quotation_pkg',
+ 'addl_from' => 'LEFT JOIN quotation USING ( quotationnum )'.
+ 'LEFT JOIN prospect_main USING ( prospectnum )',
+ 'hashref' => { 'quotationpkgnum' => $pkgnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
});
+
my @orig_details = $quotation_pkg->details();
my $action = 'Quotation details'.
diff --git a/httemplate/edit/process/svc_dsl.html b/httemplate/edit/process/svc_dsl.html
index 436ca766b..489c86916 100644
--- a/httemplate/edit/process/svc_dsl.html
+++ b/httemplate/edit/process/svc_dsl.html
@@ -25,7 +25,7 @@ my $precheck_callback = sub {
my $psc = $part_svc->part_svc_column('password');
if ( $psc->columnflag eq 'F' ) {
# enforce it here and skip password validation
- $cgi->param('password', $psc->columnvalue;
+ $cgi->param('password', $psc->columnvalue);
} else {
my $newpass = $cgi->param('password');
if ( $old and $newpass ne $old->password ) {
diff --git a/httemplate/edit/process/tower.html b/httemplate/edit/process/tower.html
index 02362db6a..d14ac56f8 100644
--- a/httemplate/edit/process/tower.html
+++ b/httemplate/edit/process/tower.html
@@ -4,6 +4,7 @@
process_o2m => { 'table' => 'tower_sector',
'fields' => [qw(
sectorname ip_addr height freq_mhz direction width
+ downtilt v_width margin
sector_range
)],
},
diff --git a/httemplate/edit/prospect_main.html b/httemplate/edit/prospect_main.html
index fb6751532..6aefe80d0 100644
--- a/httemplate/edit/prospect_main.html
+++ b/httemplate/edit/prospect_main.html
@@ -34,8 +34,10 @@
{ 'field' => 'contactnum',
'type' => 'contact',
'colspan' => 7,
- 'o2m_table' => 'contact',
- 'm2_label' => 'Contact',
+ 'prospectnum' => $prospectnum,
+ 'm2m_method' => 'prospect_contact',
+ 'm2m_dstcol' => 'contactnum',
+ 'm2_label' => 'Contact',
'm2_error_callback' => $m2_error_callback,
},
@@ -50,6 +52,7 @@
},
{ 'field' => 'taxstatusnum',
'type' => 'select-tax_status',
+ 'required' => 1,
'empty_label' => ' ',
},
],
@@ -68,18 +71,25 @@ my $conf = new FS::Conf;
my $prospectnum;
if ( $cgi->param('error') ) {
- $prospectnum = scalar($cgi->param('prospectnum'));
+ $cgi->param('prospectnum') =~ /^(\d*)$/ or die 'illegal prospectnum';
+ $prospectnum = $1;
die "access denied"
unless $curuser->access_right(($prospectnum ? 'Edit' : 'New'). ' prospect');
} elsif ( $cgi->keywords ) { #editing
+ my($query) = $cgi->keywords;
+ $query =~ /^(\d+)$/ or die 'no prospectnum';
+ $prospectnum = $1;
+
die "access denied"
unless $curuser->access_right('Edit prospect');
} else { #new prospect
+ $prospectnum = '';
+
die "access denied"
unless $curuser->access_right('New prospect');
diff --git a/httemplate/edit/quick-charge.html b/httemplate/edit/quick-charge.html
index 14c3f51e6..2bbbe43d8 100644
--- a/httemplate/edit/quick-charge.html
+++ b/httemplate/edit/quick-charge.html
@@ -1,4 +1,4 @@
-<& /elements/header-popup.html, mt('One-time charge'), '',
+<& /elements/header.html, mt('One-time charge'), '',
( ($quotationnum || $cgi->param('error')) ? '' : 'onload="addRow()"' ),
&>
@@ -93,6 +93,18 @@ function bill_now_changed (what) {
</SCRIPT>
+<P>
+% if ( $cust_main ) {
+<& /elements/small_custview.html,
+ $cust_main->custnum,
+ scalar($conf->config('countrydefault')),
+ 1, #no balance
+&>
+% } else {
+<& /elements/small_prospect_view.html, $prospect_main &>
+% }
+</P>
+
<FORM ACTION = "process/quick-charge.cgi"
NAME = "QuickChargeForm"
ID = "QuickChargeForm"
@@ -183,7 +195,10 @@ function bill_now_changed (what) {
<& /elements/tr-select-taxclass.html, 'curr_value' => $part_pkg->get('taxclass') &>
- <& /elements/tr-select-taxproduct.html, 'label' => emt('Tax product'), 'onclick' => 'parent.taxproductmagic(this);', 'curr_value' => $part_pkg->get('taxproductnum') &>
+ <& /elements/tr-select-taxproduct.html,
+ 'label' => emt('Tax product'),
+ 'curr_value' => $part_pkg->get('taxproductnum')
+ &>
% }
% } else { # new one-time charge
@@ -307,9 +322,14 @@ function bill_now_changed (what) {
<& /elements/tr-select-taxclass.html, 'curr_value' => $cgi->param('taxclass') &>
-<& /elements/tr-select-taxproduct.html, 'label' => emt('Tax product'), 'onclick' => 'parent.taxproductmagic(this);', 'curr_value' => $cgi->param('taxproductnum') &>
+<& /elements/tr-select-taxproduct.html,
+ 'label' => emt('Tax product'),
+ 'curr_value' => $cgi->param('taxproductnum')
+&>
-<& /elements/tr-select-taxoverride.html, 'onclick' => 'parent.taxoverridemagic(this);', 'curr_value' => $cgi->param('tax_override') &>
+<& /elements/tr-select-taxoverride.html,
+ 'curr_value' => $cgi->param('tax_override')
+&>
% } # if !$cust_pkg
diff --git a/httemplate/edit/quotation_pkg_detail.html b/httemplate/edit/quotation_pkg_detail.html
index 036bffdde..9aa50ec38 100644
--- a/httemplate/edit/quotation_pkg_detail.html
+++ b/httemplate/edit/quotation_pkg_detail.html
@@ -61,6 +61,13 @@ my $quotation_pkg = qsearchs({
'LEFT JOIN cust_main USING ( custnum )',
'hashref' => { 'quotationpkgnum' => $pkgnum },
'extra_sql' => ' AND '. $curuser->agentnums_sql,
+})
+|| qsearchs({
+ 'table' => 'quotation_pkg',
+ 'addl_from' => 'LEFT JOIN quotation USING ( quotationnum )'.
+ 'LEFT JOIN prospect_main USING ( prospectnum )',
+ 'hashref' => { 'quotationpkgnum' => $pkgnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
});
my $part_pkg = $quotation_pkg->part_pkg;
diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi
index ca26c6cf0..7be5eabb7 100755
--- a/httemplate/edit/svc_acct.cgi
+++ b/httemplate/edit/svc_acct.cgi
@@ -31,7 +31,7 @@
</TR>
<% include('/elements/tr-td-label.html',
- 'label' => mt('Username'),
+ 'label' => $part_svc->part_svc_column('username')->columnlabel || mt('Username'),
'required' => $part_svc->part_svc_column('username')->required ) %>
% if ( $svcnum && $conf->exists('svc_acct-no_edit_username') ) {
<TD BGCOLOR="#eeeeee"><% $svc_acct->username() %></TD>
@@ -46,7 +46,7 @@
%if ( $part_svc->part_svc_column('_password')->columnflag ne 'F' ) {
% #XXX eventually should require "Edit Password" ACL
<% include('/elements/tr-td-label.html',
- 'label' => mt('Password'),
+ 'label' => $part_svc->part_svc_column('_password')->columnlabel || mt('Password'),
'required' => $part_svc->part_svc_column('_password')->required ) %>
<TD>
<INPUT TYPE="text" ID="clear_password" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>>
@@ -68,7 +68,7 @@
% && $part_svc->part_svc_column('sec_phrase')->columnflag ne 'F' ) {
<% include('/elements/tr-td-label.html',
- 'label' => mt('Security phrase'),
+ 'label' => $part_svc->part_svc_column('sec_phrase')->columnlabel || mt('Security phrase'),
'required' => $part_svc->part_svc_column('sec_phrase')->required ) %>
<TD>
<INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $sec_phrase %>" SIZE=32>
@@ -106,7 +106,7 @@
% );
<% include('/elements/tr-td-label.html',
- 'label' => mt('Domain'),
+ 'label' => $part_svc->part_svc_column('domsvc')->columnlabel || mt('Domain'),
'required' => $part_svc->part_svc_column('domsvc')->required ) %>
<TD>
<SELECT NAME="domsvc" SIZE=1>
@@ -142,6 +142,7 @@
'curr_value' => $svc_acct->pbxsvc,
'part_svc' => $part_svc,
'cust_pkg' => $cust_pkg,
+ 'label' => $part_svc->part_svc_column('pbxsvc')->columnlabel || 'PBX',
&>
%#pop
@@ -153,7 +154,7 @@
% } else {
<% include('/elements/tr-td-label.html',
- 'label' => mt('Access number'),
+ 'label' => $part_svc->part_svc_column('popnum')->columnlabel || mt('Access number'),
'required' => $part_svc->part_svc_column('popnum')->required ) %>
<TD><% FS::svc_acct_pop::popselector($popnum) %></TD>
</TR>
@@ -167,6 +168,7 @@
#'part_svc' => $part_svc,
#'cust_pkg' => $cust_pkg,
'required' => $part_svc->part_svc_column('sectornum')->required,
+ 'label' => $part_svc->part_svc_column('sectornum')->columnlabel || mt('Tower sector'),
&>
%} else {
<INPUT TYPE="hidden" NAME="sectornum" VALUE="<% $svc_acct->sectornum %>">
@@ -188,10 +190,10 @@
% if ( length($svc_acct->$xid()) ) {
<% include('/elements/tr-td-label.html',
- 'label' => uc($xid),
+ 'label' => $part_svc->part_svc_column($xid)->columnlabel || uc($xid),
'required' => $part_svc->part_svc_column($xid)->required ) %>
- <TR>
- <TD ALIGN="right"><% uc($xid) %></TD>
+%# <TR>
+%# <TD ALIGN="right"><% uc($xid) %></TD>
<TD BGCOLOR="#eeeeee"><% $svc_acct->$xid() %></TD>
<TD>
</TD>
@@ -202,7 +204,7 @@
% } else {
<% include('/elements/tr-td-label.html',
- 'label' => uc($xid),
+ 'label' => $part_svc->part_svc_column($xid)->columnlabel || uc($xid),
'required' => $part_svc->part_svc_column($xid)->required ) %>
<TD>
<INPUT TYPE="text" NAME="<% $xid %>" SIZE=8 MAXLENGTH=6 VALUE="<% $svc_acct->$xid() %>">
@@ -221,7 +223,7 @@
<% include('/elements/tr-td-label.html',
- 'label' => mt('Real Name'),
+ 'label' => $part_svc->part_svc_column('finger')->columnlabel || mt('Real Name'),
'required' => $part_svc->part_svc_column('finger')->required ) %>
<TD>
<INPUT TYPE="text" NAME="finger" VALUE="<% $svc_acct->finger %>">
@@ -240,7 +242,7 @@
<% include('/elements/tr-td-label.html',
- 'label' => mt('Home directory'),
+ 'label' => $part_svc->part_svc_column('dir')->columnlabel || mt('Home directory'),
'required' => $part_svc->part_svc_column('dir')->required ) %>
<TD><INPUT TYPE="text" NAME="dir" VALUE="<% $svc_acct->dir %>"></TD>
</TR>
@@ -258,7 +260,7 @@
<% include('/elements/tr-td-label.html',
- 'label' => mt('Shell'),
+ 'label' => $part_svc->part_svc_column('shell')->columnlabel || mt('Shell'),
'required' => $part_svc->part_svc_column('shell')->required ) %>
<TD>
<SELECT NAME="shell" SIZE=1>
@@ -289,7 +291,9 @@
'object' => $svc_acct,
'ip_field' => 'slipip',
'required' => $part_svc->part_svc_column('routernum')->required,
+ 'label' => $part_svc->part_svc_column('routernum')->columnlabel,
'ip_addr_required' => $part_svc->part_svc_column('slipip')->required,
+ 'ip_addr_label' => $part_svc->part_svc_column('slipip')->columnlabel,
&>
% } else {
% # don't expose these to the user--they're only useful in the other case
@@ -299,7 +303,7 @@
<INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>">
% } else {
<% include('/elements/tr-td-label.html',
- 'label' => mt('IP'),
+ 'label' => $part_svc->part_svc_column('slipip')->columnlabel || mt('IP'),
'required' => $part_svc->part_svc_column('slipip')->required ) %>
<TD><INPUT TYPE="text" NAME="slipip" VALUE="<% $svc_acct->slipip %>"></TD>
</TR>
@@ -346,7 +350,7 @@
<% include('/elements/tr-td-label.html',
- 'label' => mt('RADIUS groups'),
+ 'label' => $part_svc->part_svc_column('usergroup')->columnlabel || mt('RADIUS groups'),
'required' => $part_svc->part_svc_column('usergroup')->required ) %>
% if ( $part_svc_usergroup->columnflag eq 'F' ) {
<TD BGCOLOR="#eeeeee"><% join('<BR>', @groupnames) %></TD>
diff --git a/httemplate/edit/svc_acct/communigate.html b/httemplate/edit/svc_acct/communigate.html
index 370bfb0e7..544d00be3 100644
--- a/httemplate/edit/svc_acct/communigate.html
+++ b/httemplate/edit/svc_acct/communigate.html
@@ -46,7 +46,7 @@
% if ( $part_svc->part_svc_column('quota')->columnflag eq 'F' ) {
<INPUT TYPE="hidden" NAME="quota" VALUE="<% $svc_acct->quota %>">
% } else {
-% my $quota_label = $communigate ? 'Mail storage limit' : 'Quota';
+% my $quota_label = $communigate ? 'Mail storage limit' : ($part_svc->part_svc_column('quota')->columnlabel || 'Quota');
<% include('/elements/tr-td-label.html',
'label' => $quota_label,
'required' => $part_svc->part_svc_column('quota')->required ) %>
diff --git a/httemplate/edit/tower.html b/httemplate/edit/tower.html
index fa3838dcf..377a33e9b 100644
--- a/httemplate/edit/tower.html
+++ b/httemplate/edit/tower.html
@@ -26,9 +26,9 @@
'default_ip_addr' => 'Tower IP address',
'latitude' => 'Latitude',
'longitude' => 'Longitude',
- 'altitude' => 'Altitude',
- 'height' => 'Height',
- 'veg_height' => 'Vegetation height',
+ 'altitude' => 'Altitude (feet)',
+ 'height' => 'Height (feet)',
+ 'veg_height' => 'Vegetation height (feet)',
'color' => 'Color',
},
&>
@@ -38,7 +38,7 @@ my $m2_error_callback = sub { # reconstruct the list
my ($cgi, $object) = @_;
my @fields = qw(
- sectorname ip_addr height freq_mhz direction width sector_range
+ sectorname ip_addr height freq_mhz direction width tilt v_width margin sector_range
);
map {
diff --git a/httemplate/elements/checkboxes.html b/httemplate/elements/checkboxes.html
index ad9d691b9..b07b6545f 100644
--- a/httemplate/elements/checkboxes.html
+++ b/httemplate/elements/checkboxes.html
@@ -27,7 +27,7 @@ Example:
</%doc>
-<TABLE CELLSPACING=0 CELLPADDING=0>
+<TABLE CELLSPACING=0 CELLPADDING=0 <% $style %>>
% unless ( $opt{'disable_links'} ) {
@@ -108,4 +108,8 @@ $opt{'error_checked_callback'} ||= sub {
$cgi->param($opt{'element_name_prefix'}. $name );
};
+my $style = '';
+if ($opt{'style'}) {
+ $style = 'STYLE="' . $opt{'style'} . '"';
+}
</%init>
diff --git a/httemplate/elements/commission_rate.html b/httemplate/elements/commission_rate.html
new file mode 100644
index 000000000..071ebb1e3
--- /dev/null
+++ b/httemplate/elements/commission_rate.html
@@ -0,0 +1,68 @@
+% unless ( $opt{'js_only'} ) {
+
+ <INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
+
+ <& select.html,
+ field => "${name}_cycle",
+ options => [ '', 1 .. 12 ],
+ option_labels => {
+ '' => '',
+ 1 => '1st',
+ 2 => '2nd',
+ 3 => '3rd',
+ map { $_ => $_.'th' } 4 .. 12
+ },
+ onchange => $onchange,
+ curr_value => $commission_rate->get("cycle"),
+ &>
+ <B><% $money_char %></B>
+ <& input-text.html,
+ field => "${name}_amount",
+ size => 8,
+ curr_value => $commission_rate->get("amount")
+ || '0.00',
+ 'text-align' => 'right'
+ &>
+ <B> + </B>
+ <& input-text.html,
+ field => "${name}_percent",
+ size => 8,
+ curr_value => $commission_rate->get("percent")
+ || '0',
+ 'text-align' => 'right'
+ &><B>%</B>
+% }
+<%init>
+
+my( %opt ) = @_;
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $name = $opt{'field'} || 'commissionratenum';
+my $id = $opt{'id'} || 'commissionratenum';
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+my $onchange = '';
+if ( $opt{'onchange'} ) {
+ $onchange = $opt{'onchange'};
+ $onchange .= '(this)' unless $onchange =~ /\(\w*\);?$/;
+ $onchange =~ s/\(what\);/\(this\);/g; #ugh, terrible hack. all onchange
+ #callbacks should act the same
+ $onchange = 'onChange="'. $onchange. '"';
+}
+
+my $commission_rate;
+if ( $curr_value ) {
+ $commission_rate = qsearchs('commission_rate', { 'commissionratenum' => $curr_value } );
+} else {
+ $commission_rate = new FS::commission_rate {};
+}
+
+foreach my $field (qw( amount percent cycle)) {
+ my $value = $cgi->param("${name}_${field}");
+ $commission_rate->set($field, $value) if $value;
+}
+
+</%init>
diff --git a/httemplate/elements/create_uri_query b/httemplate/elements/create_uri_query
index 414d53ba4..ce6249e0e 100644
--- a/httemplate/elements/create_uri_query
+++ b/httemplate/elements/create_uri_query
@@ -18,7 +18,7 @@ my $query = $cgi->query_string;
if ( length($query) > 1920 || $opt{secure} ) { #stupid IE 2083 URL limit
- my $session = random_id(9);
+ my $session = int(rand(4294967296)); #XXX
my $pref = new FS::access_user_pref({
'usernum' => $FS::CurrentUser::CurrentUser->usernum,
'prefname' => "redirect$session",
diff --git a/httemplate/elements/cust_payby.html b/httemplate/elements/cust_payby.html
index c7d4549df..f30d18557 100644
--- a/httemplate/elements/cust_payby.html
+++ b/httemplate/elements/cust_payby.html
@@ -68,7 +68,7 @@
ID = "<%$id%>_paycvv"
SIZE = 2
MAXLENGTH = 4
- VALUE = "<% scalar($cgi->param($name.'_paycvv')) %>"
+ VALUE = "<% scalar($cgi->param($name.'_paycvv')) || ('*' x length($cust_payby->paycvv)) %>"
onChange = "<% $onchange %>"
>
<BR><FONT SIZE="-1"><% mt('CVV2') |h %>&nbsp;(<A HREF="javascript:void(0);" onClick="overlib( OLiframeContent('<%$p%>docs/cvv2.html', 480, 275, 'cvv2_popup' ), CAPTION, 'CVV2 Help', STICKY, AUTOSTATUSCAP, CLOSECLICK, DRAGGABLE ); return false;"><% mt('help') |h %></A>)</FONT>
@@ -298,8 +298,17 @@ if ( $curr_value ) {
$cust_payby = new FS::cust_payby {};
}
my $sel_payby = $cgi->param($name.'_payby') || $cust_payby->payby;
-$sel_payby = 'CARD' if $sel_payby eq 'DCRD' || $sel_payby eq '';
-$sel_payby = 'CHEK' if $sel_payby eq 'DCHK';
+# convert DCRD to CARD + no weight, and the same for DCHK/CHEK
+if ($sel_payby eq 'DCRD') {
+ $sel_payby = 'CARD';
+ $cust_payby->weight('');
+} elsif ($sel_payby eq 'DCHK') {
+ $sel_payby = 'CHEK';
+ $cust_payby->weight('');
+} elsif (!$sel_payby) {
+ # default
+ $sel_payby = 'CARD';
+}
my @payby = FS::payby->cust_payby;
my %conf_payby = map { $_=>1 } $conf->config('payby');
diff --git a/httemplate/elements/error.html b/httemplate/elements/error.html
index f65785d10..f9664bd65 100644
--- a/httemplate/elements/error.html
+++ b/httemplate/elements/error.html
@@ -1,4 +1,5 @@
% if ( $cgi->param('error') ) {
+% $m->notes('error', $cgi->param('error'));
<FONT SIZE="+1" COLOR="#ff0000"><% mt("Error: [_1]", $cgi->param('error')) |h %></FONT>
<BR><BR>
% }
diff --git a/httemplate/elements/errorpage.html b/httemplate/elements/errorpage.html
index 0f9808da0..d001bfa89 100644
--- a/httemplate/elements/errorpage.html
+++ b/httemplate/elements/errorpage.html
@@ -1,3 +1,5 @@
+% my $error = $_[0];
+% $m->notes('error', $error);
<& /elements/header.html, mt("Error") &>
% while (@_) {
@@ -5,6 +7,5 @@
<P><FONT SIZE="+1" COLOR="#ff0000"><% shift |h %></FONT>
%}
-
% $m->flush_buffer();
% $HTML::Mason::Commands::m->abort();
diff --git a/httemplate/elements/freeside-menu.css b/httemplate/elements/freeside-menu.css
index a66ebc0db..365b9d443 100644
--- a/httemplate/elements/freeside-menu.css
+++ b/httemplate/elements/freeside-menu.css
@@ -142,3 +142,13 @@ a:visited:hover.fsdarkbutton {
overflow:visible;
}
+
+/* elements/notify-tickets.html is in the menu area */
+.dot {
+ border-radius: 50%;
+ border: 1px solid black;
+ width: 1ex;
+ height: 1ex;
+ display: inline-block;
+ margin-top: 0.3ex;
+}
diff --git a/httemplate/elements/freeside.css b/httemplate/elements/freeside.css
index 7bf374c84..cc104a196 100644
--- a/httemplate/elements/freeside.css
+++ b/httemplate/elements/freeside.css
@@ -235,7 +235,7 @@ div.fstabcontainer {
.fsinnerbox th {
font-weight:normal;
font-size:80%;
- valign: bottom;
+ vertical-align: bottom;
color: #666666;
}
@@ -341,3 +341,5 @@ div.package-marker-change_from {
border-left: solid #bbffbb 30px;
display: inline-block;
}
+
+
diff --git a/httemplate/elements/header-full.html b/httemplate/elements/header-full.html
new file mode 100644
index 000000000..850eaed8c
--- /dev/null
+++ b/httemplate/elements/header-full.html
@@ -0,0 +1,242 @@
+<%doc>
+
+Example:
+
+ <& /elements/header.html',
+ {
+ 'title' => 'Title',
+ 'menubar' => \@menubar,
+ 'etc' => '', #included in <BODY> tag, for things like onLoad=
+ 'head' => '', #included before closing </HEAD> tag
+ 'nobr' => 0, #1 for no <BR><BR> after the title
+ 'no_jquery' => #for use from RT, which loads its own
+ }
+ &>
+
+ %#old-style
+ <& /elements/header.html, 'Title', $menubar, $etc, $head &>
+
+</%doc>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+%#<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+%# above is what RT declares, should we switch now? hopefully no glitches result
+%# or just fuck it, XHTML died anyway, HTML 5 or bust?
+<HTML>
+ <HEAD>
+ <TITLE>
+ <% encode_entities($title) || $title_noescape |n %>
+ </TITLE>
+ <!-- per RT, to prevent IE compatibility mode -->
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <!-- The X-UA-Compatible <meta> tag above must be very early in <head> -->
+ <META HTTP-Equiv="Cache-Control" Content="no-cache">
+ <META HTTP-Equiv="Pragma" Content="no-cache">
+ <META HTTP-Equiv="Expires" Content="0">
+% if ( $mobile ) {
+ <META NAME="viewport" content="width=device-width height=device-height user-scalable=yes">
+% }
+
+ <% include('menu.html', 'freeside_baseurl' => $fsurl,
+ 'position' => $menu_position,
+ 'nocss' => $nocss,
+ 'mobile' => $mobile,
+ ) |n
+ %>
+
+% unless ( $no_jquery ) {
+ <link rel="stylesheet" href="<% $fsurl %>elements/jquery-ui.min.css">
+ <SCRIPT SRC="<% $fsurl %>elements/jquery.js"></SCRIPT>
+ <SCRIPT SRC="<% $fsurl %>elements/jquery-ui.min.js"></SCRIPT>
+% if ( $FS::CurrentUser::CurrentUser->option('printtofit') ) {
+ <SCRIPT SRC="<% $fsurl %>elements/printtofit.js"></SCRIPT>
+% }
+% }
+ <% include('init_overlib.html') |n %>
+ <% include('rs_init_object.html') |n %>
+ <script type="text/javascript" src="<% $fsurl %>elements/topreload.js"></script>
+ <% $head |n %>
+
+%# announce our base path, and the Mason comp path of this page
+ <script type="text/javascript">
+ window.fsurl = <% $fsurl |js_string %>;
+ window.request_comp_path = <% $m->request_comp->path |js_string %>;
+ </script>
+
+ </HEAD>
+ <BODY BGCOLOR="#f8f8f8" <% $etc |n %> STYLE="margin-top:0; margin-bottom:0; margin-left:0px; margin-right:0px">
+ <table width="100%" CELLPADDING=0 CELLSPACING=0 STYLE="padding-left:0px; padding-right:4px" CLASS="fshead">
+ <tr>
+ <td BGCOLOR="#ffffff"><% $company_url ? qq(<A HREF="$company_url">) : '' |n %><IMG BORDER=0 ALT="freeside" HEIGHT="36" SRC="<%$fsurl%>view/REAL_logo.cgi"><% $company_url ? '</A>' : '' |n %></td>
+ <td align=left BGCOLOR="#ffffff"> <!-- valign="top" -->
+ <font size=6><% $company_name || 'ExampleCo' %></font>
+ </td>
+ <td align="right" BGCOLOR="#ffffff">
+ <& notify-tickets.html &>
+ </td>
+ <td align=right valign=top BGCOLOR="#ffffff"><FONT SIZE="-1">Logged in as <b><% $FS::CurrentUser::CurrentUser->username |h %>&nbsp;</b> <FONT SIZE="-2"><a href="<%$fsurl%>loginout/logout.html">logout</a></FONT><br></FONT><FONT SIZE="-2"><a href="<%$fsurl%>pref/pref.html" STYLE="color: #000000">Preferences</a>
+% if ( $conf->config("ticket_system")
+% && FS::TicketSystem->access_right(\%session, 'ModifySelf') ) {
+ | <a href="<%$fsurl%>rt/Prefs/Other.html" STYLE="color: #000000">Ticketing preferences</a>
+% }
+ <BR></FONT>
+ </td>
+ </tr>
+ </table>
+
+ <TABLE WIDTH="100%" CELLSPACING=0 CELLPADDING=0>
+
+<link href="<%$fsurl%>elements/freeside-menu.css" type="text/css" rel="stylesheet">
+
+% if ( $menu_position eq 'top' ) {
+
+ <TR CLASS="fsmenubar">
+
+% if ( $mobile ) {
+
+ <TD STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079;width:auto" BGCOLOR="#dddddd">
+ <SCRIPT TYPE="text/javascript">
+ document.write(myBar.toString());
+ </SCRIPT>
+ </TD>
+ <TD STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079;width:auto" BGCOLOR="#dddddd">
+ <% include('searchbar-combined.html') |n %>
+ </TD>
+
+% } else {
+
+ <TD COLSPAN="7" WIDTH="100%" STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079" BGCOLOR="#dddddd">
+ <SCRIPT TYPE="text/javascript">
+ document.write(myBar);
+ </SCRIPT>
+ </TD>
+
+ </TR>
+
+ <TR CLASS="fssearchbar">
+
+ <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px">
+ <% include('searchbar-prospect.html') |n %>
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px">
+ <% include('searchbar-cust_main.html') |n %>
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="center">
+ <% include('searchbar-address2.html') |n %>
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right">
+ <% include('searchbar-cust_bill.html') |n %>
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px">
+ <% include('searchbar-cust_svc.html') |n %>
+ </TD>
+
+ <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px;padding-right:2px">
+ <% include('searchbar-ticket.html') |n %>
+ </TD>
+% }
+
+ </TR>
+ </TABLE>
+
+% } else { #$menu_position eq 'left'
+
+ <TR CLASS="fsmenubar">
+
+ <TD COLSPAN="7" WIDTH="100%" STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079" BGCOLOR="#dddddd">
+ </TD>
+
+ </TR>
+
+% }
+
+
+ <TABLE WIDTH="100%" HEIGHT="100%" CELLSPACING=0 CELLPADDING=4>
+
+ <TR HEIGHT="100%">
+
+% if ( $menu_position eq 'left' ) {
+
+ <TD BGCOLOR="#dddddd" ALIGN="left" HEIGHT="100%" WIDTH="154" VALIGN="top" ALIGN="right" CLASS="fsmenubar">
+ <SCRIPT TYPE="text/javascript">
+ document.write(myBar);
+ </SCRIPT>
+
+ <BR>
+ <% include('searchbar-prospect.html') |n %>
+ <% include('searchbar-cust_main.html') |n %>
+ <% include('searchbar-address2.html') |n %>
+ <% include('searchbar-cust_bill.html') |n %>
+ <% include('searchbar-cust_svc.html') |n %>
+ <% include('searchbar-ticket.html') |n %>
+
+ </TD>
+
+% } else { #$menu_position eq 'top'
+ <BR>
+% }
+%# page content starts here
+ <TD CLASS="background" HEIGHT="100%" VALIGN="top"> <!-- WIDTH="100%"> -->
+
+ <H1>
+ <% $title_noescape || encode_entities($title) %>
+ </H1>
+
+% unless ( $nobr ) {
+ <BR>
+% }
+
+ <% $menubar !~ /^\s*$/ ? "$menubar<BR><BR>" : '' %>
+
+<%init>
+
+my( $title, $title_noescape, $menubar, $etc, $head ) = ( '', '', '', '', '' );
+my( $nobr, $nocss, $no_jquery ) = ( 0, 0, 0 );
+
+my $mobile;
+
+if ( ref($_[0]) ) {
+ my $opt = shift;
+ $title = $opt->{title};
+ $title_noescape = $opt->{title_noescape};
+ $menubar = $opt->{menubar};
+ $etc = $opt->{etc};
+ $head = $opt->{head};
+ $nobr = $opt->{nobr};
+ $nocss = $opt->{nocss};
+ $mobile = $opt->{mobile};
+ $no_jquery = $opt->{no_jquery};
+} else {
+ ($title, $menubar) = ( shift, shift );
+ $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
+ $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section
+}
+
+my $conf = new FS::Conf;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $menu_position = $curuser->option('menu_position')
+ || 'top'; #new default for 1.9
+
+if ( !defined($mobile) ) {
+ $mobile = $curuser->option('mobile_menu',1) && FS::UI::Web::is_mobile();
+}
+if ( $cgi->param('mobile') =~ /^(\d)$/ ) { # allow client to override
+ $mobile = $1;
+}
+
+my($company_name, $company_url);
+my @agentnums = $curuser->agentnums;
+if ( scalar(@agentnums) == 1 ) {
+ $company_name = $conf->config('company_name', $agentnums[0] );
+ $company_url = $conf->config('company_url', $agentnums[0] );
+} else {
+ $company_name = $conf->config('company_name');
+ $company_url = $conf->config('company_url');
+}
+
+</%init>
diff --git a/httemplate/elements/header-popup.html b/httemplate/elements/header-popup.html
index 17593693e..839a63676 100644
--- a/httemplate/elements/header-popup.html
+++ b/httemplate/elements/header-popup.html
@@ -30,7 +30,11 @@ Example:
<META HTTP-Equiv="Expires" Content="0">
% unless ( $no_jquery ) {
<SCRIPT SRC="<% $fsurl %>elements/jquery.js"></SCRIPT>
+% if ( $FS::CurrentUser::CurrentUser->option('printtofit') ) {
+ <SCRIPT SRC="<% $fsurl %>elements/printtofit.js"></SCRIPT>
+% }
% }
+ <SCRIPT SRC="<% $fsurl %>elements/topreload.js"></SCRIPT>
<% $head |n %>
</HEAD>
<BODY <% $etc |n %>>
diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html
index c8a83ca7e..c6b10e301 100644
--- a/httemplate/elements/header.html
+++ b/httemplate/elements/header.html
@@ -1,228 +1,6 @@
-<%doc>
-
-Example:
-
- <& /elements/header.html',
- {
- 'title' => 'Title',
- 'menubar' => \@menubar,
- 'etc' => '', #included in <BODY> tag, for things like onLoad=
- 'head' => '', #included before closing </HEAD> tag
- 'nobr' => 0, #1 for no <BR><BR> after the title
- 'no_jquery' => #for use from RT, which loads its own
- }
- &>
-
- %#old-style
- <& /elements/header.html, 'Title', $menubar, $etc, $head &>
-
-</%doc>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-%#<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-%# above is what RT declares, should we switch now? hopefully no glitches result
-%# or just fuck it, XHTML died anyway, HTML 5 or bust?
-<HTML>
- <HEAD>
- <TITLE>
- <% encode_entities($title) || $title_noescape |n %>
- </TITLE>
- <!-- per RT, to prevent IE compatibility mode -->
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
- <!-- The X-UA-Compatible <meta> tag above must be very early in <head> -->
- <META HTTP-Equiv="Cache-Control" Content="no-cache">
- <META HTTP-Equiv="Pragma" Content="no-cache">
- <META HTTP-Equiv="Expires" Content="0">
-% if ( $mobile ) {
- <META NAME="viewport" content="width=device-width height=device-height user-scalable=yes">
+% # for testing, disable the page menus/search boxes
+% if ( $FS::CurrentUser::CurrentUser->option('header-minimal') ) {
+<& header-minimal.html, @_ &>
+% } else {
+<& header-full.html, @_ &>
% }
-
- <% include('menu.html', 'freeside_baseurl' => $fsurl,
- 'position' => $menu_position,
- 'nocss' => $nocss,
- 'mobile' => $mobile,
- ) |n
- %>
-
-% unless ( $no_jquery ) {
- <link rel="stylesheet" href="<% $fsurl %>elements/jquery-ui.min.css">
- <SCRIPT SRC="<% $fsurl %>elements/jquery.js"></SCRIPT>
- <SCRIPT SRC="<% $fsurl %>elements/jquery-ui.min.js"></SCRIPT>
-% }
- <% include('init_overlib.html') |n %>
- <% include('rs_init_object.html') |n %>
-
- <% $head |n %>
-
- </HEAD>
- <BODY BGCOLOR="#f8f8f8" <% $etc |n %> STYLE="margin-top:0; margin-bottom:0; margin-left:0px; margin-right:0px">
- <table width="100%" CELLPADDING=0 CELLSPACING=0 STYLE="padding-left:0px; padding-right:4px" CLASS="fshead">
- <tr>
- <td BGCOLOR="#ffffff"><% $company_url ? qq(<A HREF="$company_url">) : '' |n %><IMG BORDER=0 ALT="freeside" HEIGHT="36" SRC="<%$fsurl%>view/REAL_logo.cgi"><% $company_url ? '</A>' : '' |n %></td>
- <td align=left BGCOLOR="#ffffff"> <!-- valign="top" -->
- <font size=6><% $company_name || 'ExampleCo' %></font>
- </td>
- <td align=right valign=top BGCOLOR="#ffffff"><FONT SIZE="-1">Logged in as <b><% $FS::CurrentUser::CurrentUser->username |h %>&nbsp;</b> <FONT SIZE="-2"><a href="<%$fsurl%>loginout/logout.html">logout</a></FONT><br></FONT><FONT SIZE="-2"><a href="<%$fsurl%>pref/pref.html" STYLE="color: #000000">Preferences</a>
-% if ( $conf->config("ticket_system")
-% && FS::TicketSystem->access_right(\%session, 'ModifySelf') ) {
- | <a href="<%$fsurl%>rt/Prefs/Other.html" STYLE="color: #000000">Ticketing preferences</a>
-% }
- <BR></FONT>
- </td>
- </tr>
- </table>
-
- <TABLE WIDTH="100%" CELLSPACING=0 CELLPADDING=0>
-
-<link href="<%$fsurl%>elements/freeside-menu.css" type="text/css" rel="stylesheet">
-
-% if ( $menu_position eq 'top' ) {
-
- <TR CLASS="fsmenubar">
-
-% if ( $mobile ) {
-
- <TD STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079;width:auto" BGCOLOR="#dddddd">
- <SCRIPT TYPE="text/javascript">
- document.write(myBar.toString());
- </SCRIPT>
- </TD>
- <TD STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079;width:auto" BGCOLOR="#dddddd">
- <% include('searchbar-combined.html') |n %>
- </TD>
-
-% } else {
-
- <TD COLSPAN="7" WIDTH="100%" STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079" BGCOLOR="#dddddd">
- <SCRIPT TYPE="text/javascript">
- document.write(myBar);
- </SCRIPT>
- </TD>
-
- </TR>
-
- <TR CLASS="fssearchbar">
-
- <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px">
- <% include('searchbar-prospect.html') |n %>
- </TD>
-
- <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px">
- <% include('searchbar-cust_main.html') |n %>
- </TD>
-
- <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="center">
- <% include('searchbar-address2.html') |n %>
- </TD>
-
- <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right">
- <% include('searchbar-cust_bill.html') |n %>
- </TD>
-
- <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px">
- <% include('searchbar-cust_svc.html') |n %>
- </TD>
-
- <TD COLSPAN=1 BGCOLOR="#dddddd" ALIGN="right" STYLE="padding-left:2px;padding-right:2px">
- <% include('searchbar-ticket.html') |n %>
- </TD>
-% }
-
- </TR>
- </TABLE>
-
-% } else { #$menu_position eq 'left'
-
- <TR CLASS="fsmenubar">
-
- <TD COLSPAN="7" WIDTH="100%" STYLE="padding:1px 0px 0px 0px;border-top: 1px solid #7e0079" BGCOLOR="#dddddd">
- </TD>
-
- </TR>
-
-% }
-
-
- <TABLE WIDTH="100%" HEIGHT="100%" CELLSPACING=0 CELLPADDING=4>
-
- <TR HEIGHT="100%">
-
-% if ( $menu_position eq 'left' ) {
-
- <TD BGCOLOR="#dddddd" ALIGN="left" HEIGHT="100%" WIDTH="154" VALIGN="top" ALIGN="right" CLASS="fsmenubar">
- <SCRIPT TYPE="text/javascript">
- document.write(myBar);
- </SCRIPT>
-
- <BR>
- <% include('searchbar-prospect.html') |n %>
- <% include('searchbar-cust_main.html') |n %>
- <% include('searchbar-address2.html') |n %>
- <% include('searchbar-cust_bill.html') |n %>
- <% include('searchbar-cust_svc.html') |n %>
- <% include('searchbar-ticket.html') |n %>
-
- </TD>
-
-% } else { #$menu_position eq 'top'
- <BR>
-% }
-%# page content starts here
- <TD CLASS="background" HEIGHT="100%" VALIGN="top"> <!-- WIDTH="100%"> -->
-
- <H1>
- <% $title_noescape || encode_entities($title) %>
- </H1>
-
-% unless ( $nobr ) {
- <BR>
-% }
-
- <% $menubar !~ /^\s*$/ ? "$menubar<BR><BR>" : '' %>
-<%init>
-
-my( $title, $title_noescape, $menubar, $etc, $head ) = ( '', '', '', '', '' );
-my( $nobr, $nocss, $no_jquery ) = ( 0, 0, 0 );
-
-my $mobile;
-
-if ( ref($_[0]) ) {
- my $opt = shift;
- $title = $opt->{title};
- $title_noescape = $opt->{title_noescape};
- $menubar = $opt->{menubar};
- $etc = $opt->{etc};
- $head = $opt->{head};
- $nobr = $opt->{nobr};
- $nocss = $opt->{nocss};
- $mobile = $opt->{mobile};
- $no_jquery = $opt->{no_jquery};
-} else {
- ($title, $menubar) = ( shift, shift );
- $etc = @_ ? shift : ''; #$etc is for things like onLoad= etc.
- $head = @_ ? shift : ''; #$head is for things that go in the <HEAD> section
-}
-
-my $conf = new FS::Conf;
-
-my $curuser = $FS::CurrentUser::CurrentUser;
-
-my $menu_position = $curuser->option('menu_position')
- || 'top'; #new default for 1.9
-
-if ( !defined($mobile) ) {
- $mobile = $curuser->option('mobile_menu',1) && FS::UI::Web::is_mobile();
-}
-if ( $cgi->param('mobile') =~ /^(\d)$/ ) { # allow client to override
- $mobile = $1;
-}
-
-my($company_name, $company_url);
-my @agentnums = $curuser->agentnums;
-if ( scalar(@agentnums) == 1 ) {
- $company_name = $conf->config('company_name', $agentnums[0] );
- $company_url = $conf->config('company_url', $agentnums[0] );
-} else {
- $company_name = $conf->config('company_name');
- $company_url = $conf->config('company_url');
-}
-</%init>
diff --git a/httemplate/elements/location.html b/httemplate/elements/location.html
index b5f0a963c..3c8e973ed 100644
--- a/httemplate/elements/location.html
+++ b/httemplate/elements/location.html
@@ -245,13 +245,13 @@ Example:
% }
% if ( $opt{enable_district} and $conf->config('tax_district_method') ) {
<TR>
- <TD ALIGN="right">Tax&nbsp;district</TD>
+ <TH ALIGN="right">Tax&nbsp;district</TH>
<TD COLSPAN=8>
<INPUT TYPE="text" SIZE=15
NAME="<%$pre%>district"
ID="<%$pre%>district"
VALUE="<% $object->district |h %>">
- <% '(automatic)' %>
+ <FONT SIZE="-1" COLOR="#333333"><% '(automatic)' %></FONT>
</TD>
</TR>
% } else {
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index c385cb4c7..51ec26329 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -275,6 +275,7 @@ $report_packages{'Suspension summary'} = [ $fsurl.'search/cust_pkg_susp.html', '
$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'} = [ $fsurl.'search/report_477.html' ]
if $conf->exists('part_pkg-show_fcc_options');
+$report_packages{'Contract end dates'} = [ $fsurl.'search/cust_pkg-date.html?date=contract_end', 'Show packages by contract end date' ];
$report_packages{'Advanced package reports'} = [ $fsurl.'search/report_cust_pkg.html', 'by agent, date range, status, package definition' ];
tie my %report_inventory, 'Tie::IxHash',
@@ -320,7 +321,6 @@ tie my %report_ticketing, 'Tie::IxHash',
;
tie my %report_bill_event, 'Tie::IxHash',
- 'All billing events' => [ $fsurl.'search/report_cust_event.html', 'All billing events for a date range' ],
'Billing event errors' => [ $fsurl.'search/report_cust_event.html?failed=1', 'Failed credit cards, processor or printer problems, etc.' ],
;
@@ -365,7 +365,8 @@ tie my %report_commissions, 'Tie::IxHash',
'Agent per package' => [ $fsurl.'search/report_agent_commission_pkg.html' ],
'Sales Person' => [ $fsurl.'search/report_sales_commission.html' ],
'Sales Person per package' => [ $fsurl.'search/report_sales_commission_pkg.html' ],
- 'Employee' => [ $fsurl.'search/report_employee_commission.html', '' ]
+ 'Employee' => [ $fsurl.'search/report_employee_commission.html', '' ],
+ 'Agent Credits and Payments' => [ $fsurl.'search/report_agent_credit_payment.html' ],
;
tie my %report_financial, 'Tie::IxHash';
@@ -404,7 +405,7 @@ tie my %report_payable, 'Tie::IxHash',
;
tie my %report_logs, 'Tie::IxHash';
-$report_logs{'Billing events'} = [ \%report_bill_event, 'Billing events' ]
+$report_logs{'Billing events'} = [ $fsurl.'search/report_cust_event.html', 'Search billing events by date and status' ]
if $curuser->access_right('Billing event reports');
$report_logs{'Credit limit incidents'} = [ $fsurl.'search/report_cust_main_credit_limit.html', '' ]
if $curuser->access_right('List rating data');
@@ -672,7 +673,10 @@ $config_cust{'Note classes'} = [ $fsurl.'browse/cust_note_class.html', 'Note cla
tie my %config_agent, 'Tie::IxHash',
'Agent types' => [ $fsurl.'browse/agent_type.cgi', 'Agent types define groups of package definitions that you can then assign to particular agents' ],
'Agents' => [ $fsurl.'browse/agent.cgi', 'Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their type)' ],
- 'Agent payment gateways' => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors for agent overrides' ];
+ 'Agent payment gateways' => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors for agent overrides' ],
+ 'separator' => '',
+ 'Commission schedules' => [ $fsurl.'browse/commission_schedule.html',
+ 'Commission schedules for consecutive billing periods' ],
;
tie my %config_sales, 'Tie::IxHash',
diff --git a/httemplate/elements/notify-tickets.html b/httemplate/elements/notify-tickets.html
new file mode 100644
index 000000000..e661737bc
--- /dev/null
+++ b/httemplate/elements/notify-tickets.html
@@ -0,0 +1,27 @@
+% if ($enabled) {
+<div style="font-weight: bold; vertical-align: bottom; text-align: left">
+% if ( $UnrepliedTickets->Count > 0 ) {
+ <a href="<% $fsurl %>rt/Search/UnrepliedTickets.html">
+ <div class="dot" style="background-color: green"></div>
+ <% emt('New activity on [quant,_1,ticket]', $UnrepliedTickets->Count) %>
+ </a>
+% } else {
+ <% emt('No new activity on tickets') %>
+% }
+</div>
+% }
+<%init>
+use Class::Load 'load_class';
+
+my $enabled = $FS::TicketSystem::system eq 'RT_Internal';
+my $UnrepliedTickets;
+if ($enabled) {
+ my $class = 'RT::Search::UnrepliedTickets';
+ load_class($class);
+ my $session = FS::TicketSystem->session;
+ my $CurrentUser = $session->{CurrentUser};
+ $UnrepliedTickets = RT::Tickets->new($CurrentUser);
+ my $search = $class->new(TicketsObj => $UnrepliedTickets);
+ $search->Prepare;
+}
+</%init>
diff --git a/httemplate/elements/page_pref.js b/httemplate/elements/page_pref.js
new file mode 100644
index 000000000..253c6da86
--- /dev/null
+++ b/httemplate/elements/page_pref.js
@@ -0,0 +1,10 @@
+function set_page_pref(prefname, tablenum, prefvalue, success) {
+ jQuery.post( window.fsurl + 'misc/process/set_page_pref.html',
+ { path: window.request_comp_path,
+ name: prefname,
+ num: tablenum,
+ value: prefvalue
+ },
+ success
+ );
+}
diff --git a/httemplate/elements/printtofit.js b/httemplate/elements/printtofit.js
new file mode 100644
index 000000000..66257fca8
--- /dev/null
+++ b/httemplate/elements/printtofit.js
@@ -0,0 +1,26 @@
+$().ready(function() {
+ var beforePrint = function() {
+ if ($('body').width() > 0) {
+ // 7.5 inches * 96 DPI; maybe make the width a user pref?
+ var maxwidth = 7.5 * 96;
+ $('body').css('zoom', maxwidth / $('body').width());
+ }
+ };
+ var afterPrint = function() {
+ $('body').css('zoom', 1);
+ }
+
+ if (window.matchMedia) { // chrome, most importantly; also IE10?
+ window.matchMedia('print').addListener(
+ function(mq) {
+ mq.matches ? beforePrint() : afterPrint();
+ }
+ );
+ } else { // other IE
+ $(window).on('beforeprint', beforePrint);
+ $(window).on('afterprint', afterPrint);
+ }
+ // got nothing for firefox
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=774398
+ // but firefox already has "shrink to fit"
+});
diff --git a/httemplate/elements/progress-init.html b/httemplate/elements/progress-init.html
index e38dde65f..0c2b8165a 100644
--- a/httemplate/elements/progress-init.html
+++ b/httemplate/elements/progress-init.html
@@ -98,14 +98,14 @@ function <%$key%>process () {
overlib( 'Submitting job to server...', WIDTH, 444, HEIGHT, 168, CAPTION, 'Please wait...', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', CLOSECLICK, MIDX, 0, MIDY, 0 );
+ // jQuery .serializeArray() maybe?
+ var copy_fields = <% encode_json(\%copy_fields) %>;
var Hash = new Array();
var x = 0;
var fieldName;
for (var i = 0; i<document.<%$formname%>.elements.length; i++) {
field = document.<%$formname%>.elements[i];
- if ( <% join(' || ', map { "(field.name.indexOf('$_') > -1)" } @$fields ) %>
- )
- {
+ if ( <% $all_fields %> || copy_fields[ field.name ] ) {
if ( field.type == 'select-multiple' ) {
//alert('select-multiple ' + field.name);
for (var j=0; j < field.options.length; j++) {
@@ -168,6 +168,14 @@ $progress_url->query_form(
%dest_info,
);
+my $all_fields = 0;
+my %copy_fields;
+if (grep '/^ALL$/', @$fields) {
+ $all_fields = 1;
+} else {
+ %copy_fields = map { $_ => 1 } @$fields;
+}
+
#stupid safari is caching the "location" of popup iframs, and submitting them
#instead of displaying them. this should prevent that.
my $popup_name = 'popup-'.random_id();
diff --git a/httemplate/elements/select-areacode.html b/httemplate/elements/select-areacode.html
index f0f56d56d..612f03a8b 100644
--- a/httemplate/elements/select-areacode.html
+++ b/httemplate/elements/select-areacode.html
@@ -28,6 +28,7 @@
function <% $opt{'prefix'} %>update_areacodes(areacodes) {
+ var reply = JSON.parse(areacodes);
// blank the current areacode
for ( var i = what.form.<% $opt{'prefix'} %>areacode.length; i >= 0; i-- )
what.form.<% $opt{'prefix'} %>areacode.options[i] = null;
@@ -47,7 +48,7 @@
% }
// add the new areacodes
- var areacodeArray = eval('(' + areacodes + ')' );
+ var areacodeArray = reply.areacodes;
for ( var s = 0; s < areacodeArray.length; s++ ) {
var areacodeLabel = areacodeArray[s];
if ( areacodeLabel == "" )
@@ -62,6 +63,11 @@
} else {
var areacodeerror = document.getElementById('<% $opt{'prefix'} %>areacodeerror');
areacodeerror.style.display = 'inline';
+ if ( reply.error ) {
+ areacodeerror.textContent = reply.error;
+ } else {
+ areacodeerror.textContent = 'Select a different state';
+ }
}
//run the callback
@@ -78,7 +84,7 @@
<DIV ID="areacodewait" STYLE="display:none"><IMG SRC="<%$fsurl%>images/wait-orange.gif"> <B>Finding area codes</B></DIV>
-<DIV ID="areacodeerror" STYLE="display:none"><IMG SRC="<%$fsurl%>images/cross.png"> <B>Select a different state</B></DIV>
+<DIV ID="areacodeerror" STYLE="display:none; font-weight: bold"><IMG SRC="<%$fsurl%>images/cross.png"></DIV>
<SELECT NAME="<% $opt{'prefix'} %>areacode" onChange="<% $opt{'prefix'} %>areacode_changed(this); <% $opt{'onchange'} %>" <% $opt{'disabled'} %>>
<OPTION VALUE="">Select area code</OPTION>
diff --git a/httemplate/elements/select-exchange.html b/httemplate/elements/select-exchange.html
index b9677094a..33def31b9 100644
--- a/httemplate/elements/select-exchange.html
+++ b/httemplate/elements/select-exchange.html
@@ -27,6 +27,7 @@
function <% $opt{'prefix'} %>update_exchanges(exchanges) {
+ var reply = JSON.parse(exchanges);
// blank the current exchange
for ( var i = what.form.<% $opt{'prefix'} %>exchange.length; i >= 0; i-- )
what.form.<% $opt{'prefix'} %>exchange.options[i] = null;
@@ -42,7 +43,7 @@
% }
// add the new exchanges
- var exchangeArray = eval('(' + exchanges + ')' );
+ var exchangeArray = reply.exchanges;
for ( var s = 0; s < exchangeArray.length; s++ ) {
var exchangeLabel = exchangeArray[s];
if ( exchangeLabel == "" )
@@ -57,6 +58,12 @@
} else {
var exchangeerror = document.getElementById('<% $opt{'prefix'} %>exchangeerror');
exchangeerror.style.display = 'inline';
+ if ( reply.error ) {
+ exchangeerror.textContent = reply.error;
+ } else {
+ exchangeerror.textContent = 'Select a different area code';
+ }
+
}
//run the callback
@@ -73,7 +80,7 @@
<DIV ID="exchangewait" STYLE="display:none"><IMG SRC="<%$fsurl%>images/wait-orange.gif"> <B>Finding cities / exchanges</B></DIV>
-<DIV ID="exchangeerror" STYLE="display:none"><IMG SRC="<%$fsurl%>images/cross.png"> <B>Select a different area code</B></DIV>
+<DIV ID="exchangeerror" STYLE="display:none; font-weight: bold"><IMG SRC="<%$fsurl%>images/cross.png"></DIV>
<SELECT NAME="<% $opt{'prefix'} %>exchange" onChange="<% $opt{'prefix'} %>exchange_changed(this); <% $opt{'onchange'} %>" <% $opt{'disabled'} %>>
<OPTION VALUE="">Select city / exchange</OPTION>
diff --git a/httemplate/elements/select-phonenum.html b/httemplate/elements/select-phonenum.html
index 118fe4901..dd1b84736 100644
--- a/httemplate/elements/select-phonenum.html
+++ b/httemplate/elements/select-phonenum.html
@@ -56,6 +56,7 @@ passing the exchange (or region) and
function <% $opt{'prefix'} %>update_phonenums(phonenums) {
+ var reply = JSON.parse(phonenums);
// blank the current phonenum
for ( var i = what.form.<% $opt{'prefix'} %>phonenum.length; i >= 0; i-- )
what.form.<% $opt{'prefix'} %>phonenum.options[i] = null;
@@ -67,7 +68,7 @@ passing the exchange (or region) and
% }
// add the new phonenums
- var phonenumArray = eval('(' + phonenums + ')' );
+ var phonenumArray = reply.phonenums;
for ( var s = 0; s < phonenumArray.length; s++ ) {
var phonenumLabel = phonenumArray[s];
if ( phonenumLabel == "" )
@@ -86,6 +87,11 @@ passing the exchange (or region) and
} else {
var phonenumerror = document.getElementById('<% $opt{'prefix'} %>phonenumerror');
phonenumerror.style.display = 'inline';
+ if ( reply.error ) {
+ phonenumerror.textContent = reply.error;
+ } else {
+ phonenumerror.textContent = 'Select a different <% $opt{'region'} ? 'region' : 'city/exchange' %>';
+ }
}
//run the callback
@@ -157,7 +163,7 @@ passing the exchange (or region) and
% unless ( $opt{'tollfree'} ) {
<DIV ID="phonenumwait" STYLE="display:none"><IMG SRC="<%$fsurl%>images/wait-orange.gif"> <B>Finding phone numbers</B></DIV>
-<DIV ID="phonenumerror" STYLE="display:none"><IMG SRC="<%$fsurl%>images/cross.png"> <B>Select a different <% $opt{'region'} ? 'region' : 'city/exchange' %></B></DIV>
+<DIV ID="phonenumerror" STYLE="display:none; font-weight: bold"><IMG SRC="<%$fsurl%>images/cross.png"></DIV>
% }
<SELECT <% $opt{multiple} ? 'MULTIPLE SIZE=25' : '' %>
diff --git a/httemplate/elements/select-region.html b/httemplate/elements/select-region.html
index 7ed959269..46c37c935 100644
--- a/httemplate/elements/select-region.html
+++ b/httemplate/elements/select-region.html
@@ -27,6 +27,7 @@
function <% $opt{'prefix'} %>update_regions(regions) {
+ var reply = JSON.parse(regions);
// blank the current region
for ( var i = what.form.<% $opt{'prefix'} %>region.length; i >= 0; i-- )
what.form.<% $opt{'prefix'} %>region.options[i] = null;
@@ -42,7 +43,7 @@
% }
// add the new regions
- var regionArray = eval('(' + regions + ')' );
+ var regionArray = reply.regions;
for ( var s = 0; s < regionArray.length; s++ ) {
var regionLabel = regionArray[s];
if ( regionLabel == "" )
@@ -57,6 +58,11 @@
} else {
var regionerror = document.getElementById('<% $opt{'prefix'} %>regionerror');
regionerror.style.display = 'inline';
+ if ( reply.error ) {
+ regionerror.textContent = reply.error;
+ } else {
+ regionerror.textContent = 'Select a different state';
+ }
}
//run the callback
@@ -73,7 +79,7 @@
<DIV ID="<% $opt{'prefix'} %>regionwait" STYLE="display:none"><IMG SRC="<%$fsurl%>images/wait-orange.gif"> <B>Finding regions</B></DIV>
-<DIV ID="<% $opt{'prefix'} %>regionerror" STYLE="display:none"><IMG SRC="<%$fsurl%>images/cross.png"> <B>Select a different state</B></DIV>
+<DIV ID="<% $opt{'prefix'} %>regionerror" STYLE="display:none; font-weight: bold"><IMG SRC="<%$fsurl%>images/cross.png"></DIV>
<SELECT NAME="<% $opt{'prefix'} %>region" onChange="<% $opt{'prefix'} %>region_changed(this); <% $opt{'onchange'} %>" <% $opt{'disabled'} %>>
<OPTION VALUE="">Select region</OPTION>
diff --git a/httemplate/elements/select-rt-customfield.html b/httemplate/elements/select-rt-customfield.html
index 85758d585..488accac3 100644
--- a/httemplate/elements/select-rt-customfield.html
+++ b/httemplate/elements/select-rt-customfield.html
@@ -1,31 +1,27 @@
-<SELECT NAME="<% $opt{name} %>">
+<SELECT NAME="<% $opt{'name'} %>"<% $opt{'multiple'} ? ' MULTIPLE' : '' %>>
% while ( @fields ) {
-<OPTION VALUE="<% shift @fields %>"><% shift @fields %></OPTION>
+% my $value = shift @fields;
+% my $label = shift @fields;
+<OPTION VALUE="<% $value %>"<% $curr_value{$value} ? ' SELECTED' : '' %>><% $label %></OPTION>
% }
</SELECT>
<%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 %curr_value = map { $_ => 1 } split(', ',$opt{'curr_value'});
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);
+my $conf = new FS::Conf;
+
+if ($conf->config('ticket_system') eq 'RT_Internal') {
+
+ push @fields, FS::TicketSystem->custom_fields(
+ lookuptype => $opt{lookuptype},
+ valuetype => $opt{valuetype},
+ );
+
}
+
</%init>
diff --git a/httemplate/elements/select-rt-queue.html b/httemplate/elements/select-rt-queue.html
new file mode 100644
index 000000000..289336516
--- /dev/null
+++ b/httemplate/elements/select-rt-queue.html
@@ -0,0 +1,24 @@
+<SELECT NAME="<% $opt{'name'} || $opt{'field'} %>"<% $opt{'multiple'} ? ' MULTIPLE' : '' %>>
+% while ( @fields ) {
+% my $value = shift @fields;
+% my $label = shift @fields;
+<OPTION VALUE="<% $value %>"<% $curr_value{$value} ? ' SELECTED' : '' %>><% $label %></OPTION>
+% }
+</SELECT>
+<%init>
+my %opt = @_;
+
+my %curr_value = map { $_ => 1 } split(', ',$opt{'curr_value'});
+
+my @fields;
+push @fields, '', $opt{empty_label} if exists($opt{empty_label});
+
+my $conf = new FS::Conf;
+
+if ($conf->config('ticket_system') eq 'RT_Internal') {
+
+ push @fields, FS::TicketSystem->queues();
+
+}
+
+</%init>
diff --git a/httemplate/elements/select-svc.html b/httemplate/elements/select-svc.html
new file mode 100644
index 000000000..b439a2852
--- /dev/null
+++ b/httemplate/elements/select-svc.html
@@ -0,0 +1,73 @@
+<%init>
+my %opt = @_;
+my $svcdb = $opt{table};
+my $field = $opt{field} || 'svcnum';
+my $id = $opt{id} || $opt{field};
+my $curr_value = [ split(',', $opt{curr_value} || '') ];
+my $label = $opt{name_col} || 'label';
+
+# cut-down, jquerified version of select-tiered
+# XXX do we need to agent-virt this? edit/part_svc is Configuration-access.
+my @part_svc = qsearch('part_svc', {
+ disabled => '',
+ svcdb => $svcdb
+});
+my %labels; # service labels, of some kind
+my %values; # svcnums
+my (@all_l, @all_v);
+foreach my $part_svc (@part_svc) {
+ my (@l, @v);
+ foreach my $svc_x (qsearch({
+ 'table' => 'cust_svc',
+ 'addl_from' => " JOIN $svcdb USING (svcnum)",
+ 'select' => "$svcdb.*, cust_svc.svcpart",
+ 'hashref' => { 'svcpart' => $part_svc->svcpart },
+ }))
+ {
+ push @l, $svc_x->$label;
+ push @all_l, $svc_x->$label;
+ push @v, $svc_x->svcnum;
+ push @all_v, $svc_x->svcnum;
+ }
+ $labels{ $part_svc->svcpart } = \@l;
+ $values{ $part_svc->svcpart } = \@v;
+}
+$labels{ '' } = \@all_l;
+$values{ '' } = \@all_v;
+
+</%init>
+<& /elements/select-table.html,
+ 'table' => 'part_svc',
+ 'records' => \@part_svc,
+ 'id' => $id.'_svcpart',
+ 'name_col' => 'svc',
+ 'empty_label' => 'any',
+ 'curr_value' => '',
+ 'field' => $id.'_svcpart', # avoid confusion with any other field
+&>
+<BR>
+<& /elements/select.html,
+ %opt,
+ 'field' => $field,
+ 'id' => $id,
+&>
+<script>
+$().ready(function() {
+ var labels = <% encode_json(\%labels) %>;
+ var values = <% encode_json(\%values) %>;
+ var select_svcpart = $('#<% $id.'_svcpart' %>');
+ var select_svcnum = $('#<% $id %>');
+ var onchange_svcpart = function() {
+ var l = labels[ this.value ];
+ var v = values[ this.value ];
+ select_svcnum.empty();
+ for (var i = 0; i < v.length; i++) {
+ var opt = $('<option />').val(v[i]).text(l[i]);
+ select_svcnum.append(opt);
+ }
+ };
+ select_svcpart.on('change', onchange_svcpart);
+ select_svcpart.change();
+ select_svcnum.val(<% encode_json($curr_value) %>);
+});
+</script>
diff --git a/httemplate/elements/select-terms.html b/httemplate/elements/select-terms.html
index a330df17c..eda439a4c 100644
--- a/httemplate/elements/select-terms.html
+++ b/httemplate/elements/select-terms.html
@@ -34,10 +34,7 @@ my $empty_label =
my $empty_value = $opt{'empty_value'} || '';
-my @terms = ( emt('Payable upon receipt'),
- ( map "Net $_",
- 0, 3, 5, 7, 9, 10, 14, 15, 18, 20, 21, 25, 30, 45, 60, 90 ),
- );
+my @terms = map emt($_), @FS::Conf::invoice_terms;
my @pre_options = $opt{pre_options} ? @{ $opt{pre_options} } : ();
diff --git a/httemplate/elements/select.html b/httemplate/elements/select.html
index 42cd89504..3a0dc5b68 100644
--- a/httemplate/elements/select.html
+++ b/httemplate/elements/select.html
@@ -21,6 +21,7 @@
disabled => 0,
onchange => 'do_something()',
js_only => 0, # disables the whole thing
+ element_etc => '', # anything else to put in the <select> tag
&>
</%doc>
@@ -35,6 +36,7 @@
<% $style %>
<% $opt{disabled} %>
<% $onchange %>
+ <% $opt{'element_etc'} %>
>
% if ( $opt{options} ) {
diff --git a/httemplate/elements/selectlayersx.html b/httemplate/elements/selectlayersx.html
new file mode 100644
index 000000000..41f3cb0b7
--- /dev/null
+++ b/httemplate/elements/selectlayersx.html
@@ -0,0 +1,248 @@
+<%doc>
+
+Example:
+
+ include( '/elements/selectlayers.html',
+ 'field' => $key, # SELECT element NAME (passed as form field)
+ # also used as ID and a unique key for layers and
+ # functions
+ 'curr_value' => $selected_layer,
+ 'options' => [ 'option1', 'option2' ],
+ 'labels' => { 'option1' => 'Option 1 Label',
+ 'option2' => 'Option 2 Label',
+ },
+
+ #XXX put this handling it its own selectlayers-fields.html element?
+ 'layer_prefix' => 'prefix_', #optional prefix for fieldnames
+ 'layer_fields' => { 'layer' => [ 'fieldname',
+ { label => 'fieldname2',
+ type => 'text', #implemented:
+ # text, money, fixed,
+ # hidden, checkbox,
+ # checkbox-multiple,
+ # select, select-agent,
+ # select-pkg_class,
+ # select-part_referral,
+ # select-taxclass,
+ # select-table,
+ #XXX tbd:
+ # more?
+ },
+ ...
+ ],
+ 'layer2' => [ 'l2fieldname',
+ ...
+ ],
+ },
+
+ #current values for layer fields above
+ 'layer_values' => { 'layer' => { 'fieldname' => 'current_value',
+ 'fieldname2' => 'field2value',
+ ...
+ },
+ 'layer2' => { 'l2fieldname' => 'l2value',
+ ...
+ },
+ ...
+ },
+
+ #or manual control, instead of layer_fields and layer_values above
+ #called with args: my( $layer, $layer_fields, $layer_values, $layer_prefix )
+ 'layer_callback' =>
+
+ 'html_between => '', #optional HTML displayed between the SELECT and the
+ #layers, scalar or coderef ('field' passed as a param)
+ 'onchange' => '', #javascript code run when the SELECT changes
+ # ("what" is the element)
+ 'js_only' => 0, #set true to return only the JS portions
+ 'html_only' => 0, #set true to return only the HTML portions
+ 'select_only' => 0, #set true to return only the <SELECT> HTML
+ 'layers_only' => 0, #set true to return only the layers <DIV> HTML
+ )
+
+</%doc>
+% unless ( grep $opt{$_}, qw(html_only js_only select_only layers_only) ) {
+<SCRIPT TYPE="text/javascript">
+% }
+% unless ( grep $opt{$_}, qw(html_only select_only layers_only) ) {
+
+% unless ($selectlayersx_init) {
+
+var selectlayerx_info = {};
+
+function selectlayersx_changed (field) {
+
+ var what = document.getElementById(field);
+ selectlayerx_info[field]['onchange'](what);
+
+ var selectedlayer = what.options[what.selectedIndex].value;
+ for (i=0; i < selectlayerx_info[field]['layers'].length; i++) {
+ var iterlayer = selectlayerx_info[field]['layers'][i];
+ var iterobj = document.getElementById(field+'d'+iterlayer);
+ if (selectedlayer == iterlayer) {
+ iterobj.style.display = "";
+ iterobj.style.zIndex = 1;
+ } else {
+ iterobj.style.display = "none";
+ iterobj.style.zIndex = 0;
+ }
+ }
+
+}
+
+% $selectlayersx_init = 1;
+% } #selectlayersx_init
+
+selectlayerx_info['<% $key %>'] = {};
+selectlayerx_info['<% $key %>']['onchange'] = function (what) { <% $opt{'onchange'} %> };
+selectlayerx_info['<% $key %>']['layers'] = <% encode_json(\@layers) %>;
+
+
+% } #unless html_only/select_only/layers_only
+% unless ( grep $opt{$_}, qw(html_only js_only select_only layers_only) ) {
+</SCRIPT>
+% }
+%
+% unless ( grep $opt{$_}, qw(js_only layers_only) ) {
+
+ <SELECT NAME = "<% $key %>"
+ ID = "<% $key %>"
+ previousValue = "<% $selected %>"
+ previousText = "<% $options{$selected} %>"
+ onChange="selectlayersx_changed('<% $key %>')"
+ >
+
+% foreach my $option ( keys %$options ) {
+
+ <OPTION VALUE="<% $option %>"
+ <% $option eq $selected ? ' SELECTED' : '' %>
+ ><% $options->{$option} |h %></OPTION>
+
+% }
+
+ </SELECT>
+
+% }
+% unless ( grep $opt{$_}, qw(js_only select_only layers_only) ) {
+
+<% ref($between) ? &{$between}($key) : $between %>
+
+% }
+%
+% unless ( grep $opt{$_}, qw(js_only select_only) ) {
+
+% foreach my $layer ( @layers ) {
+% my $selected_layer;
+% $selected_layer = $selected;
+
+ <DIV ID="<% $key %>d<% $layer %>"
+ STYLE="<% $selected_layer eq $layer
+ ? 'display: block; z-index: 1'
+ : 'display: none; z-index: 0'
+ %>"
+ >
+
+ <% &{$layer_callback}($layer, $layer_fields, $layer_values, $layer_prefix) %>
+
+ </DIV>
+
+% }
+
+% }
+<%once>
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+my $date_noinit = 0;
+
+</%once>
+<%shared>
+
+my $selectlayersx_init = 0;
+
+</%shared>
+<%init>
+
+my %opt = @_;
+
+#use Data::Dumper;
+#warn Dumper(%opt);
+
+my $key = $opt{field}; # || 'generate_one' #?
+
+tie my %options, 'Tie::IxHash',
+ map { $_ => $opt{'labels'}->{$_} }
+ @{ $opt{'options'} }; #just arrayref for now
+
+my $between = exists($opt{html_between}) ? $opt{html_between} : '';
+my $options = \%options;
+
+my @layers = ();
+@layers = keys %options;
+
+my $selected = exists($opt{curr_value}) ? $opt{curr_value} : '';
+
+#XXX eek. also eek $layer_fields in the layer_callback() call...
+my $layer_fields = $opt{layer_fields};
+my $layer_values = $opt{layer_values};
+my $layer_prefix = $opt{layer_prefix};
+
+my $layer_callback = $opt{layer_callback} || \&layer_callback;
+
+sub layer_callback {
+ my( $layer, $layer_fields, $layer_values, $layer_prefix ) = @_;
+
+ return '' unless $layer && exists $layer_fields->{$layer};
+ tie my %fields, 'Tie::IxHash', @{ $layer_fields->{$layer} };
+
+ #XXX this should become an element itself... (false laziness w/edit.html)
+ # but at least all the elements inside are the shared mason elements now
+
+ return '' unless keys %fields;
+ my $html = "<TABLE>";
+
+ foreach my $field ( keys %fields ) {
+
+ my $lf = ref($fields{$field})
+ ? $fields{$field}
+ : { 'label'=>$fields{$field} };
+
+ my $value = $layer_values->{$layer}{$field};
+
+ my $type = $lf->{type} || 'text';
+
+ my $include = $type;
+
+ if ( $include eq 'date' ) {
+ # several important differences from other tr-*
+ $html .= include( '/elements/tr-input-date-field.html',
+ {
+ 'name' => "$layer_prefix$field",
+ 'value' => $value,
+ 'label' => $lf->{label},
+ 'format'=> $lf->{format},
+ 'noinit'=> $date_noinit,
+ }
+ );
+ $date_noinit = 1;
+ }
+ else {
+ $include = "input-$include" if $include =~ /^(text|money|percentage)$/;
+ $include = "tr-$include" unless $include eq 'hidden';
+ $html .= include( "/elements/$include.html",
+ %$lf,
+ 'field' => "$layer_prefix$field",
+ 'id' => "$layer_prefix$field", #separate?
+ #don't want field0_label0...?
+ 'label_id' => $layer_prefix.$field."_label",
+
+ 'value' => ( $lf->{'value'} || $value ), #hmm.
+ 'curr_value' => $value,
+ );
+ }
+ } #foreach $field
+ $html .= '</TABLE>';
+ return $html;
+}
+
+</%init>
diff --git a/httemplate/elements/standardize_locations.js b/httemplate/elements/standardize_locations.js
index 0c4fb029a..15d687cf3 100644
--- a/httemplate/elements/standardize_locations.js
+++ b/httemplate/elements/standardize_locations.js
@@ -115,7 +115,7 @@ function confirm_standardize(arg) {
// then all entered address fields are correct
// but we still need to set the lat/long fields and addr_clean
- if ( returned['addr_clean'] ) {
+ if ( returned['all_clean'] ) {
status_message('Verified');
} else {
status_message('Unverified');
diff --git a/httemplate/elements/table-tickets.html b/httemplate/elements/table-tickets.html
index d722c9d2b..b322a5f7c 100644
--- a/httemplate/elements/table-tickets.html
+++ b/httemplate/elements/table-tickets.html
@@ -14,6 +14,7 @@ View
<THEAD>
<TR>
+ <TH CLASS="grid" BGCOLOR="#cccccc"></TH>
<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>
@@ -42,6 +43,16 @@ View
<TR>
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% if ( $ticket->{is_unreplied} ) {
+ <A CLASS="dot" STYLE="background-color: green" HREF=<%$href%>>
+% } else {
+% # placeholder
+ <A CLASS="dot" STYLE="visibility: hidden" HREF=<%$href%>>
+% }
+ </A>
+ </TD>
+
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
<A HREF=<%$href%>><% $ticket->{id} %></A>
</TD>
diff --git a/httemplate/elements/topreload.js b/httemplate/elements/topreload.js
new file mode 100644
index 000000000..a66703b29
--- /dev/null
+++ b/httemplate/elements/topreload.js
@@ -0,0 +1,5 @@
+window.topreload = function() {
+ if (window != window.top) {
+ window.top.location.reload();
+ }
+}
diff --git a/httemplate/elements/tower_sector.html b/httemplate/elements/tower_sector.html
index 151d3ba65..987177582 100644
--- a/httemplate/elements/tower_sector.html
+++ b/httemplate/elements/tower_sector.html
@@ -56,8 +56,11 @@ tie my %label, 'Tie::IxHash',
'height' => 'Height',
'freq_mhz' => 'Freq. (MHz)',
'direction' => 'Direction', # or a button to set these to 0 for omni
- 'width' => 'Width', #
+ 'downtilt' => 'Downtilt',
+ 'width' => 'Horiz. width',
+ 'v_width' => 'Vert. width',
'sector_range' => 'Range',
+ 'margin' => 'Signal margin (dB)',
;
my @fields = keys %label;
diff --git a/httemplate/elements/tr-cust_svc_cancel.html b/httemplate/elements/tr-cust_svc_cancel.html
index 44276ec82..52dedd6c8 100644
--- a/httemplate/elements/tr-cust_svc_cancel.html
+++ b/httemplate/elements/tr-cust_svc_cancel.html
@@ -18,7 +18,14 @@ for use in view/cust_main.
% }
</B></TD>
</TR>
-%# no action links, the service is canceled
+%# no action links except unprovision, the service is canceled
+% if ( $curuser->access_right('Unprovision customer service') && ! $opt{no_links} ) {
+<TR>
+ <TD COLSPAN="2" ALIGN="right" VALIGN="top" STYLE="padding-bottom:5px; padding-top:0px">
+ <FONT SIZE="-2">(&nbsp;<% $svc_unprovision_link %>&nbsp;)</FONT>
+ </TD>
+</TR>
+% }
<%init>
my %opt = @_;
@@ -29,4 +36,9 @@ my $part_svc = $opt{'part_svc'} || $cust_svc->part_svc;
my $cust_pkg = $opt{'cust_pkg'} || $cust_svc->cust_pkg;
my $svc_x = $cust_svc->svc_x;
+my $svc_unprovision_link =
+ qq!<A HREF="javascript:areyousure('${p}misc/unprovision.cgi?! .
+ $cust_svc->svcnum .
+ qq!', '!.emt('Permanently unprovision and delete this service?').qq!')">!.emt('Unprovision').'</A>';
+
</%init>
diff --git a/httemplate/elements/tr-freq.html b/httemplate/elements/tr-freq.html
index cb58bf6b5..795684cf7 100644
--- a/httemplate/elements/tr-freq.html
+++ b/httemplate/elements/tr-freq.html
@@ -15,7 +15,7 @@
<% $freq eq $units ? 'SELECTED' : '' %>
><% $freq{$freq} %>
% }
- </SELECT>
+ </SELECT><% $opt{'post_text'} || '' %>
</TD>
diff --git a/httemplate/elements/tr-input-locale-text.html b/httemplate/elements/tr-input-locale-text.html
new file mode 100644
index 000000000..110a8aa9b
--- /dev/null
+++ b/httemplate/elements/tr-input-locale-text.html
@@ -0,0 +1,120 @@
+<%doc>
+Usage:
+
+In edit/foo.html:
+
+<& /elements/tr-input-locale-text.html,
+ cgi => $cgi, # needed to preserve values in error redirect
+ object => $record,
+ field => 'myfield',
+ label => 'My Field',
+&>
+
+And in edit/process/foo.html:
+<& elements/process.html,
+ ...
+ process_locale => 'myfield',
+&>
+
+'object' needs to be an FS::Record subclass instance for a table that has
+a '_msgcat' localization table. For a table "foo" where "foo.myfield"
+contains some customer-visible label (in the default locale),
+"foo_msgcat.myfield" contains the translation of that label for a customer
+locale. The foreign key in foo_msgcat must have the same name as the primary
+key of foo.
+
+Currently only a single field can be localized this way; including this
+element more than once in the form will lead to conflicts. This is how
+it should work; if at some point we need to localize several fields of the
+same record, we should modify this element to show multiple inputs for each
+locale.
+
+</%doc>
+<%init>
+
+my %opt = @_;
+my $object = delete $opt{object};
+my $field = delete $opt{field};
+
+# identify our locales
+my $conf = FS::Conf->new;
+my $default_locale = $conf->config('locale') || 'en_';
+my @locales = grep { ! /^$default_locale/ } $conf->config('available-locales');
+
+my $label = delete $opt{label};
+my %labels = map { $_ => "$label&mdash;".FS::Locales->description($_) }
+ @locales;
+@locales = sort { $labels{$a} cmp $labels{$b} } @locales;
+my %curr_values;
+
+# where are the msgcat records?
+my $msgcat_table = $object->table . '_msgcat';
+my $msgcat_pkey = dbdef->table($msgcat_table)->primary_key;
+my %msgcat_pkeyvals;
+
+# find existing msgcat records, if any, and record their message values
+# and pkeys
+my $pkey = $object->primary_key;
+my $pkeyval = $object->get($pkey);
+if ($pkeyval) { # of course if this is a new record there won't be any
+ my @linked = qsearch($msgcat_table, { $pkey => $pkeyval });
+ foreach (@linked) {
+ $curr_values{ $_->locale } = $_->get( $field );
+ $msgcat_pkeyvals{ $_->locale } = $_->get( $msgcat_pkey );
+ }
+}
+
+# sticky-on-error the locale inputs
+if( my $cgi = $opt{cgi} ) {
+ my $i = 0;
+ # they're named 'foomsgnum0_locale' and 'foomsgnum0_myfield'
+ while ( my $locale = $cgi->param($msgcat_pkey . $i . '_locale') ) {
+ my $value = $cgi->param($msgcat_pkey . $i . '_' . $field);
+ $curr_values{ $locale } = $value;
+ $i++;
+ }
+}
+
+# compat with tr-input-text for styling
+my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+my $colspan = $opt{'colspan'} ? 'COLSPAN="'.$opt{'colspan'}.'"' : '';
+
+
+</%init>
+% # pass through %opt on all of these to retain formatting
+% # one tr, td, and input for the default locale
+<& tr-input-text.html,
+ %opt,
+ 'label' => $label,
+ 'field' => $field
+&>
+% # and one for each of the others
+% my $i = 0;
+% foreach my $locale (@locales) {
+% my $basename = $msgcat_pkey . $i;
+% my $lfield = $basename . '_' . $field;
+<& tr-td-label.html,
+ %opt,
+ 'id' => $lfield, # uniqueness
+ 'label' => $labels{$locale}
+&>
+ <TD <% $colspan %><% $cell_style %> ID="<% $lfield %>_input0">
+ <& hidden.html,
+ 'field' => $basename,
+ 'curr_value' => $msgcat_pkeyvals{$locale},
+ # will be empty if this is a new record and/or new locale, that's fine
+ &>
+ <& hidden.html,
+ 'field' => $basename . '_locale',
+ 'curr_value' => $locale,
+ &>
+ <& input-text.html,
+ %opt,
+ 'field' => $lfield,
+ 'curr_value' => $curr_values{$locale},
+ &>
+ </TD>
+</TR>
+% $i++;
+% } # foreach $locale
diff --git a/httemplate/elements/tr-select-cust-part_pkg.html b/httemplate/elements/tr-select-cust-part_pkg.html
index 6244b6cb7..f4af405fc 100644
--- a/httemplate/elements/tr-select-cust-part_pkg.html
+++ b/httemplate/elements/tr-select-cust-part_pkg.html
@@ -86,7 +86,7 @@
</TR>
% } else { # so that the rest of the page works correctly
- <INPUT TYPE="hidden" ID="classnum" NAME="classnum" VALUE="-1`">
+ <INPUT TYPE="hidden" ID="classnum" NAME="classnum" VALUE="-1">
% }
<TR>
diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html
index 97466f175..9a430222c 100755
--- a/httemplate/elements/tr-select-reason.html
+++ b/httemplate/elements/tr-select-reason.html
@@ -188,9 +188,8 @@ my $class = $opt{'reason_class'};
my $init_reason;
if ( $opt{'cgi'} ) {
$init_reason = $opt{'cgi'}->param($name);
-} else {
- $init_reason = $opt{'curr_value'};
}
+$init_reason ||= $opt{'curr_value'};
my $id = $opt{'id'} || $name;
$id =~ s/\./_/g; # for edit/part_event
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
index ee135686c..2aa715e29 100644
--- a/httemplate/elements/tr-select-router_block_ip.html
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -56,7 +56,7 @@ function clearhint_ip_addr (what) {
]
&>
</td></tr>
-<& /elements/tr-td-label.html, label => 'IP address', required => $opt{'ip_addr_required'} &>
+<& /elements/tr-td-label.html, label => ($opt{'ip_addr_label'} || 'IP address'), required => $opt{'ip_addr_required'} &>
<td>
% #warn Dumper \%fixed;
% if ( exists $fixed{$ip_field} ) {
diff --git a/httemplate/elements/tr-select-rt-queue.html b/httemplate/elements/tr-select-rt-queue.html
new file mode 100644
index 000000000..ac3689b1c
--- /dev/null
+++ b/httemplate/elements/tr-select-rt-queue.html
@@ -0,0 +1,7 @@
+
+<& 'tr-td-label.html', @_ &>
+<TD>
+<& 'select-rt-queue.html', @_ &>
+</TD>
+</TR>
+
diff --git a/httemplate/elements/tr-selectlayersx.html b/httemplate/elements/tr-selectlayersx.html
new file mode 100644
index 000000000..ca7a36079
--- /dev/null
+++ b/httemplate/elements/tr-selectlayersx.html
@@ -0,0 +1,25 @@
+% unless ( $opt{js_only} ) {
+
+ <% include('tr-td-label.html', @_ ) %>
+
+ <TD <% $style %>>
+
+% }
+
+ <% include('selectlayersx.html', @_ ) %>
+
+% unless ( $opt{js_only} ) {
+
+ </TD>
+
+ </TR>
+
+% }
+
+<%init>
+
+my %opt = @_;
+
+my $style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+</%init>
diff --git a/httemplate/elements/xmlhttp.html b/httemplate/elements/xmlhttp.html
index 2f4f0d555..e70871169 100644
--- a/httemplate/elements/xmlhttp.html
+++ b/httemplate/elements/xmlhttp.html
@@ -44,7 +44,7 @@ my %initialized = ();#won't work if component is "preloaded"... so don't do that
len = args.length - 1;
}
for (var i = 0; i < len; i++)
- content = content + "&arg=" + escape(args[i]);
+ content = content + "&arg=" + encodeURIComponent(args[i]);
content = content.replace( /[+]/g, '%2B'); // fix unescaped plus signs
if ( '<%$method%>' == 'GET' ) {
diff --git a/httemplate/misc/areacodes.cgi b/httemplate/misc/areacodes.cgi
index 4b31deb00..afbe93e91 100644
--- a/httemplate/misc/areacodes.cgi
+++ b/httemplate/misc/areacodes.cgi
@@ -1,4 +1,4 @@
-<% encode_json(\@areacodes) %>\
+<% encode_json({ error => $error, areacodes => \@areacodes}) %>\
<%init>
my( $state, $svcpart ) = $cgi->param('arg');
@@ -7,6 +7,8 @@ my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
die "unknown svcpart $svcpart" unless $part_svc;
my @areacodes = ();
+my $error;
+
if ( $state ) {
my @exports = $part_svc->part_export_did;
@@ -17,9 +19,12 @@ if ( $state ) {
}
my $export = $exports[0];
- my $something = $export->get_dids('state'=>$state);
+ local $@;
+ local $SIG{__DIE__};
+ my $something = eval { $export->get_dids('state'=>$state) };
+ $error = $@;
- @areacodes = @{ $something };
+ @areacodes = @{ $something } if $something;
}
diff --git a/httemplate/misc/cancel_pkg.html b/httemplate/misc/cancel_pkg.html
index c80b2b278..d3ca9964f 100755
--- a/httemplate/misc/cancel_pkg.html
+++ b/httemplate/misc/cancel_pkg.html
@@ -45,12 +45,41 @@
} &>
<& /elements/tr-checkbox.html,
- 'label' => mt("Uncancel even if a service can't be re-provisioned"),
+ 'label' => mt("Uncancel even if service reprovisioning fails"),
'field' => 'svc_not_fatal',
'value' => 'Y',
&>
% $date_init = 1;
+% my @uncancel_svcs = sort { $b->{'reprovisionable'} <=> $a->{'reprovisionable'} }
+% sort { $a->{'svcpart'} <=> $b->{'svcpart'} }
+% $cust_pkg->uncancel_svc_summary();
+% if (@uncancel_svcs) {
+<TR><TD COLSPAN="2">&nbsp;</TD></TR>
+<TR><TH ALIGN="right"><% emt("Re-provision the following services") %></TH><TD></TD>
+% foreach my $uncancel_svc (@uncancel_svcs) {
+% my $uncancel_curr_value = $uncancel_svc->{'uncancel_svcnum'};
+% my $uncancel_disabled = '';
+% my $uncancel_postfix = '';
+% if ($cgi->param('error')) {
+% $uncancel_curr_value = '' unless grep { $_ == $uncancel_svc->{'uncancel_svcnum'} } $cgi->param('only_svcnum');
+% }
+% unless ($uncancel_svc->{'reprovisionable'}) {
+% $uncancel_curr_value = '';
+% $uncancel_disabled = 1;
+% $uncancel_postfix = '<I>(' . emt('Cannot be reprovisioned') . ')</I>';
+% }
+ <& /elements/tr-checkbox.html,
+ 'label' => $uncancel_svc->{'svc'} . ': ' . $uncancel_svc->{'label'},
+ 'field' => 'only_svcnum',
+ 'value' => $uncancel_svc->{'uncancel_svcnum'},
+ 'curr_value' => $uncancel_curr_value,
+ 'disabled' => $uncancel_disabled,
+ 'cell_style' => 'font-weight: normal',
+ 'postfix' => $uncancel_postfix,
+ &>
+% }
+% }
% }
% unless ( $method eq 'resume' || $method eq 'uncancel' ) {
@@ -62,7 +91,7 @@
&>
% }
-% if ( $method eq 'adjourn' || $method eq 'suspend' ) {
+% if (( $method eq 'adjourn' || $method eq 'suspend' ) && $curuser->access_right('Customize billing during suspension')) {
<TR><TD COLSPAN=2>
% if ( $part_pkg->option('suspend_bill', 1) ) {
<& /elements/checkbox.html, name=>'no_suspend_bill', value=>'Y' &>
diff --git a/httemplate/misc/change_pkg_start.html b/httemplate/misc/change_pkg_date.html
index 5a890c86e..642a5b89e 100755
--- a/httemplate/misc/change_pkg_start.html
+++ b/httemplate/misc/change_pkg_date.html
@@ -3,11 +3,12 @@
<& /elements/error.html &>
% # only slightly different from unhold_pkg.
-<FORM NAME="MyForm" ACTION="process/change_pkg_start.html" METHOD=POST>
+<FORM NAME="MyForm" ACTION="process/change_pkg_date.html" METHOD=POST>
<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
+<INPUT TYPE="hidden" NAME="field" VALUE="<% $field %>">
<BR>
-<% emt('Start billing [_1]', $part_pkg->pkg_comment(cust_pkg => $cust_pkg)) %>
+<% emt(($isstart ? 'Start billing' : 'Set contract end for').' [_1]', $part_pkg->pkg_comment(cust_pkg => $cust_pkg)) %>
<UL STYLE="padding-left: 3ex; list-style: none; background-color: #cccccc">
<LI>
<& /elements/radio.html,
@@ -16,7 +17,7 @@
value => 'now',
curr_value => $when,
&>
- <label for="when_now"><% emt('Immediately') %></label>
+ <label for="when_now"><% emt($isstart ? 'Now' : 'Never') %></label>
</LI>
% if ( $next_bill_date ) {
<LI>
@@ -41,13 +42,13 @@
&>
<label for="when_date"> <% emt('On this date:') %> </label>
<& /elements/input-date-field.html,
- { name => 'start_date',
- value => $cgi->param('start_date') || $cust_pkg->start_date,
+ { name => 'date_value',
+ value => $cgi->param('date_value') || $cust_pkg->get($field),
}
&>
</LI>
</UL>
-<INPUT TYPE="submit" NAME="submit" VALUE="<% emt('Set start date') %>">
+<INPUT TYPE="submit" NAME="submit" VALUE="<% emt('Set '.($isstart ? 'start date' : 'contract end')) %>">
</FORM>
</BODY>
@@ -55,9 +56,21 @@
<%init>
+my $field = $cgi->param('field');
+
+my ($acl, $isstart);
+if ($field eq 'start_date') {
+ $acl = 'Change package start date';
+ $isstart = 1;
+} elsif ($field eq 'contract_end') {
+ $acl = 'Change package contract end date';
+} else {
+ die "Unknown date field";
+}
+
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
- unless $curuser->access_right('Change package start date');
+ unless $curuser->access_right($acl);
my $pkgnum;
if ( $cgi->param('pkgnum') =~ /^(\d+)$/ ) {
@@ -69,7 +82,7 @@ if ( $cgi->param('pkgnum') =~ /^(\d+)$/ ) {
my $conf = new FS::Conf;
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
-my $title = 'Start billing package';
+my $title = $isstart ? 'Start billing package' : 'Change contract end';
my $cust_pkg = qsearchs({
table => 'cust_pkg',
@@ -83,12 +96,12 @@ my $next_bill_date = $cust_pkg->cust_main->next_bill_date;
my $part_pkg = $cust_pkg->part_pkg;
# defaults:
-# sticky on error, then the existing start date if any, then the customer's
+# sticky on error, then the existing date if any, then the customer's
# next bill date, and if none of those, default to now
my $when = $cgi->param('when');
if (!$when) {
- if ($cust_pkg->start_date) {
+ if ($cust_pkg->get($field)) {
$when = 'date';
} elsif ($next_bill_date) {
$when = 'next_bill_date';
diff --git a/httemplate/misc/confirm-address_standardize.html b/httemplate/misc/confirm-address_standardize.html
index 9d1a5c135..8bd43ca54 100644
--- a/httemplate/misc/confirm-address_standardize.html
+++ b/httemplate/misc/confirm-address_standardize.html
@@ -7,6 +7,9 @@ th { line-height: 150%;
vertical-align: middle;
text-align: center;
}
+button {
+ width: 215px;
+}
</STYLE>
<CENTER><BR><B>
% if ( $is_error ) {
@@ -23,9 +26,13 @@ Confirm address standardization
% my $name = $pre eq 'bill_' ? 'billing' : 'service';
% my $rows = 5;
% if ( $new{$pre.'error'} ) {
+% # Standardization returned an error, so the user can either "continue
+% # without replacing" (replace = "") or "abort".
<TR>
<TD ROWSPAN=<% $rows %> CLASS="td_radio">
+% if ( $show_radio ) {
<INPUT TYPE="radio" NAME="<% $pre %>replace" VALUE="" CHECKED="Y">
+% } # else confirm_manual_address will be called
</TD>
<TH>Entered <%$name%> address
</TH>
@@ -51,15 +58,19 @@ Confirm address standardization
% $rows++ if !$new{$pre.'addr_clean'};
<TR>
<TD ROWSPAN=<% $rows %> CLASS="td_radio">
+% if ( $show_radio ) {
<INPUT TYPE="radio" NAME="<% $pre %>replace" VALUE="">
+% }
</TD>
<TH>Entered <%$name%> address</TH>
<TH>Standardized <%$name%> address</TH>
<TD ROWSPAN=<% $rows %> CLASS="td_radio">
+% if ( $show_radio ) {
<INPUT TYPE="radio" NAME="<% $pre %>replace" VALUE="Y" CHECKED="Y">
+% }
</TD>
</TR>
-% if ( !$new{$pre.'addr_clean'} ) {
+% if ( !$new{$pre.'addr_clean'} ) { # we incremented $rows to fit this in
<TR>
<TD></TD>
<TH STYLE="font-size:smaller;color:#ff0000">(unverified)</TH>
@@ -88,18 +99,24 @@ Confirm address standardization
% } # if error
% } # for $pre
-%# only do this part if address standardization provided a censustract
-% my $pre = $old{same} ? 'bill_' : 'ship_';
+% # the prefix for the censustract
+% my $pre = $old{billship} ?
+% ( $old{same} ? 'bill_' : 'ship_' ) :
+% '';
% my $censustract = $new{$pre.'censustract'};
% if ( $censustract ) {
<TR>
<TD ROWSPAN=2 CLASS="td_radio">
+% if ( $show_radio ) {
<INPUT TYPE="radio" NAME="census_replace" VALUE="" <% $census_error ? 'CHECKED="Y"' : '' %>>
+% }
</TD>
<TH>Entered census tract</TH>
<TH>Calculated census tract</TH>
<TD ROWSPAN=2 CLASS="td_radio">
+% if ( $show_radio ) {
<INPUT TYPE="radio" NAME="census_replace" VALUE="Y" <% $census_error ? '' : 'CHECKED="Y"' %>>
+% }
</TD>
</TR>
<TR>
@@ -115,17 +132,40 @@ Confirm address standardization
% } #if censustract
<TR>
+% if ( $show_radio ) {
+% # One button: "use selected address(es)".
+ <TD ALIGN="center" COLSPAN=4>
+ <BUTTON TYPE="button" onclick="replace_address();">
+ <IMG SRC="<%$p%>images/<% $is_error ? 'error.png' : 'tick.png' %>"
+ ALT="">
+ Use selected <%$addresses%>
+ </BUTTON>
+ </TD>
+% } else {
+% # Two buttons: "use entered address", and "use selected address"
+% # and empty columns before and after
<TD> </TD>
<TD ALIGN="center">
- <BUTTON TYPE="button" STYLE="width:205px" onclick="replace_address();">
- <IMG SRC="<%$p%>images/<% $is_error ? 'error.png' : 'tick.png' %>"
- ALT=""> Use selected <%$addresses%>
- </BUTTON></TD>
+ <BUTTON TYPE="button" onclick="confirm_manual_address();">
+ <IMG SRC="<%$p%>images/error.png" ALT=""> Use entered <%$addresses%>
+ </BUTTON>
+ </TD>
+% # disable the button if standardization completely failed
<TD ALIGN="center">
- <BUTTON TYPE="button" STYLE="width:205px" onclick="submit_abort();">
- <IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission
- </BUTTON></TD>
+ <BUTTON TYPE="button" onclick="replace_address();" <% $failed ? 'DISABLED' : '' %>>
+ <IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized <%$addresses%>
+ </BUTTON>
+ </TD>
<TD> </TD>
+% }
+ </TR>
+% # always provide a cancel button
+ <TR>
+ <TD ALIGN="center" COLSPAN=4>
+ <BUTTON TYPE="button" onclick="submit_abort();">
+ <IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission
+ </BUTTON>
+ </TD>
</TR>
</TABLE>
</FORM>
@@ -138,15 +178,43 @@ my %old = %{ $q->{old} };
my %new = %{ $q->{new} };
my $addresses = $old{billship} ? 'addresses' : 'address';
+my $show_radio = 0;
+my $failed = 0; # true if standardization returned no addresses
my @prefixes = ('');
+
if ( $old{same} ) {
+
+ # Then there are bill and ship addresses and they're the same. Treat
+ # bill_error as a failure, and let the user accept or reject the whole
+ # standardization.
+
@prefixes = ('bill_');
+ $failed = 1 if $new{'bill_error'};
+
} elsif ( $old{billship} ) {
+
+ # There are separate bill and ship addresses. Treat error in both as a
+ # failure. Otherwise, at least one of them has a choice between entered
+ # and standardized address, so let the user choose.
+
@prefixes = ('bill_', 'ship_');
+ if ( $new{'bill_error'} and $new{'ship_error'} ) {
+ $failed = 1;
+ } else {
+ $show_radio = 1;
+ }
+
+} else {
+
+ # There are no bill/ship addresses (this is used for package locations).
+ # Treat like the first case but without the bill_ prefix.
+ @prefixes = ('');
+ $failed = 1 if $new{'error'};
+
}
-my $census_error = $new{'census_error'};
+my $census_error = $new{'census_error'}; # seems to be unused
my $is_error = $census_error || grep { $new{$_.'error'} } @prefixes;
</%init>
diff --git a/httemplate/misc/cust_credit-import.html b/httemplate/misc/cust_credit-import.html
index 937010dd6..a124c9591 100644
--- a/httemplate/misc/cust_credit-import.html
+++ b/httemplate/misc/cust_credit-import.html
@@ -62,15 +62,16 @@ Import a file containing credits.
Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets. The file should have a .CSV or .XLS extension.
<BR><BR>
- <b>Default</b> format has the following field order: <i>custnum, amount, reasonnum, invnum</i><br>
+ <b>Default</b> format has the following field order: <i>custnum, amount, reasonnum, invnum, agent_custid</i><br>
<BR><BR>
Field information:
<ul>
- <li><i>custnum</i>: Customer number
- <li><i>amount</i>:
- <li><i>reasonnum</i>: <A HREF="<%$p%>browse/reason_type.html?class=R">Credit reason</A>
- <li><i>invnum</i>: Invoice number
+ <li><i>custnum</i>: This is the freeside customer number. It may be left blank. If specified, agent_custid must be blank.</li>
+ <li><i>amount</i>:</li>
+ <li><i>reasonnum</i>: <A HREF="<%$p%>browse/reason_type.html?class=R">Credit reason</A></li>
+ <li><i>invnum</i>: Invoice number</li>
+ <li><i>agent_custid</i>: This is the reseller's idea of the customer number or identifier. It may be left blank. If specified, custnum must be blank.</li>
</ul>
<BR><BR>
diff --git a/httemplate/misc/cust_main-cancel.cgi b/httemplate/misc/cust_main-cancel.cgi
index f6fd1e915..73c4deb7a 100755
--- a/httemplate/misc/cust_main-cancel.cgi
+++ b/httemplate/misc/cust_main-cancel.cgi
@@ -1,6 +1,6 @@
<& /elements/header-popup.html, mt("Customer cancelled") &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/cust_main-suspend.cgi b/httemplate/misc/cust_main-suspend.cgi
index 7a501d61a..e81e2b490 100755
--- a/httemplate/misc/cust_main-suspend.cgi
+++ b/httemplate/misc/cust_main-suspend.cgi
@@ -1,6 +1,6 @@
<& /elements/header-popup.html, mt("Customer suspended") &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/cust_main-unsuspend.cgi b/httemplate/misc/cust_main-unsuspend.cgi
index e8ac8d31e..99ec70a23 100755
--- a/httemplate/misc/cust_main-unsuspend.cgi
+++ b/httemplate/misc/cust_main-unsuspend.cgi
@@ -1,6 +1,6 @@
<& /elements/header-popup.html, mt("Customer unsuspended") &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/delete-addr_range.html b/httemplate/misc/delete-addr_range.html
index c6310e9b1..239332d23 100644
--- a/httemplate/misc/delete-addr_range.html
+++ b/httemplate/misc/delete-addr_range.html
@@ -3,7 +3,7 @@
% } else {
<& /elements/header-popup.html, "Address range deleted" &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/delete-rate_detail.html b/httemplate/misc/delete-rate_detail.html
index 30856a73a..b4d31b379 100755
--- a/httemplate/misc/delete-rate_detail.html
+++ b/httemplate/misc/delete-rate_detail.html
@@ -3,7 +3,7 @@
% } else {
<% header('Rate deleted') %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
diff --git a/httemplate/misc/did_order_confirmed.html b/httemplate/misc/did_order_confirmed.html
index 53dbb2f3a..3cc121c6f 100644
--- a/httemplate/misc/did_order_confirmed.html
+++ b/httemplate/misc/did_order_confirmed.html
@@ -14,7 +14,7 @@ die "access denied"
my $action = $1;
my $header = '';
my $popup = '';
-my $js = 'window.top.location.reload();';
+my $js = 'topreload();';
$cgi->param('ordernum') =~ /^(\d+)$/ or die 'illegal ordernum';
my $ordernum = $1;
diff --git a/httemplate/misc/disable-cust_location.cgi b/httemplate/misc/disable-cust_location.cgi
index ee7ba1dbc..677f0b891 100755
--- a/httemplate/misc/disable-cust_location.cgi
+++ b/httemplate/misc/disable-cust_location.cgi
@@ -1,6 +1,6 @@
<% header("Location disabled") %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/disable-msg_template.cgi b/httemplate/misc/disable-msg_template.cgi
index 1eb4d25e5..565eb2cee 100644
--- a/httemplate/misc/disable-msg_template.cgi
+++ b/httemplate/misc/disable-msg_template.cgi
@@ -3,7 +3,7 @@
% } else {
<& /elements/header-popup.html, "Template ${actioned}" &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html
index 8e2863455..d086c676d 100644
--- a/httemplate/misc/email-customers.html
+++ b/httemplate/misc/email-customers.html
@@ -7,8 +7,8 @@ selecting an existing msg_template, or creating a custom message, and shows a
preview of the message before sending. If linked to as a popup, include the
cgi parameter 'popup' for proper header handling.
-This may also be used as an element in other pages, enabling you to provide an
-alternate initial form while using this for search freezing/thawing and
+This may also be used as an element in other pages, enabling you to provide
+an alternate initial form while using this for search freezing/thawing and
preview/send actions, with the following options:
acl - the access right to use (defaults to 'Bulk send customer notices')
@@ -48,6 +48,7 @@ from/subject/body cgi params
<INPUT TYPE="hidden" NAME="search" VALUE="<% encode_base64(nfreeze(\%search)) %>">
<INPUT TYPE="hidden" NAME="popup" VALUE="<% $popup %>">
<INPUT TYPE="hidden" NAME="url" VALUE="<% $url | h %>">
+<INPUT TYPE="hidden" NAME="to_contact_classnum" VALUE="<% join(',', @contact_classnum) %>">
% if ( $cgi->param('preview') ) {
% # preview mode: at this point we have a msg_template (either "real" or
@@ -57,7 +58,7 @@ from/subject/body cgi params
<FONT SIZE="+2">Preview notice</FONT>
<& /elements/progress-init.html,
'OneTrueForm',
- [ qw( search table msgnum ) ],
+ [ qw( search table msgnum to_contact_classnum ) ],
$process_url,
$pdest,
&>
@@ -79,6 +80,10 @@ from/subject/body cgi params
<td><% $from |h %></td>
</tr>
+ <& /elements/tr-td-label.html, 'label' => 'To contacts:' &>
+ <td><% join('<BR>', @contact_classname) %></td>
+ </tr>
+
<& /elements/tr-td-label.html, 'label' => 'Subject:' &>
<td><% $subject |h %></td>
</tr>
@@ -158,6 +163,20 @@ Template:
&>
<BR>
% }
+% # select destination contact classes
+Send to contacts:
+ <& /elements/checkboxes.html,
+ 'style' => 'display: inline; vertical-align: top',
+ 'disable_links' => 1,
+ 'names_list' => \@contact_checkboxes,
+ 'element_name_prefix' => 'contact_class_',
+ 'checked_callback' => sub {
+ my($cgi, $name) = @_;
+ $name eq 'invoice' #others default to unchecked
+ },
+ &>
+<BR>
+% # if sending a one-off message, show a form to edit it
<TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%" id="table_no_template">
<& /elements/tr-td-label.html, 'label' => 'From:' &>
<TD><& /elements/input-text.html,
@@ -262,6 +281,9 @@ if ( $cgi->param('msgnum') =~ /^(\d+)$/ ) {
or die "template not found: ".$cgi->param('msgnum');
}
+my @contact_classnum;
+my @contact_classname;
+
my $subject = $cgi->param('subject');
my $body = $cgi->param('body');
my ($html_body, $text_body);
@@ -337,6 +359,28 @@ if ( !$cgi->param('preview') ) {
$subject = $1;
}
}
+
+ # contact_class_X params
+ foreach my $param ( $cgi->multi_param ) {
+ if ( $param =~ /^contact_class_(\w+)$/ ) {
+ push @contact_classnum, $1;
+ if ( $1 eq 'invoice' ) {
+ push @contact_classname, 'Invoice recipients';
+ } else {
+ my $contact_class = FS::contact_class->by_key($1);
+ push @contact_classname, encode_entities($contact_class->classname);
+ }
+ }
+ }
}
+my @contact_checkboxes = (
+ [ 'invoice' => { label => 'Invoice recipients' } ]
+);
+foreach my $class (qsearch('contact_class', { disabled => '' })) {
+ push @contact_checkboxes, [
+ $class->classnum,
+ { label => $class->classname }
+ ];
+}
</%init>
diff --git a/httemplate/misc/exchanges.cgi b/httemplate/misc/exchanges.cgi
index 0de4ace25..d62679191 100644
--- a/httemplate/misc/exchanges.cgi
+++ b/httemplate/misc/exchanges.cgi
@@ -1,4 +1,4 @@
-<% encode_json(\@exchanges) %>\
+<% encode_json({ error => $error, exchanges => \@exchanges}) %>\
<%init>
my( $areacode, $svcpart ) = $cgi->param('arg');
@@ -7,6 +7,8 @@ my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
die "unknown svcpart $svcpart" unless $part_svc;
my @exchanges = ();
+my $error;
+
if ( $areacode ) {
my @exports = $part_svc->part_export_did;
@@ -17,9 +19,12 @@ if ( $areacode ) {
}
my $export = $exports[0];
- my $something = $export->get_dids('areacode'=>$areacode);
+ local $@;
+ local $SIG{__DIE__};
+ my $something = eval { $export->get_dids('areacode'=>$areacode) };
+ $error = $@;
- @exchanges = @{ $something };
+ @exchanges = @{ $something } if $something;
}
diff --git a/httemplate/misc/make_appointment.html b/httemplate/misc/make_appointment.html
index 6f308e0a8..79c3c2c89 100644
--- a/httemplate/misc/make_appointment.html
+++ b/httemplate/misc/make_appointment.html
@@ -6,13 +6,10 @@
<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $cgi->param('custnum') |h %>">
-% my @sched_item = qsearch('sched_item', { 'disabled' => '', });
-% my @username = map $_->access_user->username, @sched_item;
-% foreach my $username (@username) {
- <INPUT TYPE="hidden" NAME="username" VALUE="<% $username |h %>">
-% }
-
-Length:
+<TABLE>
+<TR>
+<TD STYLE="text-align: right">Length:</TD>
+<TD>
<SELECT NAME="LengthMin">
% for ( my $hours = .5; $hours < 10.5; $hours += .5 ) {
% my $min = $hours * 60;
@@ -21,9 +18,26 @@ Length:
><% $hours %> hour<% $hours > 1 ? 's' : '' %>
% }
</SELECT>
-<BR>
-<BR>
+</TD>
+</TR>
+
+% my @sched_item = qsearch('sched_item', { 'disabled' => '', });
+% my @username = map $_->access_user->username, @sched_item;
+
+<TR>
+<TD STYLE="text-align: right">Installer:</TD>
+<TD>
+<SELECT NAME="username" ID="username_select" MULTIPLE>
+% foreach my $username (@username) {
+ <OPTION SELECTED><% $username |h %></OPTION>
+% }
+</SELECT>
+</TD>
+</TR>
+</TABLE>
+
+<BR>
<INPUT TYPE="submit" VALUE="Schedule appointment">
</FORM>
diff --git a/httemplate/misc/phonenums.cgi b/httemplate/misc/phonenums.cgi
index 62923ac62..aae04f5d1 100644
--- a/httemplate/misc/phonenums.cgi
+++ b/httemplate/misc/phonenums.cgi
@@ -1,4 +1,4 @@
-<% encode_json(\@phonenums) %>\
+<% encode_json({ error => $error, phonenums => \@phonenums}) %>\
<%init>
my( $exchangestring, $svcpart ) = $cgi->param('arg');
@@ -7,6 +7,7 @@ my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
die "unknown svcpart $svcpart" unless $part_svc;
my @phonenums = ();
+my $error;
if ( $exchangestring ) {
@@ -35,8 +36,12 @@ if ( $exchangestring ) {
$opts{'exchange'} = $exchange;
}
- my $something = $export->get_dids(%opts);
- @phonenums = @{ $something };
+ local $@;
+ local $SIG{__DIE__};
+ my $something = eval { $export->get_dids(%opts) };
+ $error = $@;
+
+ @phonenums = @{ $something } if $something;
}
diff --git a/httemplate/misc/post_fsinc-invoice.cgi b/httemplate/misc/post_fsinc-invoice.cgi
new file mode 100644
index 000000000..94eaf667e
--- /dev/null
+++ b/httemplate/misc/post_fsinc-invoice.cgi
@@ -0,0 +1,43 @@
+% my $title = $error ? 'Error printing and mailing invoice' : 'Invoice printed and mailed';
+<% include('/elements/header-popup.html', $title ) %>
+<DIV STYLE="text-align: center;">
+<SPAN STYLE="color: red; font-weight: bold;"><% $error %></SPAN><BR>
+<BUTTON TYPE="button" onClick="parent.cClick();">Close</BUTTON>
+</DIV>
+<% include('/elements/footer-popup.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Print and mail invoices');
+
+my $invnum = $cgi->param('invnum');
+
+my $template = $cgi->param('template');
+my $notice_name = $cgi->param('notice_name') if $cgi->param('notice_name');
+my $no_coupon = $cgi->param('no_coupon');
+
+#XXX agent-virt
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum})
+ or die "Unknown invnum\n";
+
+my $mode;
+if ( $cgi->param('mode') =~ /^(\d+)$/ ) {
+ $mode = $1;
+}
+$cust_bill->set('mode' => $mode) if $mode;
+
+#these methods die instead of return errors, so, handle that without a backtrace
+local $@;
+my $letter_id =
+ eval { $cust_bill->postal_mail_fsinc( 'template' => $template,
+ 'notice_name' => $notice_name,
+ 'no_coupon' => $no_coupon,
+ );
+ };
+my $error = "$@";
+
+$error ||= 'Unknown print and mail error: no letter ID returned'
+ unless $letter_id;
+
+</%init>
diff --git a/httemplate/misc/process/bulk_pkg_increment_bill.cgi b/httemplate/misc/process/bulk_pkg_increment_bill.cgi
index 0d8417b26..8da849508 100755
--- a/httemplate/misc/process/bulk_pkg_increment_bill.cgi
+++ b/httemplate/misc/process/bulk_pkg_increment_bill.cgi
@@ -4,7 +4,7 @@
%} else {
<% header('Packages Adjusted') %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
% }
diff --git a/httemplate/misc/process/cancel_pkg.html b/httemplate/misc/process/cancel_pkg.html
index c7a43979d..46ba06a6d 100755
--- a/httemplate/misc/process/cancel_pkg.html
+++ b/httemplate/misc/process/cancel_pkg.html
@@ -1,6 +1,6 @@
<% header(emt("Package $past_method")) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
@@ -33,8 +33,10 @@ $method =~ /^(cancel|expire|suspend|adjourn|resume|uncancel)$/
$method = $1;
my $past_method = $past{$method};
+my $curuser = $FS::CurrentUser::CurrentUser;
+
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right($right{$method});
+ unless $curuser->access_right($right{$method});
#untaint pkgnum
my $pkgnum = $cgi->param('pkgnum');
@@ -60,7 +62,8 @@ if ( $method eq 'suspend' ) { #or 'adjourn'
$options = { map { $_ => scalar($cgi->param($_)) }
qw( suspend_bill no_suspend_bill )
- };
+ }
+ if $curuser->access_right('Customize billing during suspension');
}
my $cust_pkg = qsearchs( 'cust_pkg', {'pkgnum'=>$pkgnum} );
@@ -82,6 +85,8 @@ my $bill =
my $svc_fatal = ( $cgi->param('svc_not_fatal') ne 'Y' );
+my $only_svcnum = ($method eq 'uncancel') ? [ $cgi->param('only_svcnum') ] : undef;
+
$error ||= $cust_pkg->$method( 'reason' => $reasonnum,
'date' => $date,
'resume_date' => $resume_date,
@@ -89,6 +94,7 @@ $error ||= $cust_pkg->$method( 'reason' => $reasonnum,
'bill' => $bill,
'svc_fatal' => $svc_fatal,
'options' => $options,
+ 'only_svcnum' => $only_svcnum,
);
if ($error) {
diff --git a/httemplate/misc/process/change_pkg_contact.html b/httemplate/misc/process/change_pkg_contact.html
index 2795c1197..5bf896200 100644
--- a/httemplate/misc/process/change_pkg_contact.html
+++ b/httemplate/misc/process/change_pkg_contact.html
@@ -1,6 +1,6 @@
<% header(emt("Package contact $past_method")) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/process/change_pkg_start.html b/httemplate/misc/process/change_pkg_date.html
index 17a8518f9..3084ec538 100755
--- a/httemplate/misc/process/change_pkg_start.html
+++ b/httemplate/misc/process/change_pkg_date.html
@@ -6,9 +6,21 @@
</HTML>
<%init>
+my $field = $cgi->param('field');
+
+my ($acl, $isstart);
+if ($field eq 'start_date') {
+ $acl = 'Change package start date';
+ $isstart = 1;
+} elsif ($field eq 'contract_end') {
+ $acl = 'Change package contract end date';
+} else {
+ die "Unknown date field";
+}
+
my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied"
- unless $curuser->access_right('Change package start date');
+ unless $curuser->access_right($acl);
$cgi->param('pkgnum') =~ /^(\d+)$/
or die "illegal pkgnum";
@@ -24,23 +36,25 @@ my $cust_pkg = qsearchs({
my $cust_main = $cust_pkg->cust_main;
my $error;
-my $start_date;
+my $date_value;
if ( $cgi->param('when') eq 'now' ) {
- # start it the next time billing runs
- $start_date = '';
+ # blank start means start it the next time billing runs ("Now")
+ # blank contract end means it never ends ("Never")
+ $date_value = '';
} elsif ( $cgi->param('when') eq 'next_bill_date' ) {
- $start_date = $cust_main->next_bill_date;
+ $date_value = $cust_main->next_bill_date;
} elsif ( $cgi->param('when') eq 'date' ) {
- $start_date = parse_datetime($cgi->param('start_date'));
+ $date_value = parse_datetime($cgi->param('date_value'));
}
-if ( $cust_pkg->setup ) {
+if ( $isstart && $cust_pkg->setup ) {
# shouldn't happen
$error = 'This package has already started billing.';
} else {
local $FS::UID::AutoCommit = 0;
foreach my $pkg ($cust_pkg, $cust_pkg->supplemental_pkgs) {
- $pkg->set('start_date', $start_date);
+ last if $error;
+ $pkg->set($field, $date_value);
$error ||= $pkg->replace;
}
$error ? dbh->rollback : dbh->commit;
@@ -48,6 +62,6 @@ if ( $cust_pkg->setup ) {
if ( $error ) {
$cgi->param('error', $error);
- print $cgi->redirect($fsurl.'misc/change_pkg_start.html?', $cgi->query_string);
+ print $cgi->redirect($fsurl.'misc/change_pkg_date.html?', $cgi->query_string);
}
</%init>
diff --git a/httemplate/misc/process/cust_bill-promised_date.html b/httemplate/misc/process/cust_bill-promised_date.html
index 721a763eb..f390609c7 100644
--- a/httemplate/misc/process/cust_bill-promised_date.html
+++ b/httemplate/misc/process/cust_bill-promised_date.html
@@ -1,4 +1,4 @@
-<SCRIPT TYPE="text/javascript">window.top.location.reload()</SCRIPT>
+<SCRIPT TYPE="text/javascript">topreload()</SCRIPT>
<%init>
# XXX ACL?
die "access denied"
diff --git a/httemplate/misc/process/delay_susp_pkg.html b/httemplate/misc/process/delay_susp_pkg.html
index 675da0496..15a3c963c 100755
--- a/httemplate/misc/process/delay_susp_pkg.html
+++ b/httemplate/misc/process/delay_susp_pkg.html
@@ -1,6 +1,6 @@
<% header($msg) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/process/enable_or_disable_tax.html b/httemplate/misc/process/enable_or_disable_tax.html
index 9b7324b0d..8a7a559a0 100755
--- a/httemplate/misc/process/enable_or_disable_tax.html
+++ b/httemplate/misc/process/enable_or_disable_tax.html
@@ -4,7 +4,7 @@
<% include('/elements/header-popup.html', $title) %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
diff --git a/httemplate/misc/process/nms-add_iface.html b/httemplate/misc/process/nms-add_iface.html
index 79e685686..cbd0fc048 100644
--- a/httemplate/misc/process/nms-add_iface.html
+++ b/httemplate/misc/process/nms-add_iface.html
@@ -1,6 +1,6 @@
<& /elements/header-popup.html, 'Interface added' &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
<%init>
diff --git a/httemplate/misc/process/nms-add_router.html b/httemplate/misc/process/nms-add_router.html
index c3b42a8d3..a4c3423da 100644
--- a/httemplate/misc/process/nms-add_router.html
+++ b/httemplate/misc/process/nms-add_router.html
@@ -1,6 +1,6 @@
<& /elements/header-popup.html, 'Router added' &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
<%init>
diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi
index d232fe729..7768f921f 100644
--- a/httemplate/misc/process/payment.cgi
+++ b/httemplate/misc/process/payment.cgi
@@ -86,7 +86,7 @@ if ( (my $custpaybynum = scalar($cgi->param('custpaybynum'))) > 0 ) {
$payinfo = $cust_payby->payinfo;
$paymask = $cust_payby->paymask;
- $paycvv = '';
+ $paycvv = $cust_payby->paycvv; # pass it if we got it, running a transaction will clear it
( $month, $year ) = $cust_payby->paydate_mon_year;
$payname = $cust_payby->payname;
diff --git a/httemplate/misc/process/recharge_svc.html b/httemplate/misc/process/recharge_svc.html
index 2d49f6b06..88a1f7f36 100755
--- a/httemplate/misc/process/recharge_svc.html
+++ b/httemplate/misc/process/recharge_svc.html
@@ -4,7 +4,7 @@
%} else {
<% header("Package recharged") %>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
%}
diff --git a/httemplate/misc/process/set_page_pref.html b/httemplate/misc/process/set_page_pref.html
new file mode 100644
index 000000000..a7f123116
--- /dev/null
+++ b/httemplate/misc/process/set_page_pref.html
@@ -0,0 +1,12 @@
+<%init>
+my $path = $cgi->param('path');
+my $name = $cgi->param('name');
+my $tablenum = $cgi->param('num') || '';
+my $value = $cgi->param('value');
+
+my $error = $FS::CurrentUser::CurrentUser->set_page_pref($path, $name, $tablenum, $value);
+my $result = { 'error' => $error }; # in case someone cares
+
+http_header('Content-Type', 'application/json');
+</%init>
+<% encode_json($result) %>
diff --git a/httemplate/misc/process/unhold_pkg.html b/httemplate/misc/process/unhold_pkg.html
index 694048023..7e54262c6 100755
--- a/httemplate/misc/process/unhold_pkg.html
+++ b/httemplate/misc/process/unhold_pkg.html
@@ -1,6 +1,6 @@
<& /elements/header-popup.html &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/process/void-cust_bill.html b/httemplate/misc/process/void-cust_bill.html
index 7773b0ba9..32a2fc591 100755
--- a/httemplate/misc/process/void-cust_bill.html
+++ b/httemplate/misc/process/void-cust_bill.html
@@ -4,7 +4,7 @@
%} else {
<& /elements/header-popup.html, 'Invoice voided' &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY></HTML>
%}
diff --git a/httemplate/misc/reason-merge.html b/httemplate/misc/reason-merge.html
index 14f5ebb84..b7e4df454 100644
--- a/httemplate/misc/reason-merge.html
+++ b/httemplate/misc/reason-merge.html
@@ -1,7 +1,7 @@
% if ($success) {
<% include('/elements/header-popup.html', 'Reason Merge Success') %>
<SCRIPT>
-window.top.location.reload()
+topreload()
</SCRIPT>
% } else {
<% include('/elements/header-popup.html', 'Merge Reasons') %>
diff --git a/httemplate/misc/regions.cgi b/httemplate/misc/regions.cgi
index 31538b08e..882dd48df 100644
--- a/httemplate/misc/regions.cgi
+++ b/httemplate/misc/regions.cgi
@@ -1,4 +1,4 @@
-<% encode_json(\@regions) %>\
+<% encode_json({ error => $error, regions => \@regions}) %>\
<%init>
my( $state, $svcpart ) = $cgi->param('arg');
@@ -7,6 +7,8 @@ my $part_svc = qsearchs('part_svc', { 'svcpart'=>$svcpart } );
die "unknown svcpart $svcpart" unless $part_svc;
my @regions = ();
+my $error;
+
if ( $state ) {
my @exports = $part_svc->part_export_did;
@@ -17,9 +19,12 @@ if ( $state ) {
}
my $export = $exports[0];
- my $something = $export->get_dids('state'=>$state);
+ local $@;
+ local $SIG{__DIE__};
+ my $something = eval { $export->get_dids('state'=>$state) };
+ $error = $@;
- @regions = @{ $something };
+ @regions = @{ $something } if $something;
}
diff --git a/httemplate/misc/sector-create_map.html b/httemplate/misc/sector-create_map.html
new file mode 100644
index 000000000..6af5fddbe
--- /dev/null
+++ b/httemplate/misc/sector-create_map.html
@@ -0,0 +1,10 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); # ???
+
+my $server =
+ new FS::UI::Web::JSRPC 'FS::tower_sector::process_generate_coverage', $cgi;
+
+</%init>
diff --git a/httemplate/misc/tax-fetch_and_import.cgi b/httemplate/misc/tax-fetch_and_import.cgi
index 33a6c9b01..970d47c32 100644
--- a/httemplate/misc/tax-fetch_and_import.cgi
+++ b/httemplate/misc/tax-fetch_and_import.cgi
@@ -29,8 +29,9 @@ Import a tax data update.
<TR>
<TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
<INPUT TYPE = "submit"
+ NAME = "submitButton"
VALUE = "Download and Import"
- onClick = "document.TaxRateImport.submit.disabled=true; process();"
+ onClick = "document.TaxRateImport.submitButton.disabled=true; process();"
>
</TD>
</TR>
diff --git a/httemplate/misc/tax-fetch_and_replace.cgi b/httemplate/misc/tax-fetch_and_replace.cgi
index 3290a3c44..ff64e6320 100644
--- a/httemplate/misc/tax-fetch_and_replace.cgi
+++ b/httemplate/misc/tax-fetch_and_replace.cgi
@@ -29,8 +29,9 @@ Replace tax data.
<TR>
<TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
<INPUT TYPE = "submit"
+ NAME = "submitButton"
VALUE = "Download and Import"
- onClick = "document.TaxRateImport.submit.disabled=true; process();"
+ onClick = "document.TaxRateImport.submitButton.disabled=true; process();"
>
</TD>
</TR>
diff --git a/httemplate/misc/tower-export.html b/httemplate/misc/tower-export.html
new file mode 100644
index 000000000..9d63640f0
--- /dev/null
+++ b/httemplate/misc/tower-export.html
@@ -0,0 +1,36 @@
+<%init>
+# currently, browse/tower just shows all towers, so do the same here
+my @towers = qsearch({ table => 'tower' });
+http_header('Content-Type' => 'text/csv');
+http_header('Content-Disposition' => 'attachment;filename=towers.csv');
+if ( $cgi->param('format') eq 'tc' ) {
+ # towercoverage.com format: not a true CSV, no quoting (so no way to include
+ # commas in any field, so we strip them)
+
+ # lat/long are signed decimals, northeast positive
+ # height is in meters
+ # Description/Group are not necessary
+ # sector/antenna information (orientation, beamwidth, gain, frequency,
+ # etc.) is in what TC calls a "Coverage", which can't be edited this way.
+ my $text = "SiteName,Latitude,Longitude,Description,Group,Height\n";
+
+ foreach my $tower (@towers) {
+ next if ( !$tower->latitude or !$tower->longitude );
+
+ my $name = $tower->towername;
+ my $height = ($tower->height || 0) / 3.28;
+ $name =~ s(,)( )g;
+ $text .= join(',',
+ $name,
+ $tower->latitude,
+ $tower->longitude,
+ '',
+ '',
+ $height,
+ ) . "\n";
+ }
+ $m->print($text);
+} else {
+ die('unknown format '.$cgi->param('format'));
+}
+</%init>
diff --git a/httemplate/misc/void-cust_credit.html b/httemplate/misc/void-cust_credit.html
index 1e71f0030..81ba31d54 100755
--- a/httemplate/misc/void-cust_credit.html
+++ b/httemplate/misc/void-cust_credit.html
@@ -1,7 +1,7 @@
%if ( $success ) {
<& /elements/header-popup.html, mt("Credit voided") &>
<SCRIPT TYPE="text/javascript">
- window.top.location.reload();
+ topreload();
</SCRIPT>
</BODY>
</HTML>
diff --git a/httemplate/misc/xmlhttp-address_standardize.html b/httemplate/misc/xmlhttp-address_standardize.html
index d0255a02a..be58618d0 100644
--- a/httemplate/misc/xmlhttp-address_standardize.html
+++ b/httemplate/misc/xmlhttp-address_standardize.html
@@ -23,6 +23,7 @@ if ( $old{same} ) {
@prefixes = ('bill_', 'ship_');
}
my $all_same = 1;
+my $all_clean = 1;
foreach my $pre ( @prefixes ) {
my $location = {
@@ -48,8 +49,13 @@ foreach my $pre ( @prefixes ) {
$old{$pre.'censustract'} ne $new{$pre.'censustract'} );
$all_same = 0 if $new{$pre.'error'};
+
+ $all_clean = 0 if !$new{$pre.'addr_clean'};
}
-my $return = { old => \%old, new => \%new, all_same => $all_same };
+my $return = { old => \%old,
+ new => \%new,
+ all_same => $all_same,
+ all_clean => $all_clean };
warn "result:\n".encode_json($return) if $DEBUG;
</%init>
diff --git a/httemplate/misc/xmlhttp-ticket-update.html b/httemplate/misc/xmlhttp-ticket-update.html
index bd58b95c6..01fb1b44d 100644
--- a/httemplate/misc/xmlhttp-ticket-update.html
+++ b/httemplate/misc/xmlhttp-ticket-update.html
@@ -8,14 +8,18 @@ my $username = $cgi->param('username');
my $ticket = FS::TicketSystem->get_ticket_object( \%session, ticket_id=>$id );
-#hmm, this should happen in a single transaction and either commit or rollback,
-# but in reality failures "Don't Happen" so its not like a ticket gets
-# half changed
+#hmm, this should happen in a single transaction and either commit or rollback
my $return;
if ( $ticket ) {
- my($orv, $omsg) = $ticket->SetOwner( $username, 'Steal' );
+ my $curowner = $ticket->OwnerObj->Name;
+ my($orv, $omsg);
+ if (( $curowner eq $FS::CurrentUser::CurrentUser->username ) or ( $curowner eq 'nobody' )) {
+ ($orv, $omsg) = $ticket->SetOwner( $username );
+ } else {
+ ($orv, $omsg) = $ticket->SetOwner( $username, 'Steal' );
+ }
$orv = 1 if ! $orv && $omsg =~ /already own/i;
if ( $orv ) {
@@ -42,15 +46,18 @@ if ( $ticket ) {
my %hash = $m->comp('/rt/Ticket/Elements/Customers', Ticket => $ticket);
my @cust_main = values( %{$hash{cust_main}} );
+ my $timelabel = FS::sched_avail::pretty_time($sh*60+$sm). '-'.
+ FS::sched_avail::pretty_time($eh*60+$em);
+ my $titlelabel = encode_entities($cust_main[0]->_FreesideURILabel);
+
$return = { 'error' => '',
#'starts' => $starts,
#'due' => $due,
#'username' => $username,
#false laziness w/CalendarSlotSchedule
- 'sched_label' =>
- FS::sched_avail::pretty_time($sh*60+$sm). '-'.
- FS::sched_avail::pretty_time($eh*60+$em). ': '.
- encode_entities($cust_main[0]->_FreesideURILabel),
+ 'sched_label' => $timelabel . ': ' . $titlelabel,
+ 'sched_label_time' => $timelabel,
+ 'sched_label_title' => $titlelabel,
};
} else {
$return = { 'error' => $smsg };
diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html
index f03a8dfa3..b622efc15 100644
--- a/httemplate/pref/pref-process.html
+++ b/httemplate/pref/pref-process.html
@@ -55,6 +55,7 @@ unless ( $error ) { # if ($access_user) {
disable_html_editor disable_enter_submit_onetimecharge
enable_mask_clipboard_hack dashboard_customers
customer_view_emails
+ printtofit
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 50d6e8d23..93c71996c 100644
--- a/httemplate/pref/pref.html
+++ b/httemplate/pref/pref.html
@@ -36,7 +36,7 @@ Interface
<TH ALIGN="right">Locale: </TH>
<TD COLSPAN=2>
<SELECT NAME="locale">
-% foreach my $locale ( FS::Locales->locales ) {
+% foreach my $locale ( @locales ) {
% my %info = FS::Locales->locale_info($locale);
% my $selected = ($locale eq $curuser->option('locale'))
% ? 'SELECTED' : '';
@@ -130,6 +130,13 @@ Interface
</TR>
<TR>
+ <TH ALIGN="right">Scale documents to fit on a letter-size page</TH>
+ <TD ALIGN="left">
+ <INPUT TYPE="checkbox" NAME="printtofit" VALUE="Y" <% $curuser->option('printtofit') ? 'CHECKED' : '' %>>
+ </TD>
+ </TR>
+
+ <TR>
<TH ALIGN="right">How many recently-modified customers displayed on dashboard</TH>
<TD ALIGN="left" COLSPAN=2>
<INPUT TYPE="text" NAME="dashboard_customers" VALUE="<% $curuser->option('dashboard_customers') %>"></TD>
@@ -143,8 +150,6 @@ Interface
</TD>
</TR>
-
-
</TABLE>
<BR>
@@ -282,4 +287,14 @@ my $menu_position = $1;
=~ /^([,\w\@.\-]*)$/ or die "illegal email_address"; #too late
my $email_address = $1;
+my $conf = new FS::Conf;
+
+my @locales = $conf->config('available-locales');
+
+if ( ! @locales ) {
+
+ @locales = FS::Locales->locales ;
+
+}
+
</%init>
diff --git a/httemplate/search/agent_credit_payment.html b/httemplate/search/agent_credit_payment.html
new file mode 100644
index 000000000..0dda83bbd
--- /dev/null
+++ b/httemplate/search/agent_credit_payment.html
@@ -0,0 +1,155 @@
+<& elements/grid-report.html,
+ title => $title.'Package Agent Credits and Payments',
+ rows => $rows,
+ cells => $cells,
+ head => <<END,
+<P>Shows agent commission credits, and payments applied to invoices for packages that triggered those credits.</P>
+<STYLE SCOPED>
+td.creditcell { background-color: #ffff99; }
+td.paycell { background-color: #66ff66; }
+</STYLE>
+END
+&>
+
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied"
+ unless $curuser->access_right('Financial reports');
+
+my $extra_sql = '';
+
+# search for agent
+my ($agentnum,$sel_agent);
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ $agentnum = $1;
+ $sel_agent = qsearchs('agent', { 'agentnum' => $agentnum } );
+ die "agentnum $agentnum not found!" unless $sel_agent;
+ $extra_sql .= " AND cust_credit.commission_agentnum = $agentnum\n";
+}
+my $title = $sel_agent ? $sel_agent->agent.' ' : '';
+
+# search for credits in time period (applied to payments in $query)
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+$extra_sql .= " AND cust_credit._date >= $beginning\n";
+$extra_sql .= " AND cust_credit._date <= $ending\n";
+
+# agent virtualization
+my $agentnums_sql = $curuser->agentnums_sql( table => 'agent' );
+
+my $join = <<END;
+ LEFT JOIN agent ON ( cust_credit.commission_agentnum = agent.agentnum )
+ LEFT JOIN cust_pkg ON ( cust_credit.commission_pkgnum = cust_pkg.pkgnum )
+ LEFT JOIN part_pkg ON ( cust_pkg.pkgpart = part_pkg.pkgpart )
+END
+
+my $query = <<END;
+SELECT DISTINCT
+ agent,
+ cust_pkg.custnum AS xcustnum,
+ cust_credit.commission_pkgnum AS xpkgnum,
+ pkg,
+ 'cust_pay' AS xtable,
+ cust_pay.paynum AS xnum,
+ to_timestamp(cust_pay._date) AS xdate,
+ cust_pay.paid AS xamount,
+ cust_pay.order_number AS order_number
+FROM cust_pay
+ INNER JOIN cust_bill_pay ON ( cust_pay.paynum = cust_bill_pay.paynum )
+ INNER JOIN cust_bill_pkg ON ( cust_bill_pay.invnum = cust_bill_pkg.invnum )
+ INNER JOIN cust_credit ON ( cust_bill_pkg.pkgnum = cust_credit.commission_pkgnum )
+$join
+WHERE cust_credit.commission_pkgnum IS NOT NULL
+ AND cust_pay._date >= $beginning
+ AND cust_pay._date <= $ending
+ AND $agentnums_sql
+$extra_sql
+UNION
+SELECT DISTINCT
+ agent,
+ cust_pkg.custnum AS xcustnum,
+ cust_credit.commission_pkgnum AS xpkgnum,
+ pkg,
+ 'cust_credit' AS xtable,
+ cust_credit.crednum AS xnum,
+ to_timestamp(cust_credit._date) AS xdate,
+ cust_credit.amount AS xamount,
+ '' AS order_number
+FROM cust_credit
+$join
+WHERE cust_credit.commission_pkgnum is not null
+ AND $agentnums_sql
+$extra_sql
+ORDER BY agent, xcustnum, xpkgnum, xdate
+END
+
+my $sth = dbh->prepare($query) or die dbh->errstr;
+$sth->execute() or die $sth->errstr;
+
+my $cells = [];
+my $rows = [];
+my $agentstack = [];
+my $custstack = [];
+my $pkgstack = [];
+my ($prev_agent,$count_agent,$prev_cust,$count_cust,$prev_pkg,$count_pkg);
+while (my $row = $sth->fetchrow_arrayref) {
+ my @row = @$row;
+ my $curr_agent = shift @row;
+ my $curr_cust = shift @row;
+ my $curr_pkg = (shift @row) . ': ' . (shift @row);
+
+ if ($curr_pkg eq $prev_pkg) {
+ $count_pkg += 1;
+ } else {
+ unshift @{$$pkgstack[0]}, { value => $prev_pkg, rowspan => $count_pkg } if @$pkgstack;;
+ push @$custstack, @$pkgstack;
+ $pkgstack = [];
+ $count_pkg = 1;
+ }
+ $prev_pkg = $curr_pkg;
+
+ if ($curr_cust eq $prev_cust) {
+ $count_cust += 1;
+ } else {
+ if (@$custstack) {
+ my $cust_main = qsearchs('cust_main',{ custnum => $prev_cust });
+ unshift @{$$custstack[0]}, { value => $cust_main->name, rowspan => $count_cust } if @$custstack;;
+ }
+ push @$agentstack, @$custstack;
+ $custstack = [];
+ $count_cust = 1;
+ }
+ $prev_cust = $curr_cust;
+
+ if ($curr_agent eq $prev_agent) {
+ $count_agent += 1;
+ } else {
+ unshift @{$$agentstack[0]}, { value => $prev_agent, rowspan => $count_agent } if @$agentstack;;
+ push @$cells, @$agentstack;
+ $agentstack = [];
+ $count_agent = 1;
+ }
+ $prev_agent = $curr_agent;
+
+ my %coloropts = ($row[0] eq 'cust_credit') ? ( 'class' => 'creditcell' ) : ( 'class' => 'paycell' );
+ push @$pkgstack, [ map { { value => $_, %coloropts } } @row ];
+}
+
+unshift @{$$pkgstack[0]}, { value => $prev_pkg, rowspan => $count_pkg } if @$pkgstack;;
+push @$custstack, @$pkgstack;
+if (@$custstack) {
+ my $cust_main = qsearchs('cust_main',{ custnum => $prev_cust });
+ unshift @{$$custstack[0]}, { value => $cust_main->name, rowspan => $count_cust } if @$custstack;;
+}
+push @$agentstack, @$custstack;
+unshift @{$$agentstack[0]}, { value => $prev_agent, rowspan => $count_agent } if @$agentstack;;
+push @$cells, @$agentstack;
+
+$sth->finish;
+
+my $rows = [ map { {} } @$cells ];
+
+unshift @$cells, [ map { { value => $_, header => 1 } } ('Agent','Customer','Package','Table','#','Date','Amount','Order Number') ];
+unshift @$rows, { header => 1 };
+
+</%init>
diff --git a/httemplate/search/contact.html b/httemplate/search/contact.html
index c3667df98..44c864c16 100644
--- a/httemplate/search/contact.html
+++ b/httemplate/search/contact.html
@@ -22,6 +22,12 @@ push @select, map "contact.$_", qw( first last title );
my %hash = ();
my $addl_from = '';
+my $email_sub = sub {
+ my $contact = shift;
+ my @contact_email = $contact->contact_email;
+ join(', ', map $_->emailaddress, @contact_email);
+};
+
my $link; #for closure in this sub, we'll define it later
my $contact_classname_sub = sub {
my $contact = shift;
@@ -37,9 +43,9 @@ my $contact_classname_sub = sub {
$X_contact->contact_classname;
};
-my @header = ( 'First', 'Last', 'Title', 'Type' );
-my @fields = ( 'first', 'last', 'title', $contact_classname_sub );
-my @links = ( '', '', '', '', );
+my @header = ( 'First', 'Last', 'Title', 'Email', 'Type' );
+my @fields = ( 'first', 'last', 'title', $email_sub, $contact_classname_sub );
+my @links = ( '', '', '', '', '', );
my $company_link = '';
diff --git a/httemplate/search/cust_bill_pay_pkg.html b/httemplate/search/cust_bill_pay_pkg.html
index 5a3be7551..e2ffd1258 100644
--- a/httemplate/search/cust_bill_pay_pkg.html
+++ b/httemplate/search/cust_bill_pay_pkg.html
@@ -14,6 +14,7 @@
#payment
'Date',
+ @on_header,
'By',
#application
@@ -43,6 +44,7 @@
? cardtype($cust_pay->paymask) : '';
},
sub { time2str('%b %d %Y', shift->get('cust_pay_date') ) },
+ @on_field,
sub { shift->cust_bill_pay->cust_pay->otaker },
sub { sprintf($money_char.'%.2f', shift->amount ) },
@@ -64,6 +66,7 @@
'', #payinfo/paymask
'', #cardtype
'cust_pay_date',
+ @on_null, #order_number
'', #'otaker',
'', #amount
'', #line item description
@@ -80,6 +83,7 @@
'',
'',
'',
+ @on_null,
'',
'',
'',
@@ -92,10 +96,9 @@
FS::UI::Web::cust_header()
),
],
- 'align' => 'rcrlrlrlll',
-#original value before cardtype & package were added
-#why are there 13 cols?
-#'rcrrlrlllrrcl'.
+ 'align' => 'rcrlr'.
+ $on_align.
+ 'lrlll'.
$post_desc_align.
'rr'.
FS::UI::Web::cust_aligns(),
@@ -105,6 +108,7 @@
'',
'',
'',
+ @on_null,
'',
'',
'',
@@ -121,6 +125,7 @@
'',
'',
'',
+ @on_null,
'',
'',
'',
@@ -142,6 +147,17 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+my @on_header = ();
+my @on_field = ();
+my @on_null = ();
+my $on_align = '';
+if ($cgi->param('show_order_number')) {
+ @on_header = ('Order Number');
+ @on_field = (sub { shift->cust_bill_pay->cust_pay->order_number });
+ @on_null = ('');
+ $on_align = 'r';
+}
+
my $conf = new FS::Conf;
my %payby = FS::payby->payby2shortname;
diff --git a/httemplate/search/cust_credit.html b/httemplate/search/cust_credit.html
index 2241f02e3..dbf0ff333 100755
--- a/httemplate/search/cust_credit.html
+++ b/httemplate/search/cust_credit.html
@@ -56,24 +56,29 @@ if ($unapplied) {
push @header, emt('Date'),
emt('By'),
emt('Reason'),
+ emt('Info'),
;
push @fields, sub { time2str('%b %d %Y', shift->_date ) },
'otaker',
- 'reason',
+ 'reason_only',
+ 'addlinfo',
;
-push @sort_fields, '_date', 'otaker', 'reason';
-$align .= 'rll';
+push @sort_fields, '_date', 'otaker', 'reasonnum', 'addlinfo';
+$align .= 'rlll';
push @links, '',
'',
'',
+ '',
;
push @color, '',
'',
'',
+ '',
;
push @style, '',
'',
'',
+ '',
;
# insert customer email after 'Reason' if this is a commission report
diff --git a/httemplate/search/cust_credit_bill_pkg.html b/httemplate/search/cust_credit_bill_pkg.html
index b9bbc4dbb..0cdd8defd 100644
--- a/httemplate/search/cust_credit_bill_pkg.html
+++ b/httemplate/search/cust_credit_bill_pkg.html
@@ -14,6 +14,7 @@
'Date',
'By',
'Reason',
+ 'Info',
# line item
'Description',
@@ -33,7 +34,8 @@
sub { time2str('%b %d %Y', shift->get('cust_credit_date') ) },
sub { shift->cust_credit_bill->cust_credit->otaker },
- sub { shift->cust_credit_bill->cust_credit->reason },
+ sub { shift->cust_credit_bill->cust_credit->reason_only },
+ sub { shift->cust_credit_bill->cust_credit->addlinfo },
sub { $_[0]->pkgnum > 0
? $_[0]->get('pkg') # possibly use override.pkg
@@ -51,6 +53,7 @@
'cust_credit_date',
'', #'otaker',
'', #reason
+ '', #addlinfo
'', #line item description
'', #location
@post_desc_null,
@@ -66,6 +69,7 @@
'',
'',
'',
+ '',
@post_desc_null,
$ilink,
$ilink,
@@ -73,7 +77,7 @@
FS::UI::Web::cust_header()
),
],
- 'align' => 'rrrllll'.
+ 'align' => 'rrrlllll'.
$post_desc_align.
'rr'.
FS::UI::Web::cust_aligns(),
@@ -85,6 +89,7 @@
'',
'',
'',
+ '',
@post_desc_null,
'',
'',
@@ -98,6 +103,7 @@
'',
'',
'',
+ '',
@post_desc_null,
'',
'',
diff --git a/httemplate/search/cust_credit_source_bill_pkg.html b/httemplate/search/cust_credit_source_bill_pkg.html
index 3ef88bdf9..1d5f8d2a0 100644
--- a/httemplate/search/cust_credit_source_bill_pkg.html
+++ b/httemplate/search/cust_credit_source_bill_pkg.html
@@ -28,7 +28,7 @@
sub { time2str('%b %d %Y', shift->get('cust_credit_date') ) },
sub { shift->cust_credit->otaker },
- sub { shift->cust_credit->reason },
+ sub { shift->cust_credit->reason }, # split into reason_only/addlinfo if addlinfo ever gets used here
sub { $_[0]->pkgnum > 0
? $_[0]->get('pkg') # possibly use override.pkg
diff --git a/httemplate/search/cust_credit_void.html b/httemplate/search/cust_credit_void.html
index 18731d144..8a8b4133e 100755
--- a/httemplate/search/cust_credit_void.html
+++ b/httemplate/search/cust_credit_void.html
@@ -47,6 +47,7 @@ push @header, emt('Void Date'),
emt('Date'),
emt('By'),
emt('Reason'),
+ emt('Info'),
FS::UI::Web::cust_header(),
;
push @fields, sub { time2str('%b %d %Y', shift->void_date ) },
@@ -54,7 +55,8 @@ push @fields, sub { time2str('%b %d %Y', shift->void_date ) },
'void_reason',
sub { time2str('%b %d %Y', shift->_date ) },
'otaker',
- 'reason',
+ 'reason_only',
+ 'addlinfo',
\&FS::UI::Web::cust_fields,
;
push @sort_fields, 'void_date',
@@ -63,14 +65,16 @@ push @sort_fields, 'void_date',
'_date',
'usernum', #ditto
'reasonnum, reason', #ditto
+ 'addlinfo',
FS::UI::Web::cust_sort_fields();
-$align .= 'rllrll'.FS::UI::Web::cust_aligns();
+$align .= 'rllrlll'.FS::UI::Web::cust_aligns();
push @links, '',
'',
'',
'',
'',
'',
+ '',
( map { $_ ne 'Cust. Status' ? $clink : '' }
FS::UI::Web::cust_header()
),
@@ -81,6 +85,7 @@ push @color, '',
'',
'',
'',
+ '',
FS::UI::Web::cust_colors(),
;
push @style, '',
@@ -89,6 +94,7 @@ push @style, '',
'',
'',
'',
+ '',
FS::UI::Web::cust_styles(),
;
diff --git a/httemplate/search/cust_event.html b/httemplate/search/cust_event.html
index 757982b95..b1ba9090e 100644
--- a/httemplate/search/cust_event.html
+++ b/httemplate/search/cust_event.html
@@ -1,6 +1,6 @@
<& elements/search.html,
'title' => $title,
- 'html_init' => $html_init,
+ 'html_init' => include('.init'),
'menubar' => $menubar,
'name' => 'billing events',
'query' => $sql_query,
@@ -140,6 +140,11 @@ my $trigger_link = sub {
};
</%once>
+<%shared>
+my @scalars = qw( agentnum status custnum invnum pkgnum failed );
+my @lists = qw( eventpart event_status );
+my %search;
+</%shared>
<%init>
my $curuser = $FS::CurrentUser::CurrentUser;
@@ -152,18 +157,19 @@ die "access denied"
|| $cgi->param('pkgnum') =~ /^(\d+)$/
);
-my $title = $cgi->param('failed') ? 'Failed billing events' : 'Billing events';
-
-my %search = ();
+my @statuses = $cgi->param('event_status');
+my $title = 'Billing events';
+if ( $statuses[0] eq 'failed' and !defined($statuses[1]) ) {
+ # tweak the title if we're showing only failed events
+ $title = 'Failed billing events';
+}
-my @scalars = qw( agentnum status custnum invnum pkgnum failed );
for my $param (@scalars) {
$search{$param} = scalar( $cgi->param($param) )
if $cgi->param($param);
}
#lists
-my @lists = qw( eventpart );
foreach my $param (@lists) {
$search{$param} = [ $cgi->param($param) ];
}
@@ -175,12 +181,16 @@ $search{'ending'} = $ending;
my $where = ' WHERE '. FS::cust_event->search_sql_where( \%search );
my $join = FS::cust_event->join_sql() .
+ # warning: does not show the true service address for package events.
+ # the query to do that would be painfully slow.
'LEFT JOIN cust_location bill_location '.
'ON (cust_main.bill_locationnum = bill_location.locationnum) '.
'LEFT JOIN cust_location ship_location '.
- 'ON (cust_main.ship_locationnum = ship_location.locationnum)';
- # warning: does not show the true service address for package events.
- # the query to do that would be painfully slow.
+ 'ON (cust_main.ship_locationnum = ship_location.locationnum)'.
+ # include link to referral in case it's in cust-fields
+ # (maybe we should be using FS::UI::Web::join_cust_main instead?)
+ 'LEFT JOIN (select refnum, referral from part_referral) AS part_referral_x '.
+ 'ON (cust_main.refnum = part_referral_x.refnum) ';
my $sql_query = {
'table' => 'cust_event',
@@ -203,55 +213,6 @@ my $count_sql = "SELECT COUNT(*) FROM cust_event $join $where";
my $conf = new FS::Conf;
-my @params = ( @scalars, qw( beginning ending ) );
-
-my $html_init = join("\n", map {
- ( my $action = $_ ) =~ s/_$//;
- include('/elements/progress-init.html',
- $_.'form',
- [ 'action', @params ],
- "../misc/${_}events.cgi",
- { 'message' => "Invoices re-${action}ed" }, #would be nice to show the number of them, but...
- $_, #key
- ),
- qq!<FORM NAME="${_}form">!,
- qq!<INPUT TYPE="hidden" NAME="action" VALUE="$_">!, #not used though
- ( map { my $value = encode_entities( $search{$_} );
- qq(<INPUT TYPE="hidden" NAME="$_" VALUE="$value">);
- }
- @params #keys %search
- ),
- ( map { my $value = encode_entities( join(',', @{ $search{$_} } ) );
- qq(<INPUT TYPE="hidden" NAME="$_" VALUE="$value">);
- }
- @lists
- ),
- qq!</FORM>!
-} qw( print_ email_ fax_ ) ).
-
-'<SCRIPT TYPE="text/javascript">
-
-function confirm_print_process() {
- if ( ! confirm("Are you sure you want to reprint these invoices?") ) {
- return;
- }
- print_process();
-}
-function confirm_email_process() {
- if ( ! confirm("Are you sure you want to re-email these invoices?") ) {
- return;
- }
- email_process();
-}
-function confirm_fax_process() {
- if ( ! confirm("Are you sure you want to re-fax these invoices?") ) {
- return;
- }
- fax_process();
-}
-
-</SCRIPT>';
-
my $menubar = [];
if ( $curuser->access_right('Resend invoices') ) {
@@ -276,3 +237,46 @@ my $link_cust = sub {
};
</%init>
+<%def .init>
+% # action is part of the target URL, don't need to pass it as a param
+% foreach my $action (qw(print email fax)) {
+<& /elements/progress-init.html,
+ $action.'_form',
+ [ @scalars, @lists, 'beginning', 'ending' ],
+ "../misc/${action}_events.cgi",
+ { 'message' => "Invoices re-${action}ed" }, #would be nice to show the number of them, but...
+ $action.'_', #key
+&>
+<FORM NAME="<% $action %>_form">
+% foreach my $param (@scalars, 'beginning', 'ending') {
+ <INPUT TYPE="hidden" NAME="<% $param %>" VALUE="<% $search{$param} |h %>">
+% }
+% foreach my $param (@lists) {
+% foreach my $value (@{ $search{$param} }) {
+ <INPUT TYPE="hidden" NAME="<% $param %>" VALUE="<% $value |h %>">
+% }
+% }
+</FORM>
+% } # foreach $action
+<SCRIPT TYPE="text/javascript">
+
+function confirm_print_process() {
+ if ( ! confirm("Are you sure you want to reprint these invoices?") ) {
+ return;
+ }
+ print_process();
+}
+function confirm_email_process() {
+ if ( ! confirm("Are you sure you want to re-email these invoices?") ) {
+ return;
+ }
+ email_process();
+}
+function confirm_fax_process() {
+ if ( ! confirm("Are you sure you want to re-fax these invoices?") ) {
+ return;
+ }
+ fax_process();
+}
+</SCRIPT>
+</%def>
diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi
index 96f5af913..da6c89d44 100755
--- a/httemplate/search/cust_main.cgi
+++ b/httemplate/search/cust_main.cgi
@@ -120,9 +120,21 @@
% foreach my $cust_pkg ( @{$all_pkgs{$custnum}} ) {
% my %cust_svc_by_svcpart;
% my $rows = 0;
-% local($FS::part_pkg::cache_enabled) = 1; #for $cust_pkg->part_svc
+% #local($FS::part_pkg::cache_enabled) = 1; #for $cust_pkg->part_svc
+% local($FS::cust_svc::cache_enabled) = 1; #for $cust_svc->part_svc
+% #local($FS::pkg_svc::cache_enabled) = 1; #for $pkg_svc->part_svc
% foreach my $part_svc (
-% $cust_pkg->part_svc( summarize_size=>$large_pkg_size )
+% #$cust_pkg->part_svc( summarize_size=>$large_pkg_size )
+% qsearch({
+% 'select' => 'part_svc.*, COUNT(*) AS num_cust_svc',
+% 'table' => 'part_svc',
+% 'addl_from' => 'LEFT JOIN cust_svc USING ( svcpart )',
+% 'extra_sql' => 'WHERE pkgnum = ? '.
+% ' GROUP BY '. join(', ',
+% map "part_svc.$_", fields('part_svc')
+% ),
+% 'extra_param' => [ [$cust_pkg->pkgnum,'int'] ],
+% })
% ) {
% my $svcpart = $part_svc->svcpart;
% my $num_cust_svc = $part_svc->num_cust_svc;
@@ -134,7 +146,9 @@
% $rows += 2;
% }
% elsif ( $num_cust_svc ) {
-% $cust_svc_by_svcpart{$svcpart} = $part_svc->cust_pkg_svc;
+% #$cust_svc_by_svcpart{$svcpart} = $part_svc->cust_pkg_svc;
+% #further optimization opportunities: don't need to re-pull in another $part_svc object, sorting this is expensive, etc.
+% $cust_svc_by_svcpart{$svcpart} = [ $cust_pkg->cust_svc($part_svc->svcpart) ];
% $rows += $num_cust_svc;
% } #if summarize
% } #foreach $part_svc
@@ -234,6 +248,7 @@
% my $n1 = '';
% foreach ( @{$all_pkgs{$custnum}} ) {
+% local($FS::cust_svc::cache_enabled) = 1; #for $cust_svc->part_svc
% my $pkgnum = $_->pkgnum;
% my $part_pkg = $_->part_pkg;
%
@@ -281,12 +296,13 @@
% }
% elsif ( scalar @$these ) { # do not summarize
% foreach my $cust_svc ( @$these ) {
+% my $part_svc = $cust_svc->part_svc;
<% $n2 %>
<% $td %>
- <% FS::UI::Web::svc_link($m, $cust_svc->part_svc, $cust_svc) %>
+ <% FS::UI::Web::svc_link($m, $part_svc, $cust_svc) %>
</TD>
<% $td %>
- <% FS::UI::Web::svc_label_link($m, $cust_svc->part_svc, $cust_svc) %>
+ <% FS::UI::Web::svc_label_link($m, $part_svc, $cust_svc) %>
</TD>
% $n2="</TR><TR>";
% } #foreach $cust_svc
@@ -485,7 +501,7 @@ if ( $cgi->param('browse')
);
}
- @cust_main = grep { $_->num_ncancelled_pkgs || ! $_->num_pkgs } @cust_main
+ @cust_main = grep { $_->status ne 'cancelled' } @cust_main
if ! $cgi->param('cancelled')
&& (
$cgi->param('showcancelledcustomers') eq '0' #see if it was set by me
@@ -517,8 +533,9 @@ if ( scalar(@cust_main) > 1 || $cgi->param('referral_custnum') ) {
local($FS::cust_pkg::cache_enabled) = 1; #for $cust_pkg->part_pkg
%all_pkgs = map { $_->custnum =>
- [ $_->$pkgs_method({ select => $select,
- addl_from => $addl_from,
+ [ $_->$pkgs_method({ select => $select,
+ addl_from => $addl_from,
+ skip_label_sort => 1,
})
];
}
@@ -686,12 +703,20 @@ sub address2search {
or errorpage(emt("Illegal address2"));
my $address2 = $1;
- push @cust_main, qsearch( 'cust_main',
- { 'address2' => { 'op' => 'ILIKE',
- 'value' => $address2 } } );
- push @cust_main, qsearch( 'cust_main',
- { 'ship_address2' => { 'op' => 'ILIKE',
- 'value' => $address2 } } );
+ # matching at the start or end of an address, but not in the middle
+ my @where;
+ foreach my $toggle (0,1) {
+ push @where, 'LOWER(cust_location.address2) LIKE LOWER('
+ . dbh->quote($toggle ? $address2 . '%' : '%' . $address2)
+ . ')';
+ }
+
+ push @cust_main, qsearch({
+ 'debug' => 1,
+ 'table' => 'cust_main',
+ 'addl_from' => 'JOIN cust_location ON (cust_location.locationnum IN (cust_main.bill_locationnum, cust_main.ship_locationnum))',
+ 'extra_sql' => 'WHERE ' . join(' OR ',@where),
+ });
\@cust_main;
}
diff --git a/httemplate/search/cust_pay_pending.html b/httemplate/search/cust_pay_pending.html
index 8662d1989..697bdbbf0 100755
--- a/httemplate/search/cust_pay_pending.html
+++ b/httemplate/search/cust_pay_pending.html
@@ -17,7 +17,7 @@
my %statusaction = (
'new' => 'delete',
'pending' => 'complete',
- #'authorized' => '',
+ 'authorized' => 'complete',
'captured' => 'capture',
#'declined' => '',
#wouldn't need to take action on a done state#'done'
diff --git a/httemplate/search/cust_pkg-date.html b/httemplate/search/cust_pkg-date.html
new file mode 100644
index 000000000..1b9377546
--- /dev/null
+++ b/httemplate/search/cust_pkg-date.html
@@ -0,0 +1,90 @@
+<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+die 'access denied' unless $curuser->access_right('List packages');
+
+my %cols = (
+ 'contract_end' => 'Contract end',
+ # We could put any of the date fields in cust_pkg in here, but keep in
+ # mind:
+ # - for start_date, setup, and bill, make sure to include rows where
+ # the field is null, as that's effectively "right now".
+ # - for cancel and susp, and maybe expire, adjourn, and resume, add a
+ # column for the cancel or suspend reason.
+ # - for expire, also figure out if there's a future change scheduled.
+ # - for change_date, should probably show what it was changed from.
+);
+
+my $col = $cgi->param('date');
+die "invalid date column" unless $cols{$col};
+
+my $title = 'Packages by ' . lc($cols{$col}) . ' date';
+# second option on the cust_fields_avail list, plus email
+my $cust_fields = 'Cust# | Customer | Day phone | Night phone | Mobile phone | Invoicing email(s)';
+my @header = ( $cols{$col},
+ emt('#'),
+ emt('Quan.'),
+ emt('Package'),
+ # anything else? package status, maybe?
+ );
+my @fields = ( sub { time2str('%b %d %Y', $_[0]->$col) },
+ 'pkgnum',
+ 'quantity',
+ 'pkg_label',
+ );
+my @sort_fields = ( map '', @fields ); # should only ever sort by $col
+
+push @header, FS::UI::Web::cust_header($cust_fields);
+push @fields, \&FS::UI::Web::cust_fields;
+
+my $query = {
+ 'table' => 'cust_pkg',
+ 'addl_from' => FS::UI::Web::join_cust_main('cust_pkg', 'cust_pkg'),
+ 'hashref' => {
+ $col => { op => '!=', value => '' },
+ 'cancel' => '',
+ },
+ 'order_by' => "ORDER BY $col",
+};
+
+my $count_query =
+ "SELECT COUNT(*) FROM cust_pkg WHERE $col IS NOT NULL AND cancel IS NULL";
+
+my $pkg_link = sub {
+ my $self = shift;
+ my $frag = 'cust_pkg'. $self->pkgnum;
+ [ "${p}view/cust_main.cgi?custnum=".$self->custnum.
+ ";show=packages;fragment=$frag#cust_pkg",
+ 'pkgnum'
+ ];
+};
+
+my @links = ( '', ($pkg_link) x 3,
+ FS::UI::Web::cust_links() );
+
+my $date_color_sub = sub {
+ my $self = shift;
+ my $color;
+ my $interval = ($self->$col - time) / 86400;
+ if ( $interval > 30 ) {
+ $color = 'palegreen';
+ } elsif ( $interval > 0 ) {
+ $color = 'yellow';
+ } else {
+ $color = 'tomato';
+ }
+ "background-color: $color";
+};
+
+</%init>
+<& elements/search.html,
+ 'title' => $title,
+ 'name' => 'packages',
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'align' => 'rrrl'. FS::UI::Web::cust_aligns(),
+ 'links' => \@links,
+ 'cell_style' => [ $date_color_sub ],
+&>
+
diff --git a/httemplate/search/cust_pkg_churn.html b/httemplate/search/cust_pkg_churn.html
index 30962c996..4c7e7e8b2 100644
--- a/httemplate/search/cust_pkg_churn.html
+++ b/httemplate/search/cust_pkg_churn.html
@@ -18,7 +18,7 @@
emt('Susp.'),
emt('Changed'),
emt('Cancel'),
- #emt('Reason'), # hard to do this right
+ @reason_header,
FS::UI::Web::cust_header(
$cgi->param('cust_fields')
),
@@ -45,6 +45,7 @@
( map { time_or_blank($_) }
qw( setup last_bill bill susp change_date cancel ) ),
+ @reason_fields,
\&FS::UI::Web::cust_fields,
],
'sort_fields' => [
@@ -53,21 +54,25 @@
('') x 3, # can't use at all
# use the plain SQL column names
qw( setup last_bill bill susp change_date cancel ),
+ @reason_blank,
# cust_fields can take care of themselves
],
'color' => [
('') x 15,
+ @reason_blank,
FS::UI::Web::cust_colors(),
],
'style' => [ ('') x 15,
+ @reason_blank,
FS::UI::Web::cust_styles() ],
'size' => [ '', '', '', '', '-1' ],
- 'align' => 'rrlcccrrlrrrrrr'. FS::UI::Web::cust_aligns(). 'r',
+ 'align' => 'rrlcccrrlrrrrrr'.$reason_align. FS::UI::Web::cust_aligns(). 'r',
'links' => [
$link,
$link,
$link,
('') x 12,
+ @reason_blank,
( map { $_ ne 'Cust. Status' ? $clink : '' }
FS::UI::Web::cust_header(
$cgi->param('cust_fields')
@@ -184,4 +189,16 @@ sub time_or_blank {
};
}
+my (@reason_header,@reason_fields,@reason_blank);
+my $reason_align = '';
+if ($status eq 'cancel') {
+ push @reason_header, emt('Cancel Reason');
+ push @reason_fields, sub {
+ my $c = shift;
+ my $cust_pkg_reason = $c->last_cust_pkg_reason('cancel');
+ $cust_pkg_reason ? $cust_pkg_reason->reason->reason : '';
+ };
+ push @reason_blank, '';
+ $reason_align = 'l';
+}
</%init>
diff --git a/httemplate/search/cust_svc.html b/httemplate/search/cust_svc.html
index 3b770432e..b2826309a 100644
--- a/httemplate/search/cust_svc.html
+++ b/httemplate/search/cust_svc.html
@@ -1,50 +1,66 @@
<& elements/search.html,
'title' => emt('Service search results'),
- 'name' => emt('services'),
- 'query' => $sql_query,
- 'count_query' => $count_query,
- 'redirect' => $link,
- 'header' => [ emt('#'),
- emt('Service'),
- # package?
- FS::UI::Web::cust_header(),
- ],
- 'fields' => [ 'svcnum',
- sub {
- #$_[0]->svc. ': '. $_[0]->label;
- my($label, $value, $svcdb) = $_[0]->label;
- my $id = $_[0]->agent_svcid
- ? $_[0]->agent_svcid.': '
- : '';
- "$label: $id$value";
- },
- # package?
- \&FS::UI::Web::cust_fields,
- ],
- 'links' => [ $link,
- $link,
- # package?
- ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
- FS::UI::Web::cust_header()
- ),
- ],
- 'align' => 'rl'. FS::UI::Web::cust_aligns(),
- 'color' => [
- '',
- '',
- FS::UI::Web::cust_colors(),
- ],
- 'style' => [
- '',
- '',
- FS::UI::Web::cust_styles(),
- ],
+ 'name' => emt('services'),
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => [ emt('#'),
+ emt('Service'),
+ emt('Pkg. Status'),
+ # package?
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [ 'svcnum',
+ sub {
+ #$_[0]->svc. ': '. $_[0]->label;
+ my($label, $value, $svcdb) = $_[0]->label;
+ my $id = $_[0]->agent_svcid
+ ? $_[0]->agent_svcid.': '
+ : '';
+ "$label: $id$value";
+ },
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
+ # package?
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'links' => [ $link,
+ $link,
+ '', # pkg status
+ # package?
+ ( map { $_ ne 'Cust. Status' ? $link_cust : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rlr'. FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ 'b', # pkg status
+ FS::UI::Web::cust_styles(),
+ ],
&>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $sql_query;
my $orderby = 'ORDER BY cust_svc.svcnum'; #has to be ordered by something
@@ -83,6 +99,13 @@ if ( length( $cgi->param('search_svc') ) ) {
} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
push @extra_sql, "svcpart = $1";
+ if (defined($cgi->param('cancelled'))) {
+ if ($cgi->param('cancelled')) {
+ push @extra_sql, "cust_pkg.cancel IS NOT NULL";
+ } else {
+ push @extra_sql, "cust_pkg.cancel IS NULL";
+ }
+ }
} else {
errorpage("No search term specified");
diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html
index 7d7fb73e9..1b1be5f36 100755
--- a/httemplate/search/elements/cust_pay_or_refund.html
+++ b/httemplate/search/elements/cust_pay_or_refund.html
@@ -63,10 +63,20 @@ Examples:
sub { sprintf($money, $_[0]->$amount_field) },
],
'total_row' => [ '<B>Total</B>',
- sub { warn Dumper @_; sprintf("<B>$money</B>", $_[0]->$amount_field) },
+ sub { sprintf("<B>$money</B>", $_[0]->$amount_field) },
],
'show_combined' => 1,
&>
+<%shared>
+# canonicalize the payby subtype string to an SQL-quoted list
+my %cardtype_of = (
+ 'VisaMC' => q['VISA card', 'MasterCard'],
+ 'Amex' => q['American Express card'],
+ 'Discover' => q['Discover card'],
+ 'Maestro' => q['Switch', 'Solo', 'Laser'],
+ 'Tokenized' => q['Tokenized'],
+);
+</%shared>
<%init>
my %opt = @_;
@@ -91,29 +101,30 @@ my $title = '';
$title = 'Unapplied ' if $unapplied;
$title .= "\u$name_singular Search Results";
-my $link = '';
-if ( ( $curuser->access_right('View invoices') #remove in 2.5 (2.7?)
- || ($curuser->access_right('View payments') && $table =~ /^cust_pay/)
- || ($curuser->access_right('View refunds') && $table eq 'cust_refund')
- )
- && ! $opt{'disable_link'}
- )
-{
-
- my $key;
- my $q = '';
- if ( $table eq 'cust_pay_void' ) {
- $key = 'paynum';
- $q .= 'void=1;';
- } elsif ( $table eq /^cust_(\w+)$/ ) {
- $key = $1.'num';
- }
-
- if ( $key ) {
- $q .= "$key=";
- $link = [ "${p}view/$table.html?$q", $key ]
- }
-}
+###NOT USED???
+#my $link = '';
+#if ( ( $curuser->access_right('View invoices') #remove in 2.5 (2.7?)
+# || ($curuser->access_right('View payments') && $table =~ /^cust_pay/)
+# || ($curuser->access_right('View refunds') && $table eq 'cust_refund')
+# )
+# && ! $opt{'disable_link'}
+# )
+#{
+#
+# my $key;
+# my $q = '';
+# if ( $table eq 'cust_pay_void' ) {
+# $key = 'paynum';
+# $q .= 'void=1;';
+# } elsif ( $table eq /^cust_(\w+)$/ ) {
+# $key = $1.'num';
+# }
+#
+# if ( $key ) {
+# $q .= "$key=";
+# $link = [ "${p}view/$table.html?$q", $key ]
+# }
+#}
my $cust_link = sub {
my $cust_thing = shift;
@@ -166,12 +177,18 @@ if ( $opt{'pre_header'} ) {
push @sort_fields, @{ $opt{'pre_fields'} };
}
-my $sub_receipt = sub {
+my $sub_receipt = $opt{'disable_link'} ? '' : sub {
my $obj = shift;
my $objnum = $obj->primary_key . '=' . $obj->get($obj->primary_key);
+ my $table = $obj->table;
+ my $void = '';
+ if ($table eq 'cust_pay_void') {
+ $table = 'cust_pay';
+ $void = ';void=1';
+ }
include('/elements/popup_link_onclick.html',
- 'action' => $p.'view/cust_pay.html?link=popup;'.$objnum,
+ 'action' => $p.'view/'.$table.'.html?link=popup;'.$objnum.$void,
'actionlabel' => emt('Payment Receipt'),
);
};
@@ -191,10 +208,8 @@ if ($opt{'show_card_type'}) {
push @header, emt('Card Type');
$align .= 'r';
push @links, '';
- push @fields, sub {
- (($_[0]->payby eq 'CARD') && ($_[0]->paymask !~ /N\/A/)) ? cardtype($_[0]->paymask) : ''
- };
- push @sort_fields, '';
+ push @fields, 'paycardtype';
+ push @sort_fields, 'paycardtype';
}
if ( $unapplied ) {
@@ -211,6 +226,14 @@ push @links, '';
push @fields, sub { time2str('%b %d %Y', shift->_date ) };
push @sort_fields, '_date';
+if ($cgi->param('show_order_number')) {
+ push @header, emt('Order Number');
+ $align .= 'r';
+ push @links, '';
+ push @fields, 'order_number';
+ push @sort_fields, 'order_number';
+}
+
unless ( $opt{'disable_by'} ) {
push @header, emt('By');
$align .= 'c';
@@ -297,125 +320,23 @@ if ( $cgi->param('magic') ) {
if ( $cgi->param('payby') ) {
my @all_payby_search = ();
- foreach my $payby ( $cgi->param('payby') ) {
-
- $payby =~
- /^(CARD|CHEK|BILL|CASH|PPAL|APPL|ANRD|PREP|WIRE|WEST|IDTP|EDI|MCRD|MCHK)(-(VisaMC|Amex|Discover|Maestro|Tokenized))?$/
- or die "illegal payby $payby";
-
- my $payby_search = "$table.payby = '$1'";
-
- if ( $3 ) {
-
- my $cardtype = $3;
-
- my $similar_to = dbh->{Driver}->{Name} =~ /^mysql/i
- ? 'REGEXP' #doesn't behave exactly the same, but
- #should work for our patterns
- : 'SIMILAR TO';
-
- my $search;
- if ( $cardtype eq 'VisaMC' ) {
-
- #avoid posix regexes for portability
- $search =
- " ( ( substring($table.payinfo from 1 for 1) = '4' ".
- " AND substring($table.payinfo from 1 for 4) != '4936' ".
- " AND substring($table.payinfo from 1 for 6) ".
- " NOT $similar_to '49030[2-9]' ".
- " AND substring($table.payinfo from 1 for 6) ".
- " NOT $similar_to '49033[5-9]' ".
- " AND substring($table.payinfo from 1 for 6) ".
- " NOT $similar_to '49110[1-2]' ".
- " AND substring($table.payinfo from 1 for 6) ".
- " NOT $similar_to '49117[4-9]' ".
- " AND substring($table.payinfo from 1 for 6) ".
- " NOT $similar_to '49118[1-2]' ".
- " )".
- " OR substring($table.payinfo from 1 for 2) = '51' ".
- " OR substring($table.payinfo from 1 for 2) = '52' ".
- " OR substring($table.payinfo from 1 for 2) = '53' ".
- " OR substring($table.payinfo from 1 for 2) = '54' ".
- " OR substring($table.payinfo from 1 for 2) = '54' ".
- " OR substring($table.payinfo from 1 for 2) = '55' ".
-# " OR substring($table.payinfo from 1 for 2) = '36' ". #Diner's int'l was processed as Visa/MC inside US, now Discover
- " ) ";
-
- } elsif ( $cardtype eq 'Amex' ) {
-
- $search =
- " ( substring($table.payinfo from 1 for 2 ) = '34' ".
- " OR substring($table.payinfo from 1 for 2 ) = '37' ".
- " ) ";
-
- } elsif ( $cardtype eq 'Discover' ) {
-
- my $country = $conf->config('countrydefault') || 'US';
-
- $search =
- " ( substring($table.payinfo from 1 for 4 ) = '6011' ".
- " OR substring($table.payinfo from 1 for 2 ) = '65' ".
- " OR substring($table.payinfo from 1 for 3 ) = '300' ".
- " OR substring($table.payinfo from 1 for 3 ) = '301' ".
- " OR substring($table.payinfo from 1 for 3 ) = '302' ".
- " OR substring($table.payinfo from 1 for 3 ) = '303' ".
- " OR substring($table.payinfo from 1 for 3 ) = '304' ".
- " OR substring($table.payinfo from 1 for 3 ) = '305' ".
- " OR substring($table.payinfo from 1 for 4 ) = '3095' ".
- " OR substring($table.payinfo from 1 for 2 ) = '36' ".
- " OR substring($table.payinfo from 1 for 2 ) = '38' ".
- " OR substring($table.payinfo from 1 for 2 ) = '39' ".
- " OR substring($table.payinfo from 1 for 3 ) = '644' ".
- " OR substring($table.payinfo from 1 for 3 ) = '645' ".
- " OR substring($table.payinfo from 1 for 3 ) = '646' ".
- " OR substring($table.payinfo from 1 for 3 ) = '647' ".
- " OR substring($table.payinfo from 1 for 3 ) = '648' ".
- " OR substring($table.payinfo from 1 for 3 ) = '649' ".
- ( $country =~ /^(US|CA)$/
- ?" OR substring($table.payinfo from 1 for 4 ) = '3528' ". # JCB cards in the 3528-3589 range identified as Discover inside US/CA
- " OR substring($table.payinfo from 1 for 4 ) = '3529' ".
- " OR substring($table.payinfo from 1 for 3 ) = '353' ".
- " OR substring($table.payinfo from 1 for 3 ) = '354' ".
- " OR substring($table.payinfo from 1 for 3 ) = '355' ".
- " OR substring($table.payinfo from 1 for 3 ) = '356' ".
- " OR substring($table.payinfo from 1 for 3 ) = '357' ".
- " OR substring($table.payinfo from 1 for 3 ) = '358' "
- :""
- ).
- " OR substring($table.payinfo from 1 for 3 ) = '622' ". #China Union Pay processed as Discover outside CN
- " ) ";
-
- } elsif ( $cardtype eq 'Maestro' ) {
-
- $search =
- " ( substring($table.payinfo from 1 for 2 ) = '63' ".
- " OR substring($table.payinfo from 1 for 2 ) = '67' ".
- " OR substring($table.payinfo from 1 for 6 ) = '564182' ".
- " OR substring($table.payinfo from 1 for 4 ) = '4936' ".
- " OR substring($table.payinfo from 1 for 6 ) ".
- " $similar_to '49030[2-9]' ".
- " OR substring($table.payinfo from 1 for 6 ) ".
- " $similar_to '49033[5-9]' ".
- " OR substring($table.payinfo from 1 for 6 ) ".
- " $similar_to '49110[1-2]' ".
- " OR substring($table.payinfo from 1 for 6 ) ".
- " $similar_to '49117[4-9]' ".
- " OR substring($table.payinfo from 1 for 6 ) ".
- " $similar_to '49118[1-2]' ".
- " ) ";
-
- } elsif ( $cardtype eq 'Tokenized' ) {
-
- $search = " substring($table.payinfo from 1 for 2 ) = '99' ";
-
- } else {
- die "unknown card type $cardtype";
- }
-
- my $masksearch = $search;
- $masksearch =~ s/$table\.payinfo/$table.paymask/gi;
-
- $payby_search = "( $payby_search AND ( $search OR ( $table.paymask IS NOT NULL AND $masksearch ) ) )";
+ foreach my $payby_string ( $cgi->param('payby') ) {
+
+ my $payby_search;
+
+ my ($payby, $subtype) = split('-', $payby_string);
+ # make sure it exists and is a transaction type
+ if ( FS::payby->payment_payby2longname($payby) ) {
+ $payby_search = "$table.payby = " . dbh->quote($payby);
+ } else {
+ die "illegal payby $payby_string";
+ }
+
+ if ( $subtype ) {
+
+ my $in_cardtype = $cardtype_of{$subtype}
+ or die "unknown card type $subtype";
+ $payby_search .= " AND $table.paycardtype IN($in_cardtype)";
}
diff --git a/httemplate/search/elements/gmap.html b/httemplate/search/elements/gmap.html
index 8b070ebf9..b7d135dd6 100644
--- a/httemplate/search/elements/gmap.html
+++ b/httemplate/search/elements/gmap.html
@@ -1,5 +1,6 @@
<%args>
@features
+@overlays
</%args>
<%doc>
Generic Google Maps front end.
@@ -24,6 +25,14 @@ Generic Google Maps front end.
}
}, # end of feature
],
+ overlays => [
+ { url => 'https://localhost/freeside/view/sector_map-png.html?102',
+ west => -130.0,
+ east => -128.0,
+ south => 10.0,
+ north => 12.0,
+ }, # make a ground overlay
+ ],
&>
</%doc>
@@ -54,6 +63,7 @@ body { height: 100%; margin: 0px; padding: 0px }
<script type="text/javascript">
var data_geojson = <% encode_json($tree) %>;
+var data_overlays = <% encode_json(\@overlays) %>;
var baseStyle = {
clickable: true,
@@ -75,6 +85,7 @@ var featureStyle = function(feature) {
};
var map;
+var overlays = [];
function initMap() {
var canvas = $('#map_canvas');
map = new google.maps.Map(canvas[0], { zoom: 6 });
@@ -116,6 +127,15 @@ function initMap() {
}
}); // addListener()
+
+ data_overlays.forEach(function(x) {
+ var url = x.url;
+ delete x.url;
+ var overlay = new google.maps.GroundOverlay( url, x );
+ overlay.setMap(map);
+ overlay.setOpacity(0.4);
+ overlays.push(overlay);
+ });
}
$().ready( initMap );
diff --git a/httemplate/search/elements/grouped-search/core b/httemplate/search/elements/grouped-search/core
index ffa8cee39..3d38a8c7e 100644
--- a/httemplate/search/elements/grouped-search/core
+++ b/httemplate/search/elements/grouped-search/core
@@ -131,12 +131,14 @@ for my $i (0 .. scalar(@groups) - 1) {
}
if ( $opt{show_combined} ) {
- # set up group 0 as a combined view
- unshift @groups, $totals;
- unshift @group_labels, 'All ' . PL($opt{name_singular}) .
- ' (' . $totals->num_rows . ')';
- unshift @group_footers, []; # the total footer will suffice
- unshift @queries, $base_query->clone;
+ if ( @groups > 1 ) {
+ # set up group 0 as a combined view
+ unshift @groups, $totals;
+ unshift @group_labels, 'All ' . PL($opt{name_singular}) .
+ ' (' . $totals->num_rows . ')';
+ unshift @group_footers, []; # the total footer will suffice
+ unshift @queries, $base_query->clone;
+ }
}
my @total_footer;
diff --git a/httemplate/search/elements/grouped-search/html b/httemplate/search/elements/grouped-search/html
index df1471a52..293da338f 100644
--- a/httemplate/search/elements/grouped-search/html
+++ b/httemplate/search/elements/grouped-search/html
@@ -67,8 +67,10 @@ if ( $group->num_rows > scalar(@rows) ) {
# set up tab bar
my @menubar;
-for (my $i = 0; $i < $group_info->{num}; $i++) {
- push @menubar, $group_info->{group_labels}[$i], ";group=$i";
+if ($group_info->{num} > 1) {
+ for (my $i = 0; $i < $group_info->{num}; $i++) {
+ push @menubar, $group_info->{group_labels}[$i], ";group=$i";
+ }
}
# not enabled yet; if we need this at some point, enable it on a per-report
diff --git a/httemplate/search/elements/report_cust_pay_or_refund.html b/httemplate/search/elements/report_cust_pay_or_refund.html
index 730db68e8..806746a23 100644
--- a/httemplate/search/elements/report_cust_pay_or_refund.html
+++ b/httemplate/search/elements/report_cust_pay_or_refund.html
@@ -151,6 +151,12 @@ Examples:
'value' => 1,
&>
+ <& /elements/tr-checkbox.html,
+ 'label' => emt('Include order number'),
+ 'field' => 'show_order_number',
+ 'value' => 1,
+ &>
+
</TABLE>
% }
diff --git a/httemplate/search/elements/search.html b/httemplate/search/elements/search.html
index a279f5327..b6ee7b373 100644
--- a/httemplate/search/elements/search.html
+++ b/httemplate/search/elements/search.html
@@ -135,8 +135,11 @@ Example:
# sort, link & display properties for fields
- 'sort_fields' => [], #optional list of field names or SQL expressions for
- # sorts
+ 'sort_fields' => [], #optional list of field names or SQL expressions for sorts
+
+ 'order_by_sql' => { #to keep complex SQL expressions out of cgi order_by value,
+ 'fieldname' => 'sql snippet', # maps fields/sort_fields values to sql snippets
+ }
#listref - each item is the empty string,
# or a listref of link and method name to append,
@@ -406,6 +409,12 @@ $order_by = $cgi->param('order_by') if $cgi->param('order_by');
my $header = [ map { ref($_) ? $_->{'label'} : $_ } @{$opt{header}} ];
my $rows;
+my ($order_by_key,$order_by_desc) = ($order_by =~ /^\s*(.*?)(\s+DESC)?\s*$/i);
+$opt{'order_by_sql'} ||= {};
+$order_by_desc ||= '';
+$order_by = $opt{'order_by_sql'}{$order_by_key} . $order_by_desc
+ if $opt{'order_by_sql'}{$order_by_key};
+
if ( ref $query ) {
my @query;
if (ref($query) eq 'HASH') {
diff --git a/httemplate/search/log.html b/httemplate/search/log.html
index b607f505d..5b330f899 100644
--- a/httemplate/search/log.html
+++ b/httemplate/search/log.html
@@ -81,15 +81,15 @@ a:visited {text-decoration: none}
<TD>Level
<& /elements/select.html,
field => 'min_level',
- options => [ 0..7 ],
- labels => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+ options => [ &FS::Log::levelnums ],
+ labels => { &FS::Log::levelmap },
curr_value => $cgi->param('min_level'),
&>
to
<& /elements/select.html,
field => 'max_level',
- options => [ 0..7 ],
- labels => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 },
+ options => [ &FS::Log::levelnums ],
+ labels => { &FS::Log::levelmap },
curr_value => $cgi->param('max_level'),
&>
</TD>
@@ -101,6 +101,12 @@ a:visited {text-decoration: none}
labels => { map {$_, $_} @contexts },
curr_value => ($cgi->param('context') || ''),
&>
+ <BR><& /elements/checkbox.html,
+ 'field' => 'context_height',
+ 'postfix' => 'Only match most specific context',
+ 'value' => 1,
+ 'curr_value' => scalar($cgi->param('context_height')),
+ &>
</TD>
</TR>
<TR>
@@ -122,7 +128,7 @@ a:visited {text-decoration: none}
<%once>
my $date_sub = sub { time2str('%Y-%m-%d %T', $_[0]->_date) };
-my $level_sub = sub { $FS::Log::LEVELS[$_[0]->level] };
+my $level_sub = sub { $FS::Log::LEVELS{$_[0]->level} };
my $context_sub = sub {
my $log = shift;
@@ -185,18 +191,15 @@ my $object_link_sub = sub {
}
};
-my @colors = (
- '404040', #debug
- '0000aa', #info
- '00aa00', #notice
- 'aa0066', #warning
- '000000', #error
- 'aa0000', #critical
- 'ff0000', #alert
- 'ff0000', #emergency
+my %colors = (
+ 0 => '404040', #debug, gray
+ 1 => '000000', #info, black
+ 3 => '0000aa', #warning, blue
+ 4 => 'aa0066', #error, purple
+ 5 => 'ff0000', #critical, red
);
-my $color_sub = sub { $colors[ $_[0]->level ]; };
+my $color_sub = sub { $colors{ $_[0]->level }; };
my @contexts = ('', sort FS::log_context->contexts);
</%once>
@@ -206,15 +209,15 @@ die "access denied"
unless $curuser->access_right([ 'View system logs', 'Configuration' ]);
my @menubar = ();
-push @menubar, qq(<A HREF="${fsurl}browse/log_email.html" STYLE="text-decoration: underline;">Configure conditions for sending email when logging</A>),
+push @menubar, qq(<A HREF="${fsurl}browse/log_email.html" STYLE="text-decoration: underline;">Configure conditions for sending email when logging</A>);
$cgi->param('min_level', 0) unless defined($cgi->param('min_level'));
-$cgi->param('max_level', 7) unless defined($cgi->param('max_level'));
+$cgi->param('max_level', 5) unless defined($cgi->param('max_level'));
my %search = ();
$search{'date'} = [ FS::UI::Web::parse_beginning_ending($cgi) ];
$search{'level'} = [ $cgi->param('min_level'), $cgi->param('max_level') ];
-foreach my $param (qw(agentnum context tablename tablenum custnum message)) {
+foreach my $param (qw(agentnum context context_height tablename tablenum custnum message)) {
if ( $cgi->param($param) ) {
$search{$param} = $cgi->param($param);
}
diff --git a/httemplate/search/report_agent_credit_payment.html b/httemplate/search/report_agent_credit_payment.html
new file mode 100755
index 000000000..57fc6a79c
--- /dev/null
+++ b/httemplate/search/report_agent_credit_payment.html
@@ -0,0 +1,30 @@
+<& /elements/header.html, 'Package Agent Credits and Payments' &>
+
+<P>Shows agent commission credits, and payments applied to invoices for packages that triggered those credits.</P>
+
+<FORM ACTION="agent_credit_payment.html" METHOD="GET">
+
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <& /elements/tr-select-agent.html,
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'label' => 'Agent ',
+ 'disable_empty' => 0,
+ &>
+
+ <& /elements/tr-input-beginning_ending.html &>
+
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</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_bill_pay_pkg.html b/httemplate/search/report_cust_bill_pay_pkg.html
index 2347bab6d..bdcd1549e 100644
--- a/httemplate/search/report_cust_bill_pay_pkg.html
+++ b/httemplate/search/report_cust_bill_pay_pkg.html
@@ -41,6 +41,13 @@
field => 'paid',
&>
+ <& /elements/tr-checkbox.html,
+ 'label' => emt('Display order number'),
+ 'field' => 'show_order_number',
+ 'value' => 1,
+ 'cell_style' => 'font-weight: normal', #for consistency
+ &>
+
<!--
<TR>
<TD ALIGN="right"><INPUT TYPE="checkbox" NAME="nottax" VALUE="Y" onClick="nottax_changed(this)" onChange="nottax_change(thid)"></TD>
diff --git a/httemplate/search/report_cust_bill_void.html b/httemplate/search/report_cust_bill_void.html
index 91209aeba..65cce5fff 100644
--- a/httemplate/search/report_cust_bill_void.html
+++ b/httemplate/search/report_cust_bill_void.html
@@ -24,7 +24,7 @@
label => mt('Customer Class'),
field => 'cust_classnum',
multiple => 1,
- 'pre_options' => [ '' => emt('(none)') ],
+ 'pre_options' => [ '0' => emt('(none)') ],
'all_selected' => 1,
&>
diff --git a/httemplate/search/report_cust_event.html b/httemplate/search/report_cust_event.html
index 0dd98d479..7aa4ff9d7 100644
--- a/httemplate/search/report_cust_event.html
+++ b/httemplate/search/report_cust_event.html
@@ -5,7 +5,6 @@
%>
<FORM ACTION="cust_event.html" METHOD="GET">
- <INPUT TYPE="hidden" NAME="failed" VALUE="<% $cgi->param('failed') ? 1 : 0 %>">
<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
<TR>
@@ -15,7 +14,8 @@
<% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %>
<% include( '/elements/tr-select-cust_main-status.html',
- 'label' => 'Status'
+ 'label' => 'Customer status',
+ # this field is just called 'status'
)
%>
@@ -26,6 +26,36 @@
)
%>
+% if ( $cgi->param('failed') ) {
+ <& /elements/tr-fixed.html,
+ 'label' => 'Event status',
+ 'field' => 'event_status',
+ 'curr_value' => 'failed',
+ 'formatted_value' => 'Failed',
+ &>
+% } else {
+
+% # 'initial' is not on here, since nobody needs to see it. also,
+% # 'done_Y' = "done, and no_action is null, and statustext is null"
+% # 'done_S' = "done, and no_action is null, and statustext is not null"
+% # 'done_N' = "done, and no_action = 'Y'".
+ <& /elements/tr-select.html,
+ 'label' => 'Event status',
+ 'field' => 'event_status',
+ 'multiple' => 1,
+ 'all_selected' => 1,
+ 'size' => 5,
+ 'options' => [ qw( done_Y done_S done_N failed new locked ) ],
+ 'option_labels' => { done_Y => 'Completed normally',
+ done_S => 'Completed, with an error',
+ done_N => 'Completed, no action taken',
+ failed => 'Failed',
+ new => 'Not yet processed',
+ locked => 'Running',
+ },
+ &>
+% }
+
<% include( '/elements/tr-input-beginning_ending.html' ) %>
</TABLE>
diff --git a/httemplate/search/report_sqlradius_usage-custnum.html b/httemplate/search/report_sqlradius_usage-custnum.html
new file mode 100644
index 000000000..a71012dd4
--- /dev/null
+++ b/httemplate/search/report_sqlradius_usage-custnum.html
@@ -0,0 +1,71 @@
+<& /elements/header-popup.html, mt($title) &>
+
+<FORM ACTION="sqlradius_usage.html" METHOD="GET" TARGET="_top">
+
+<& /elements/hidden.html,
+ 'field' => 'custnum',
+ 'value' => $custnum,
+&>
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+% if ( scalar(@exports) == 1 ) {
+<tr><td>
+<& /elements/hidden.html,
+ 'field' => 'exportnum',
+ 'value' => $exports[0]->exportnum,
+&>
+</td></tr>
+% } else {
+<& /elements/tr-select-table.html,
+ 'label' => 'Export', # kind of non-indicative...
+ 'table' => 'part_export',
+ 'name_col' => 'label',
+ 'value_col' => 'exportnum',
+ 'records' => \@exports,
+ 'disable_empty' => 1,
+&>
+% }
+<& /elements/tr-input-beginning_ending.html &>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="<% mt('Get Report') |h %>">
+
+</FORM>
+
+<& /elements/footer.html &>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied"
+ unless $curuser->access_right('Usage: RADIUS sessions');
+ # yes?
+
+my $title = 'Data Usage Report';
+my $custnum;
+if ($cgi->keywords) {
+ ($custnum) = $cgi->keywords;
+} else {
+ $custnum = $cgi->param('custnum');
+}
+$custnum =~ /^(\d+)$/
+ or die "illegal custnum $custnum";
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+# get all exports that apply to this customer's services--should be fast, as
+# everything here is indexed
+my @exports = qsearch({
+ 'table' => 'part_export',
+ 'select' => 'DISTINCT part_export.*',
+ 'addl_from' => ' JOIN export_svc USING (exportnum)
+ JOIN cust_svc USING (svcpart)
+ JOIN cust_pkg USING (pkgnum) ',
+ 'extra_sql' => ' WHERE cust_pkg.custnum = '.$custnum,
+});
+@exports = grep { $_->can('usage_sessions') } @exports;
+
+</%init>
diff --git a/httemplate/search/report_sqlradius_usage.html b/httemplate/search/report_sqlradius_usage.html
index e818fb57d..89b60847b 100644
--- a/httemplate/search/report_sqlradius_usage.html
+++ b/httemplate/search/report_sqlradius_usage.html
@@ -1,3 +1,4 @@
+%# some overlap with report_sqlradius_usage_custnum.html
<& /elements/header.html, mt($title) &>
<FORM ACTION="sqlradius_usage.html" METHOD="GET">
diff --git a/httemplate/search/report_tax-xls.cgi b/httemplate/search/report_tax-xls.cgi
index c914d5adc..30b32e8d8 100755
--- a/httemplate/search/report_tax-xls.cgi
+++ b/httemplate/search/report_tax-xls.cgi
@@ -13,9 +13,7 @@ my %params = (
beginning => $beginning,
ending => $ending,
);
-$params{country} = $cgi->param('country');
$params{debug} = $DEBUG;
-$params{breakdown} = { map { $_ => 1 } $cgi->param('breakdown') };
my $agentname;
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
@@ -24,15 +22,38 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
$agentname = $agent->agentname;
}
-# allow anything in here; FS::Report::Tax will treat it as unsafe
-if ( length($cgi->param('taxname')) ) {
- $params{taxname} = $cgi->param('taxname');
+# credit date behavior: limit by the date of the credit application, or
+# the invoice?
+if ( $cgi->param('credit_date') eq 'cust_credit_bill' ) {
+ $params{credit_date} = 'cust_credit_bill';
} else {
- die "taxname required";
+ $params{credit_date} = 'cust_bill';
+}
+
+my $all = $cgi->param('all');
+my $report_class;
+
+if ( $all ) {
+ $report_class = 'FS::Report::Tax::All';
+} else {
+ $report_class = 'FS::Report::Tax::ByName';
+ $params{country} = $cgi->param('country');
+ $params{breakdown} = { map { $_ => 1 } $cgi->param('breakdown') };
+
+ # allow anything in here; FS::Report::Tax will treat it as unsafe
+ if ( length($cgi->param('taxname')) ) {
+ $params{taxname} = $cgi->param('taxname');
+ } else {
+ die "taxname required";
+ }
+}
+
+if ($DEBUG) {
+ warn "REPORT: $report_class\nPARAMS:\n".Dumper(\%params)."\n\n";
}
# generate the report
-my $report = FS::Report::Tax->report_internal(%params);
+my $report = $report_class->report(%params);
my @rows = $report->table; # array of hashrefs
my %pkgclass_name = map { $_->classnum, $_->classname } qsearch('pkg_class');
diff --git a/httemplate/search/report_tax.cgi b/httemplate/search/report_tax.cgi
index bbb3bc199..410fe4603 100644
--- a/httemplate/search/report_tax.cgi
+++ b/httemplate/search/report_tax.cgi
@@ -78,14 +78,6 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px }
% # cust_bill_pkg.cgi wants a list of specific taxnums (and package class)
% # cust_credit_bill_pkg.html wants a geographic scope (and package class)
% my $rowlink = ';taxnum=' . $row->{taxnums};
-% # DON'T EVER USE THIS
-% # my $rowregion = ';country=' . $cgi->param('country');
-% # foreach my $loc (qw(state county city district)) {
-% # if ( $row->{$loc} ) {
-% # $rowregion .= ";$loc=" . uri_escape($row->{$loc});
-% # }
-% # }
-% # and also the package class, if we're limiting package class
% if ( $params{breakdown}->{pkgclass} ) {
% $rowlink .= ';classnum=' . ($row->{pkgclass} || 0);
% # $rowregion .= ';classnum=' . ($row->{pkgclass} || 0);
@@ -96,7 +88,26 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px }
% }
<TR CLASS="row<% $rownum % 2 %>">
% # Row label
- <TD CLASS="rowhead"><% $row->{label} |h %></TD>
+% # Special: If this report is showing all taxes, link the row label to
+% # the detailed tax report for that taxname/country.
+ <TD CLASS="rowhead">
+% if ( $all ) {
+% my $newcgi = CGI->new($cgi);
+% $newcgi->delete('all');
+% $newcgi->param('country', $row->{country});
+% $newcgi->param('taxname', $row->{taxname});
+% $newcgi->param('breakdown', qw(city district));
+
+ <A HREF="<% encode_entities( $newcgi->self_url ) %>">
+ <% $row->{label} |h %>
+ </A>
+
+% } else { # on the per-taxname report, just show the label with no link
+
+ <% $row->{label} |h %>
+
+% }
+ </TD>
<TD>
% # Total sales
<A HREF="<% $saleslink . $rowlink %>">
@@ -167,7 +178,8 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px }
% } # foreach my $row
% # at the end of everything
</TBODY>
-% if ( $report->{out_sales} > 0 ) {
+% # the all-taxes report doesn't have "out of region"
+% if ( !$all and $report->{out_sales} > 0 ) {
<TBODY CLASS="total" STYLE="background-color: #cccccc; line-height: 3">
<TR>
<TD CLASS="rowhead">
@@ -175,7 +187,7 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px }
</TD>
<TD STYLE="text-align: right">
<A HREF="<% $saleslink %>;out=1;taxname=<% encode_entities($params{'taxname'}) %>">
- <% $money_sprintf->( $report->{out_sales } ) %>
+ <% $money_sprintf->( $report->{out_sales} ) %>
</A>
</TD>
<TD COLSPAN=0></TD>
@@ -254,7 +266,7 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px }
% $prev_row = $row;
% } # foreach my $row
% # "out of taxable region" for credits (there is a need for it)
-% if ( $report->{out_credit} > 0 ) {
+% if ( !$all and $report->{out_credit} > 0 ) {
% my $creditlink = "cust_credit_bill_pkg.html?out=1;$dateagentlink";
% if ( $params{'credit_date'} eq 'cust_credit_bill' ) {
% $creditlink =~ s/begin/credit_begin/;
@@ -268,7 +280,7 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px }
</TD>
<TD STYLE="text-align: right">
<A HREF="<% $creditlink %>">
- <% $money_sprintf->( $report->{out_credit } ) %>
+ <% $money_sprintf->( $report->{out_credit} ) %>
</A>
</TD>
<TD COLSPAN=0></TD>
@@ -295,33 +307,48 @@ my %params = (
beginning => $beginning,
ending => $ending,
);
-$params{country} = $cgi->param('country');
$params{debug} = $DEBUG;
-$params{breakdown} = { map { $_ => 1 } $cgi->param('breakdown') };
-
my $agentname;
+
+# filter by agentnum
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
my $agent = FS::agent->by_key($1) or die "unknown agentnum $1";
$params{agentnum} = $1;
$agentname = $agent->agentname;
}
-# allow anything in here; FS::Report::Tax will treat it as unsafe
-if ( length($cgi->param('taxname')) ) {
- $params{taxname} = $cgi->param('taxname');
-} else {
- die "taxname required";
-}
-
+# credit date behavior: limit by the date of the credit application, or
+# the invoice?
if ( $cgi->param('credit_date') eq 'cust_credit_bill' ) {
$params{credit_date} = 'cust_credit_bill';
} else {
$params{credit_date} = 'cust_bill';
}
-warn "PARAMS:\n".Dumper(\%params)."\n\n" if $DEBUG;
+my $all = $cgi->param('all');
+my $report_class;
+
+if ( $all ) {
+ # then show the master report, no country, no taxname, no breakdown
+ $report_class = 'FS::Report::Tax::All';
+} else {
+ $report_class = 'FS::Report::Tax::ByName';
+ $params{country} = $cgi->param('country');
+ $params{breakdown} = { map { $_ => 1 } $cgi->param('breakdown') };
+
+ # allow anything in here; FS::Report::Tax will treat it as unsafe
+ if ( length($cgi->param('taxname')) ) {
+ $params{taxname} = $cgi->param('taxname');
+ } else {
+ die "taxname required";
+ }
+}
+
+if ($DEBUG) {
+ warn "REPORT: $report_class\nPARAMS:\n".Dumper(\%params)."\n\n";
+}
-my $report = FS::Report::Tax->report_internal(%params);
+my $report = $report_class->report(%params);
my @rows = $report->table; # array of hashrefs
my $money_char = $conf->config('money_char') || '$';
diff --git a/httemplate/search/report_tax.html b/httemplate/search/report_tax.html
index 8d8d1084c..f920adbac 100755
--- a/httemplate/search/report_tax.html
+++ b/httemplate/search/report_tax.html
@@ -8,6 +8,20 @@
<& /elements/tr-input-beginning_ending.html &>
+ <tr>
+ <td></td>
+ <td colspan=2 style="font-weight: bold">
+ <& /elements/radio.html,
+ 'field' => 'all',
+ 'value' => 1,
+ 'curr_value' => 1,
+ &> All taxes
+ <& /elements/radio.html,
+ 'field' => 'all',
+ 'value' => 0,
+ &> A specific tax
+ </td>
+ </tr>
<& /elements/tr-select.html,
'label' => 'Country',
'field' => 'country',
@@ -49,6 +63,21 @@
</FORM>
+<script>
+$(document).ready(function() {
+ $('[name=all]').on('change', function(ev) {
+ // disable country/taxname/breakdown if 'all' = 1
+ if (this.checked) {
+ var disabled = (this.value == 1);
+ $('[name=country').prop('disabled', disabled);
+ $('[name=taxname').prop('disabled', disabled);
+ $('[name=breakdown').prop('disabled', disabled);
+ }
+ });
+ $('[name=all]').change();
+});
+</script>
+
<% include('/elements/footer.html') %>
<%init>
diff --git a/httemplate/search/report_unprovisioned_services.html b/httemplate/search/report_unprovisioned_services.html
index fe4d46bf7..54181bb20 100755
--- a/httemplate/search/report_unprovisioned_services.html
+++ b/httemplate/search/report_unprovisioned_services.html
@@ -13,6 +13,7 @@
'field' => 'svcpart',
'label' => 'Services',
'multiple' => 1,
+ 'size' => 20,
)
%>
diff --git a/httemplate/search/sector.html b/httemplate/search/sector.html
new file mode 100644
index 000000000..037df10ea
--- /dev/null
+++ b/httemplate/search/sector.html
@@ -0,0 +1,104 @@
+<& /elements/header.html, {
+ 'title' => 'Sector coverage maps',
+ }
+&>
+<style>
+ a.createmap {
+ font-weight: bold;
+ color: blue;
+ }
+ a.viewmap {
+ font-weight: bold;
+ color: green;
+ }
+ .grid th {
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-bottom: 2px;
+ border: none;
+ empty-cells: show;
+ font-size:90%;
+ border-bottom: 1px solid #999999;
+ }
+ .grid td {
+ padding-left: 3px;
+ padding-right: 3px;
+ padding-bottom: 2px;
+ border: none;
+ empty-cells: show;
+ }
+</style>
+<table class="grid" style="border-spacing: 0px">
+ <thead>
+ <tr>
+ <th>Tower / sector</th>
+ <th colspan=3>
+ </tr>
+ </thead>
+ <tbody>
+% my $row = 0;
+% foreach my $sector (@sectors) {
+% my $sectornum = $sector->sectornum;
+ <tr class="row<% $row % 2 %>">
+ <td>
+ <a href="<% $fsurl %>edit/tower.html?<% $sector->towernum |h %>">
+ <% $sector->description |h %>
+ </a>
+ </td>
+
+% my @need_fields = $sector->need_fields_for_coverage;
+% if ( @need_fields ) {
+ <td>Need fields:</td>
+ <td>
+ <% join('<br>', @need_fields) %>
+ </td>
+% } else {
+ <td colspan="2" style="text-align: center">
+% my $text = 'Create map';
+% if ( length($sector->image) > 0 ) {
+% $text = 'Reprocess';
+% }
+ <form name="create_<% $sectornum |h %>">
+ <input type="hidden" name="sectornum" value="<% $sectornum |h %>">
+ <& /elements/progress-init.html,
+ 'create_'.$sectornum,
+ [ 'sectornum' ],
+ $fsurl.'misc/sector-create_map.html',
+ { 'message' => 'Map generated',
+ 'url' => $cgi->self_url },
+ "sector$sectornum"
+ &>
+ <a class="createmap" href="#" onclick="sector<% $sectornum %>process()">
+ <% $text %>
+ </a>
+% }
+ </td>
+ <td>
+% if ( length($sector->image) > 0 ) {
+ <a class="viewmap" href="<% $fsurl %>search/svc_broadband-map.html?sectornum=<% $sectornum %>">
+ View map&mdash;<% $sector->margin %>dB margin
+ </a>
+% }
+ </td>
+ </tr>
+% $row++;
+% } # foreach $sector
+ </tbody>
+</table>
+<& /elements/footer.html &>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $query = {
+ table => 'tower_sector',
+ select => 'tower_sector.*,
+ tower.latitude, tower.longitude, tower.color, tower.towername',
+ extra_sql => ' WHERE tower.disabled is null',
+ addl_from => ' JOIN tower USING (towernum)',
+ order_by => ' ORDER BY towername, sectorname',
+};
+
+my @sectors = qsearch($query);
+</%init>
diff --git a/httemplate/search/sqlradius_usage.html b/httemplate/search/sqlradius_usage.html
index 29ef4c0e8..08f9b6ba1 100644
--- a/httemplate/search/sqlradius_usage.html
+++ b/httemplate/search/sqlradius_usage.html
@@ -39,6 +39,7 @@
@svc_fields,
@svc_usage,
],
+ 'order_by_sql' => $order_by_sql,
'links' => [ #( map { $_ ne 'Cust. Status' ? $link_cust : '' }
# FS::UI::Web::cust_header() ),
$link_cust,
@@ -59,8 +60,8 @@
my %opt = @_;
-die "access denied" unless
- $FS::CurrentUser::CurrentUser->access_right('List services');
+my $curuser = $FS::CurrentUser::CurrentUser;
+die "access denied" unless $curuser->access_right('List services');
my $title = 'Data Usage Report - ';
my $agentnum;
@@ -92,6 +93,40 @@ if ( $ending == 4294967295 ) {
$title .= time2str('%h %o %Y', $ending);
}
+# can also show a specific customer / service. the main query will handle
+# agent restrictions, but we need a list of the services to ask the export
+# for usage data.
+my ($cust_main, @svc_x);
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $1 },
+ 'extra_sql' => ' AND '. $curuser->agentnums_sql,
+ });
+ die "Customer not found!" unless $cust_main;
+ # then only report on this agent
+ $agentnum = $cust_main->agentnum;
+ @include_agents = ();
+ # and announce that we're doing it
+ $title .= ' - ' . $cust_main->name_short;
+
+ # yes, we'll query the database once for each service the customer has,
+ # even non-radacct'd services. probably less bad than a single query that
+ # pulls records for every service for every customer.
+ foreach my $cust_pkg ($cust_main->all_pkgs) {
+ foreach my $cust_svc ($cust_pkg->cust_svc) {
+ push @svc_x, $cust_svc->svc_x;
+ }
+ }
+}
+foreach ($cgi->param('svcnum')) {
+ if (/^(\d+)$/) {
+ my $cust_svc = FS::cust_svc->by_key($1)
+ or die "service #$1 not found."; # or continue?
+ push @svc_x, $cust_svc->svc_x;
+ }
+}
+
my $export;
my %usage_by_username;
if ( exists($opt{usage_by_username}) ) {
@@ -109,16 +144,28 @@ if ( exists($opt{usage_by_username}) ) {
or die "exportnum ".$export->exportnum." is type ".$export->exporttype.
", not sqlradius";
- my $usage = $export->usage_sessions( {
+ my %usage_param = (
stoptime_start => $beginning,
stoptime_end => $ending,
summarize => 1
- } );
- # arrayref of hashrefs of
+ );
+ # usage_sessions() returns an arrayref of hashrefs of
# (username, acctsessiontime, acctinputoctets, acctoutputoctets)
# (XXX needs to include 'realm' for sqlradius_withdomain)
- # rearrange to be indexed by username.
+ my $usage;
+ if ( @svc_x ) {
+ # then query once per service
+ $usage = [];
+ foreach my $svc ( @svc_x ) {
+ $usage_param{'svc'} = $svc;
+ push @$usage, @{ $export->usage_sessions(\%usage_param) };
+ }
+ } else {
+ # one query, get everyone's data
+ my $usage = $export->usage_sessions(\%usage_param);
+ }
+ # rearrange to be indexed by username.
foreach (@$usage) {
my $username = $_->{'username'};
my @row = (
@@ -171,10 +218,22 @@ my @svc_fields = @{ $svc_fields{$svcdb} };
my %search_hash = ( 'agentnum' => $agentnum,
'exportnum' => $export->exportnum );
+if ($cust_main) {
+ $search_hash{'custnum'} = $cust_main->custnum;
+}
+if (@svc_x) {
+ $search_hash{'svcnum'} = [ map { $_->get('svcnum') } @svc_x ];
+}
+
my $sql_query = $class->search(\%search_hash);
$sql_query->{'select'} .= ', part_pkg.pkg';
$sql_query->{'addl_from'} .= ' LEFT JOIN part_pkg USING (pkgpart)';
+if ( @svc_x ) {
+ my $svcnums = join(',', map { $_->get('svcnum') } @svc_x);
+ $sql_query->{'extra_sql'} .= ' AND svcnum IN('.$svcnums.')';
+}
+
my $link_svc = [ $p.'view/cust_svc.cgi?', 'svcnum' ];
my $link_cust = [ $p.'view/cust_main.cgi?', 'custnum' ];
@@ -182,9 +241,10 @@ my $link_cust = [ $p.'view/cust_main.cgi?', 'custnum' ];
# columns between the customer name and the usage fields
my $skip_cols = 1 + scalar(@svc_header);
+my $num_rows = FS::Record->scalar_sql($sql_query->{count_query});
my @footer = (
'',
- FS::Record->scalar_sql($sql_query->{count_query}) . ' services',
+ emt('[quant,_1,service]', $num_rows),
('') x $skip_cols,
map {
my $i = $_;
@@ -198,4 +258,23 @@ sub bytes_to_gb {
$_[0] ? sprintf('%.3f', $_[0] / (1024*1024*1024.0)) : '';
}
+my $conf = new FS::Conf;
+my $order_by_sql = {
+ 'name' => "CASE WHEN cust_main.company IS NOT NULL
+ AND cust_main.company != ''
+ THEN CONCAT(cust_main.company,' (',cust_main.last,', ',cust_main.first,')')
+ ELSE CONCAT(cust_main.last,', ',cust_main.first)
+ END",
+ 'display_custnum' => $conf->exists('cust_main-default_agent_custid')
+ ? "CASE WHEN cust_main.agent_custid IS NOT NULL
+ AND cust_main.agent_custid != ''
+ AND cust_main.agent_custid ". regexp_sql. " '^[0-9]+\$'
+ THEN CAST(cust_main.agent_custid AS BIGINT)
+ ELSE cust_main.custnum
+ END"
+ : "custnum",
+};
+
+#warn Dumper \%usage_by_username;
+
</%init>
diff --git a/httemplate/search/svc_acct.cgi b/httemplate/search/svc_acct.cgi
index 58764f881..ecf37b42e 100755
--- a/httemplate/search/svc_acct.cgi
+++ b/httemplate/search/svc_acct.cgi
@@ -59,6 +59,8 @@ my $curuser = $FS::CurrentUser::CurrentUser;
die "access denied" unless $curuser->access_right('List services');
+my %cust_pkg_cache;
+
my $link = [ "${p}view/svc_acct.cgi?", 'svcnum' ];
my $link_cust = sub {
my $svc_acct = shift;
@@ -130,6 +132,7 @@ for (qw( towernum sectornum )) {
my $timepermonth = '';
my $orderby = 'ORDER BY svcnum';
+my $addl_from = '';
if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
$search_hash{'unlinked'} = 1
@@ -281,6 +284,9 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
} elsif ( $cgi->param('svcpart') ) {
$orderby = "ORDER BY uid";
#$orderby = "ORDER BY svcnum";
+ if ( defined($cgi->param('cancelled')) ) {
+ $search_hash{'cancelled'} = $cgi->param('cancelled') ? 1 : 0;
+ }
} else {
$orderby = "ORDER BY uid";
@@ -347,6 +353,22 @@ foreach my $pkg_field ( @pkg_fields ) {
}
+push @header, emt('Pkg. Status');
+push @fields, sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status;
+};
+push @links, '';
+$align .= 'r';
+push @color, sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+};
+push @style, 'b';
+
push @header, FS::UI::Web::cust_header($cgi->param('cust_fields'));
push @fields, \&FS::UI::Web::cust_fields,
push @links, map { $_ ne 'Cust. Status' ? $link_cust : '' }
@@ -357,6 +379,7 @@ push @style, FS::UI::Web::cust_styles();
$search_hash{'order_by'} = $orderby;
$search_hash{'where'} = \@extra_sql;
+$search_hash{'addl_from'} = $addl_from;
my $sql_query = FS::svc_acct->search(\%search_hash);
my $count_query = delete($sql_query->{'count_query'});
diff --git a/httemplate/search/svc_broadband-map.html b/httemplate/search/svc_broadband-map.html
index 4c660b016..fe3c0950b 100755
--- a/httemplate/search/svc_broadband-map.html
+++ b/httemplate/search/svc_broadband-map.html
@@ -1,6 +1,6 @@
<& /elements/header.html, 'Broadband Search Results' &>
-<& elements/gmap.html, features => \@features &>
+<& elements/gmap.html, features => \@features, overlays => \@overlays &>
<& /elements/footer.html &>
<%init>
@@ -76,7 +76,6 @@ foreach my $svc_broadband (@rows) {
}
- my $tower = $towers{$towernum};
if ( $tower->latitude and $tower->longitude ) {
push @features,
{
@@ -146,6 +145,19 @@ foreach my $tower (values(%towers)) {
};
}
+my @overlays;
+foreach my $sector (values %sectors) {
+ if ( length($sector->image) > 0 ) {
+ my $o = {
+ url => $fsurl.'view/sector_map-png.cgi?' . $sector->sectornum
+ };
+ foreach (qw(south north west east)) {
+ $o->{$_} = $sector->get($_) + 0;
+ }
+ push @overlays, $o;
+ };
+};
+
</%init>
<%def .svc_broadband>
% my $svc = shift;
diff --git a/httemplate/search/svc_broadband.cgi b/httemplate/search/svc_broadband.cgi
index 6bf4f0850..0e52d5fb6 100755
--- a/httemplate/search/svc_broadband.cgi
+++ b/httemplate/search/svc_broadband.cgi
@@ -10,6 +10,8 @@
'Router',
@tower_header,
'IP Address',
+ @header_pkg,
+ emt('Pkg. Status'),
FS::UI::Web::cust_header($cgi->param('cust_fields')),
],
'fields' => [ 'svcnum',
@@ -20,25 +22,43 @@
},
@tower_fields,
'ip_addr',
+ @fields_pkg,
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
$link,
'', #$link_router,
(map '', @tower_fields),
- $link,
+ $link, # ip_addr
+ @blank_pkg,
+ '', # pkg status
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header($cgi->param('cust_fields'))
),
],
- 'align' => 'rll'.('r' x @tower_fields).'r'.
+ 'align' => 'rll'.('r' x @tower_fields).
+ 'r'. # ip_addr
+ $align_pkg.
+ 'r'. # pkg status
FS::UI::Web::cust_aligns(),
'color' => [
'',
'',
'',
(map '', @tower_fields),
- '',
+ '', # ip_addr
+ @blank_pkg,
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
@@ -46,7 +66,9 @@
'',
'',
(map '', @tower_fields),
- '',
+ '', # ip_addr
+ @blank_pkg,
+ 'b', # pkg status
FS::UI::Web::cust_styles(),
],
@@ -56,6 +78,8 @@
die "access denied" unless
$FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $conf = new FS::Conf;
my %search_hash;
@@ -68,6 +92,9 @@ if ( $cgi->param('magic') eq 'unlinked' ) {
foreach (qw(pkgpart routernum towernum sectornum)) {
$search_hash{$_} = [ $cgi->param($_) ] if $cgi->param($_);
}
+ if ( defined($cgi->param('cancelled')) ) {
+ $search_hash{'cancelled'} = $cgi->param('cancelled') ? 1 : 0;
+ }
}
if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
@@ -110,4 +137,25 @@ $html_init .= ' | ' .
$fsurl . 'search/svc_broadband-map.html?' . $cgi->query_string .
'">' . emt('View a map of these services') . '</a>';
+my (@header_pkg,@fields_pkg,@blank_pkg);
+my $align_pkg = '';
+#false laziness with search/svc_acct.cgi
+$cgi->param('cust_pkg_fields') =~ /^([\w\,]*)$/ or die "bad cust_pkg_fields";
+my @pkg_fields = split(',', $1);
+foreach my $pkg_field ( @pkg_fields ) {
+ ( my $header = ucfirst($pkg_field) ) =~ s/_/ /; #:/
+ push @header_pkg, $header;
+
+ #not the most efficient to do it every field, but this is of niche use. so far
+ push @fields_pkg, sub { my $svc_x = shift;
+ my $cust_pkg = $svc_x->cust_svc->cust_pkg or return '';
+ my $value = $cust_pkg->get($pkg_field);#closures help alot
+ $value ? time2str('%b %d %Y', $value ) : '';
+ };
+
+ push @blank_pkg, '';
+ $align_pkg .= 'c';
+}
+
+
</%init>
diff --git a/httemplate/search/svc_circuit.cgi b/httemplate/search/svc_circuit.cgi
index 8f05e0488..3a85375ef 100644
--- a/httemplate/search/svc_circuit.cgi
+++ b/httemplate/search/svc_circuit.cgi
@@ -10,6 +10,7 @@
'Termination',
'Circuit ID',
'IP Address',
+ emt('Pkg. Status'),
FS::UI::Web::cust_header($cgi->param('cust_fields')),
],
'fields' => [ 'svcnum',
@@ -18,6 +19,11 @@
'termination',
'circuit_id',
'ip_addr',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
@@ -26,15 +32,23 @@
'',
$link,
$link,
+ '', # pkg status
FS::UI::Web::cust_links($cgi->param('cust_fields')),
],
- 'align' => 'rlllll'. FS::UI::Web::cust_aligns(),
+ 'align' => 'rlllllr'. FS::UI::Web::cust_aligns(),
'color' => [
('') x 6,
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
('') x 6,
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -44,6 +58,8 @@
die "access denied" unless
$FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $conf = new FS::Conf;
my %search_hash;
@@ -56,6 +72,9 @@ if ( $cgi->param('magic') eq 'unlinked' ) {
foreach (qw(pkgpart routernum towernum sectornum)) {
$search_hash{$_} = [ $cgi->param($_) ] if $cgi->param($_);
}
+ if ( defined($cgi->param('cancelled')) ) {
+ $search_hash{'cancelled'} = $cgi->param('cancelled') ? 1 : 0;
+ }
}
my $query = FS::svc_circuit->search(\%search_hash);
diff --git a/httemplate/search/svc_dish.cgi b/httemplate/search/svc_dish.cgi
index 1f8cbc395..5c476085a 100755
--- a/httemplate/search/svc_dish.cgi
+++ b/httemplate/search/svc_dish.cgi
@@ -7,31 +7,45 @@
'header' => [ '#',
'Service',
'Account #',
+ emt('Pkg. Status'),
FS::UI::Web::cust_header(),
],
'fields' => [ 'svcnum',
'svc',
'acctnum',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
$link,
$link,
+ '', # pkg status
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header()
),
],
- 'align' => 'rll'. FS::UI::Web::cust_aligns(),
+ 'align' => 'rllr'. FS::UI::Web::cust_aligns(),
'color' => [
'',
'',
'',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
'',
'',
'',
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -41,6 +55,8 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
#my $conf = new FS::Conf;
my $orderby = 'ORDER BY svcnum';
@@ -56,6 +72,13 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
}
} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
push @extra_sql, "svcpart = $1";
+ if (defined($cgi->param('cancelled'))) {
+ if ($cgi->param('cancelled')) {
+ push @extra_sql, "cust_pkg.cancel IS NOT NULL";
+ } else {
+ push @extra_sql, "cust_pkg.cancel IS NULL";
+ }
+ }
}
my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
diff --git a/httemplate/search/svc_domain.cgi b/httemplate/search/svc_domain.cgi
index 56cfa30c8..23eeba6a7 100755
--- a/httemplate/search/svc_domain.cgi
+++ b/httemplate/search/svc_domain.cgi
@@ -7,31 +7,45 @@
'header' => [ '#',
'Service',
'Domain',
+ emt('Pkg. Status'),
FS::UI::Web::cust_header(),
],
'fields' => [ 'svcnum',
'svc',
'domain',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
$link,
$link,
+ '', # pkg status
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header()
),
],
- 'align' => 'rll'. FS::UI::Web::cust_aligns(),
+ 'align' => 'rllr'. FS::UI::Web::cust_aligns(),
'color' => [
'',
'',
'',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
'',
'',
'',
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -41,6 +55,8 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $conf = new FS::Conf;
my $orderby = 'ORDER BY svcnum';
@@ -58,6 +74,13 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
push @extra_sql, "svcpart = $1";
+ if (defined($cgi->param('cancelled'))) {
+ if ($cgi->param('cancelled')) {
+ push @extra_sql, "cust_pkg.cancel IS NOT NULL";
+ } else {
+ push @extra_sql, "cust_pkg.cancel IS NULL";
+ }
+ }
} else {
$cgi->param('domain') =~ /^([\w\-\.]+)$/;
$svc_domain{'domain'} = $1;
diff --git a/httemplate/search/svc_external.cgi b/httemplate/search/svc_external.cgi
index b282939a7..426ac1645 100755
--- a/httemplate/search/svc_external.cgi
+++ b/httemplate/search/svc_external.cgi
@@ -8,29 +8,42 @@
'Service',
( FS::Msgcat::_gettext('svc_external-id') || 'External ID' ),
( FS::Msgcat::_gettext('svc_external-title') || 'Title' ),
+ emt('Pkg. Status'),
FS::UI::Web::cust_header(),
],
'fields' => [ 'svcnum',
'svc',
'id',
'title',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
$link,
$link,
$link,
+ '', # pkg status
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header()
),
],
- 'align' => 'rlrr'.
+ 'align' => 'rlrrr'.
FS::UI::Web::cust_aligns(),
'color' => [
'',
'',
'',
'',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
@@ -38,6 +51,7 @@
'',
'',
'',
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -47,6 +61,8 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $conf = new FS::Conf;
my %svc_external;
@@ -69,6 +85,13 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
push @extra_sql, "svcpart = $1";
+ if (defined($cgi->param('cancelled'))) {
+ if ($cgi->param('cancelled')) {
+ push @extra_sql, "cust_pkg.cancel IS NOT NULL";
+ } else {
+ push @extra_sql, "cust_pkg.cancel IS NULL";
+ }
+ }
} elsif ( $cgi->param('title') =~ /^(.*)$/ ) {
diff --git a/httemplate/search/svc_fiber.html b/httemplate/search/svc_fiber.html
index 0cb735c96..a07c5f541 100644
--- a/httemplate/search/svc_fiber.html
+++ b/httemplate/search/svc_fiber.html
@@ -10,6 +10,7 @@
'ONT',
'Model',
'Serial',
+ emt('Pkg. Status'),
FS::UI::Web::cust_header($cgi->param('cust_fields')),
],
'fields' => [ 'svcnum',
@@ -20,6 +21,11 @@
'ont_id',
'ont_description',
'ont_serial',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
@@ -28,15 +34,23 @@
$link,
$link,
$link,
+ '', # pkg status
FS::UI::Web::cust_links($cgi->param('cust_fields')),
],
- 'align' => 'rlllll'. FS::UI::Web::cust_aligns(),
+ 'align' => 'rlllllr'. FS::UI::Web::cust_aligns(),
'color' => [
('') x 6,
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
('') x 6,
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -48,6 +62,8 @@ die "access denied" unless
'List services'
]);
+my %cust_pkg_cache;
+
my $conf = new FS::Conf;
my %search_hash;
@@ -58,6 +74,9 @@ if ( $cgi->param('magic') eq 'unlinked' ) {
ont_typenum oltnum shelf olt_port card vlan )) {
$search_hash{$_} = $cgi->param($_) if defined($cgi->param($_));
}
+ if ( defined($cgi->param('cancelled')) ) {
+ $search_hash{'cancelled'} = $cgi->param('cancelled') ? 1 : 0;
+ }
}
my $query = FS::svc_fiber->search(\%search_hash);
diff --git a/httemplate/search/svc_forward.cgi b/httemplate/search/svc_forward.cgi
index 6a23bb3bb..c9b6012a7 100755
--- a/httemplate/search/svc_forward.cgi
+++ b/httemplate/search/svc_forward.cgi
@@ -8,28 +8,41 @@
'Service',
'Mail to',
'Forwards to',
+ emt('Pkg. Status'),
FS::UI::Web::cust_header(),
],
'fields' => [ 'svcnum',
'svc',
$format_src,
$format_dst,
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
$link,
$link_src,
$link_dst,
+ '', # pkg status
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header()
),
],
- 'align' => 'rlll'. FS::UI::Web::cust_aligns(),
+ 'align' => 'rlllr'. FS::UI::Web::cust_aligns(),
'color' => [
'',
'',
'',
'',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
@@ -37,6 +50,7 @@
'',
'',
'',
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -46,6 +60,8 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $conf = new FS::Conf;
my $orderby = 'ORDER BY svcnum';
@@ -62,6 +78,13 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
push @extra_sql, "svcpart = $1";
+ if (defined($cgi->param('cancelled'))) {
+ if ($cgi->param('cancelled')) {
+ push @extra_sql, "cust_pkg.cancel IS NOT NULL";
+ } else {
+ push @extra_sql, "cust_pkg.cancel IS NULL";
+ }
+ }
}
my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
diff --git a/httemplate/search/svc_hardware.cgi b/httemplate/search/svc_hardware.cgi
index 93fc2c391..c41cc5a6e 100644
--- a/httemplate/search/svc_hardware.cgi
+++ b/httemplate/search/svc_hardware.cgi
@@ -12,6 +12,7 @@
'Hardware addr.',
'IP addr.',
'Smartcard',
+ emt('Pkg. Status'),
FS::UI::Web::cust_header(),
],
'fields' => [ 'svcnum',
@@ -22,24 +23,39 @@
'display_hw_addr',
'ip_addr',
'smartcard',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ ($link_svc) x 8,
+ '', # pkg status
( map { $_ ne 'Cust. Status' ?
$link_cust : '' }
FS::UI::Web::cust_header() )
],
- 'align' => 'rlllllll' . FS::UI::Web::cust_aligns(),
+ 'align' => 'rlllllllr' . FS::UI::Web::cust_aligns(),
'color' => [ ('') x 8,
- FS::UI::Web::cust_colors() ],
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
+ FS::UI::Web::cust_colors() ],
'style' => [ $svc_cancel_style, ('') x 7,
- FS::UI::Web::cust_styles() ],
+ 'b',
+ FS::UI::Web::cust_styles() ],
&>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $addl_from = '
LEFT JOIN cust_svc USING ( svcnum )
LEFT JOIN part_svc USING ( svcpart )
@@ -93,6 +109,13 @@ if ( $cgi->param('typenum') =~ /^(\d+)$/ ) {
if ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
push @extra_sql, "cust_svc.svcpart = $1";
+ if (defined($cgi->param('cancelled'))) {
+ if ($cgi->param('cancelled')) {
+ push @extra_sql, "cust_pkg.cancel IS NOT NULL";
+ } else {
+ push @extra_sql, "cust_pkg.cancel IS NULL";
+ }
+ }
}
my ($orderby) = $cgi->param('orderby') =~ /^(\w+( ASC| DESC)?)$/i;
diff --git a/httemplate/search/svc_phone.cgi b/httemplate/search/svc_phone.cgi
index f3a056475..4c0b65487 100644
--- a/httemplate/search/svc_phone.cgi
+++ b/httemplate/search/svc_phone.cgi
@@ -9,6 +9,7 @@
'Country code',
'Phone number',
@header,
+ emt('Pkg. Status'),
FS::UI::Web::cust_header($cgi->param('cust_fields')),
],
'fields' => [ 'svcnum',
@@ -16,6 +17,11 @@
'countrycode',
'phonenum',
@fields,
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
@@ -23,12 +29,14 @@
$link,
$link,
( map '', @header ),
+ '', # pkg status
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header($cgi->param('cust_fields'))
),
],
'align' => 'rlrr'.
join('', map 'r', @header).
+ 'r'.
FS::UI::Web::cust_aligns(),
'color' => [
'',
@@ -36,6 +44,12 @@
'',
'',
( map '', @header ),
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
@@ -44,6 +58,7 @@
'',
'',
( map '', @header ),
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -53,6 +68,8 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
my $conf = new FS::Conf;
my @select = ();
@@ -132,6 +149,9 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
$search_hash{'svcpart'} = [ $1 ];
+ if ( defined($cgi->param('cancelled')) ) {
+ $search_hash{'cancelled'} = $cgi->param('cancelled') ? 1 : 0;
+ }
} else {
$cgi->param('phonenum') =~ /^([\d\- ]+)$/;
my $phonenum = $1;
diff --git a/httemplate/search/svc_www.cgi b/httemplate/search/svc_www.cgi
index 7410262e8..6e9ba928e 100755
--- a/httemplate/search/svc_www.cgi
+++ b/httemplate/search/svc_www.cgi
@@ -8,6 +8,7 @@
'Service',
'Zone',
'User',
+ emt('Pkg. Status'),
FS::UI::Web::cust_header(),
],
'fields' => [ 'svcnum',
@@ -20,22 +21,34 @@
? $svc_acct->email
: '';
},
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ $cust_pkg_cache{$_[0]->svcnum}->ucfirst_status
+ },
\&FS::UI::Web::cust_fields,
],
'links' => [ $link,
$link,
'',
$ulink,
+ '', # pkg status
( map { $_ ne 'Cust. Status' ? $link_cust : '' }
FS::UI::Web::cust_header()
),
],
- 'align' => 'rlll'. FS::UI::Web::cust_aligns(),
+ 'align' => 'rlllr'. FS::UI::Web::cust_aligns(),
'color' => [
'',
'',
'',
'',
+ sub {
+ $cust_pkg_cache{$_[0]->svcnum} ||= $_[0]->cust_svc->cust_pkg;
+ return '' unless $cust_pkg_cache{$_[0]->svcnum};
+ my $c = FS::cust_pkg::statuscolors;
+ $c->{$cust_pkg_cache{$_[0]->svcnum}->status };
+ }, # pkg status
FS::UI::Web::cust_colors(),
],
'style' => [
@@ -43,6 +56,7 @@
'',
'',
'',
+ 'b',
FS::UI::Web::cust_styles(),
],
@@ -52,6 +66,8 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List services');
+my %cust_pkg_cache;
+
#my $conf = new FS::Conf;
my $orderby = 'ORDER BY svcnum';
@@ -68,6 +84,13 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
push @extra_sql, "svcpart = $1";
+ if (defined($cgi->param('cancelled'))) {
+ if ($cgi->param('cancelled')) {
+ push @extra_sql, "cust_pkg.cancel IS NOT NULL";
+ } else {
+ push @extra_sql, "cust_pkg.cancel IS NULL";
+ }
+ }
}
my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
diff --git a/httemplate/search/tax_sales.cgi b/httemplate/search/tax_sales.cgi
index 4b28c934a..91abd1bd3 100644
--- a/httemplate/search/tax_sales.cgi
+++ b/httemplate/search/tax_sales.cgi
@@ -113,7 +113,7 @@ while ($countdate < $enddate) {
# run a report for each tax name
foreach my $taxname (@taxnames) {
$params{'taxname'} = $taxname;
- my $report = FS::Report::Tax->report_internal(%params);
+ my $report = FS::Report::Tax::ByName->report(%params);
# extract totals from report, kinda awkward
my $pkgclass = ''; # this will get more complicated if we breakdown by pkgclass
diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi
index cf0c3190a..d9525750c 100755
--- a/httemplate/view/cust_bill.cgi
+++ b/httemplate/view/cust_bill.cgi
@@ -95,8 +95,21 @@ function areyousure(href, message) {
% }
% if ( $curuser->access_right('Resend invoices') ) {
+ <A HREF="<% $p %>misc/send-invoice.cgi?method=print;<% $link %>"><% mt('Print this invoice') |h %></A>
+% }
+
+% if ( $conf->exists('support-key')
+% && $curuser->access_right('Print and mail invoices')
+% )
+% {
+ | <& /elements/popup_link.html,
+ 'action' => $p."misc/post_fsinc-invoice.cgi?$link",
+ 'label' => 'Print and mail this invoice online',
+ 'actionlabel' => 'Invoice printing and mailing',
+ &>
+% }
- <A HREF="<% $p %>misc/send-invoice.cgi?method=print;<% $link %>"><% mt('Re-print this invoice') |h %></A>
+% if ( $curuser->access_right('Resend invoices') ) {
% 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>
@@ -106,8 +119,11 @@ function areyousure(href, message) {
| <A HREF="<% $p %>misc/send-invoice.cgi?method=fax;<% $link %>"><% mt('Re-fax this invoice') |h %></A>
% }
- <BR><BR>
+% }
+% if ( $curuser->access_right('Resend invoices')
+% || $curuser->access_right('Print and mail invoices') ) {
+ <BR><BR>
% }
% my $br = 0;
diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi
index 3cd7d2bb3..c5ee9203c 100755
--- a/httemplate/view/cust_main.cgi
+++ b/httemplate/view/cust_main.cgi
@@ -225,16 +225,19 @@ my $cust_main = qsearchs( {
});
die "Customer not found!" unless $cust_main;
-my $title = mt("Customer").' #'. $cust_main->display_custnum. ': '.
- encode_entities($cust_main->name);
+my $title = mt("Customer").' #'. $cust_main->display_custnum. ': ';
+my $title_noescape = $title. encode_entities($cust_main->name);
+$title .= $cust_main->name;
if ( $curuser->num_agents ) {
- $title = encode_entities($cust_main->agent->agent). " $title";
+ $title_noescape =
+ encode_entities($cust_main->agent->agent). " $title_noescape";
+ $title = $cust_main->agent->agent. " $title";
}
my $status = $cust_main->status_label;
$status .= ' (Cancelled)' if $cust_main->is_status_delay_cancel;
-my $title_noescape = $title. ' (<B><FONT COLOR="#'. $cust_main->statuscolor. '">'. $status. '</FONT></B>)';
+$title_noescape .= ' (<B><FONT COLOR="#'. $cust_main->statuscolor. '">'. $status. '</FONT></B>)';
$title .= " ($status)";
#false laziness w/pref/pref.html and Conf.pm (cust_main-default_view)
@@ -259,6 +262,15 @@ $views{$conf->config('cust_main-custom_title') || emt('Custom')} = 'custom'
my %viewname = reverse %views;
my $view = $cgi->param('show') || $curuser->default_customer_view;
+
+if ($view eq 'last') {
+ # something took us away from the page and is now bouncing back
+ $view = get_page_pref('last_view', $custnum);
+} else {
+ # remember which view is open so we _can_ bounce back
+ set_page_pref('last_view', $custnum, $view);
+}
+
$view = 'basics' if $view eq 'jumbo';
my $ie_compat = $conf->config('ie-compatibility_mode');
diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html
index 08b4323a8..894b2dfc0 100644
--- a/httemplate/view/cust_main/billing.html
+++ b/httemplate/view/cust_main/billing.html
@@ -26,79 +26,12 @@
% # customer base, and compare it to a graph of the overhead for generating this
% # information. (and optimize it better, we could get it more from SQL)
% if ( $cust_main->num_ncancelled_pkgs < 54 ) {
-% my $sth = dbh->prepare("
-% SELECT DISTINCT freq FROM cust_pkg LEFT JOIN part_pkg USING (pkgpart)
-% WHERE freq IS NOT NULL AND freq != '0'
-% AND ( cancel IS NULL OR cancel = 0 )
-% AND custnum = ?
-% ") or die $DBI::errstr;
-%
-% $sth->execute($cust_main->custnum) or die $sth->errstr;
-
-% #not really a numeric sort because freqs can actually be all sorts of things
-% # but good enough for the 99% cases of ordering monthly quarterly annually
-% my @freqs = sort { $a <=> $b } map { $_->[0] } @{ $sth->fetchall_arrayref };
-%
-% foreach my $freq (@freqs) {
-% my @cust_pkg = qsearch({
-% 'table' => 'cust_pkg',
-% 'addl_from' => 'LEFT JOIN part_pkg USING (pkgpart)',
-% 'hashref' => { 'custnum' => $cust_main->custnum, },
-% 'extra_sql' => 'AND ( cancel IS NULL OR cancel = 0 )
-% AND freq = '. dbh->quote($freq),
-% 'order_by' => 'ORDER BY COALESCE(start_date,0), pkgnum', # to ensure old pkgs come before change_to_pkg
-% }) or next;
-%
-% my $freq_pretty = $cust_pkg[0]->part_pkg->freq_pretty;
-%
-% my $amount = 0;
-% my $skip_pkg = {};
-% foreach my $cust_pkg (@cust_pkg) {
-% my $part_pkg = $cust_pkg->part_pkg;
-% next if $cust_pkg->susp
-% && ! $cust_pkg->option('suspend_bill')
-% && ( ! $part_pkg->option('suspend_bill')
-% || $cust_pkg->option('no_suspend_bill')
-% );
-%
-% #pkg change handling
-% next if $skip_pkg->{$cust_pkg->pkgnum};
-% if ($cust_pkg->change_to_pkgnum) {
-% #if change is on or before next bill date, use new pkg
-% next if $cust_pkg->expire <= $cust_pkg->bill;
-% #if change is after next bill date, use old (this) pkg
-% $skip_pkg->{$cust_pkg->change_to_pkgnum} = 1;
-% }
-%
-% my $pkg_amount = 0;
-%
-% #add recurring amounts for this package and its billing add-ons
-% foreach my $l_part_pkg ( $part_pkg->self_and_bill_linked ) {
-% $pkg_amount += $l_part_pkg->base_recur($cust_pkg);
-% }
-%
-% #subtract amounts for any active discounts
-% #(there should only be one at the moment, otherwise this makes no sense)
-% foreach my $cust_pkg_discount ( $cust_pkg->cust_pkg_discount_active ) {
-% my $discount = $cust_pkg_discount->discount;
-% #and only one of these for each
-% $pkg_amount -= $discount->amount;
-% $pkg_amount -= $amount * $discount->percent/100;
-% }
-%
-% $pkg_amount *= ( $cust_pkg->quantity || 1 );
-%
-% $amount += $pkg_amount;
-%
-% }
-
+% foreach my $freq_info ($cust_main->display_recurring) {
<TR>
- <TH ALIGN="right"><% emt( ucfirst($freq_pretty). ' recurring' ) %></TH>
- <TD><% $money_char. sprintf('%.2f', $amount) %></TD>
- </TD>
+ <TH ALIGN="right"><% emt( ucfirst($freq_info->{'freq_pretty'}). ' recurring' ) %></TH>
+ <TD><% $money_char. sprintf('%.2f', $freq_info->{'amount'}) %></TD>
</TR>
% }
-
% }
% if ( $conf->exists('cust_main-select-prorate_day') ) {
diff --git a/httemplate/view/cust_main/change_history.html b/httemplate/view/cust_main/change_history.html
index 09c1d6c6c..d46a4ffde 100644
--- a/httemplate/view/cust_main/change_history.html
+++ b/httemplate/view/cust_main/change_history.html
@@ -51,20 +51,25 @@ tie my %tables, 'Tie::IxHash',
my $pkg_join = "JOIN cust_pkg USING ( pkgnum )";
my $svc_join = "JOIN cust_svc USING ( svcnum ) $pkg_join";
+my @svc_tables = qw(
+ svc_acct
+ svc_domain
+ svc_www
+ svc_forward
+ svc_broadband
+ svc_external
+ svc_phone
+ svc_cable
+);
+
my %table_join = (
- 'svc_acct' => $svc_join,
'radius_usergroup' => $svc_join,
- 'svc_domain' => $svc_join,
- 'svc_www' => $svc_join,
- 'svc_forward' => $svc_join,
- 'svc_broadband' => $svc_join,
- 'svc_external' => $svc_join,
- 'svc_phone' => $svc_join,
- 'svc_cable' => $svc_join,
'phone_device' => $svc_join,
'cust_pkg_discount'=> $pkg_join,
);
+%table_join = (%table_join, map { $_ => $svc_join } @svc_tables);
+
# cust_main
# cust_main_invoice
@@ -134,6 +139,7 @@ my $newer_than = int( time - $years * 31556736 ); #60*60*24*365.24
local($FS::Record::nowarn_classload) = 1;
+my %foundsvcs;
foreach my $table ( keys %tables ) {
my @items = qsearch({
'table' => "h_$table",
@@ -141,9 +147,51 @@ foreach my $table ( keys %tables ) {
'hashref' => { 'history_date' => { op=>'>=', value=>$newer_than }, },
'extra_sql' => ' AND custnum = '. $cust_main->custnum,
});
+ %foundsvcs = (%foundsvcs, map { $_->svcnum => 1 } @items)
+ if $table =~ /^svc/;
+ push @history, @items;
+}
+
+### Load svcs that are no longer linked (cust_svc has been deleted)
+
+# get list of all svcs for this customer from h_cust_svc
+# could also load svcdb here by joining to part_svc on svcpart,
+# but it would spoil database optimizations on this lookup
+my @svcnumobj = qsearch({
+ 'select' => 'DISTINCT svcnum',
+ 'hashref' => { 'history_date' => { op=>'>=', value=>$newer_than } },
+ 'table' => 'h_cust_svc',
+ 'addl_from' => 'JOIN cust_pkg USING (pkgnum)',
+ 'extra_sql' => ' AND custnum = '. $cust_main->custnum,
+});
+
+# now grab those svcs explicitly
+foreach my $svcnum ( map { $_->get('svcnum') } @svcnumobj ) {
+ # can skip the services we already found
+ next if $foundsvcs{$svcnum};
+ # grab any given h_cust_svc record for this svcnum to get svcdb
+ my $svcdbobj = qsearchs({
+ 'select' => 'svcdb',
+ 'hashref' => { svcnum => $svcnum },
+ 'table' => 'h_cust_svc',
+ 'addl_from' => 'JOIN part_svc USING (svcpart)',
+ 'extra_sql' => 'LIMIT 1',
+ });
+ unless ($svcdbobj && $svcdbobj->get('svcdb')) {
+ # don't think this ever happens, but just in case, don't break history page over it
+ warn "Could not load svcdb for svcnum $svcnum";
+ next;
+ }
+ my @items = qsearch({
+ 'table' => "h_".$svcdbobj->get('svcdb'),
+ 'hashref' => { 'history_date' => { op=>'>=', value=>$newer_than },
+ 'svcnum' => $svcnum },
+ });
push @history, @items;
}
+## legacy history
+
my @legacy_items = qsearch({
'table' => 'legacy_cust_history',
'hashref' => { 'history_date' => { op=>'>=', value=>$newer_than }, },
diff --git a/httemplate/view/cust_main/menu.html b/httemplate/view/cust_main/menu.html
index ab2d69d34..7c7303bc9 100644
--- a/httemplate/view/cust_main/menu.html
+++ b/httemplate/view/cust_main/menu.html
@@ -131,8 +131,6 @@
</style>
-<& /elements/one_time_charge_link.html, form_only=>1 &>
-
<ul id="customer_menu">
% foreach my $submenu (@processed_menu) {
<li>
@@ -358,12 +356,7 @@ my @menu = (
{
# it's just a popup, but there's some freaky CCH tax stuff in it
label => 'One-time charge',
- content => sub {
- include( '/elements/one_time_charge_link.html',
- custnum => shift->custnum,
- no_form => 1,
- );
- },
+ url => "edit/quick-charge.html?custnum=$custnum",
acl => 'One-time charge',
},
{
@@ -398,8 +391,17 @@ my @menu = (
url => "search/report_svc_acct.html?custnum=$custnum",
},
{
+ label => 'View data usage',
+ popup => "search/report_sqlradius_usage-custnum.html?$custnum",
+ acl => 'Usage: RADIUS sessions',
+ actionlabel => 'Data usage report',
+ width => 480,
+ height => 245,
+ },
+ {
label => 'View CDRs',
url => "search/report_cdr.html?custnum=$custnum",
+ # XXX should have a condition that the customer has any CDR packages
},
],
[
diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html
index e167e2625..0a5305ed5 100644
--- a/httemplate/view/cust_main/packages/package.html
+++ b/httemplate/view/cust_main/packages/package.html
@@ -350,14 +350,8 @@ sub pkg_change_link {
sub onetime_change_link {
my $cust_pkg = shift;
my $pkgnum = $cust_pkg->pkgnum;
- include( '/elements/popup_link-cust_pkg.html',
- 'action' => $p. "edit/quick-charge.html?change_pkgnum=$pkgnum",
- 'label' => emt('Modify one-time charge'),
- 'actionlabel' => emt('Modify'),
- 'cust_pkg' => $cust_pkg,
- 'width' => 690,
- 'height' => 440,
- );
+ '<A HREF="' . $fsurl . "edit/quick-charge.html?change_pkgnum=$pkgnum" .
+ '">' . emt('Modify one-time charge') . '</A>'
}
sub pkg_change_location_link {
diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html
index 39055f49a..a23a4c2fe 100644
--- a/httemplate/view/cust_main/packages/services.html
+++ b/httemplate/view/cust_main/packages/services.html
@@ -105,9 +105,25 @@ function clearhint_search_cust_svc(obj, str) {
</TD>
</TR>
-% }
+% }
+
+% } #foreach part_svc
+
+% if ($cust_pkg->get('cancel')) {
+% foreach my $svc (
+% $cust_pkg->uncancel_svc_summary('summarize_size' => $opt{'cust_pkg-large_pkg_size'}, 'no_test_reprovision' => 1)
+% ) {
+ <TR>
+ <TD ALIGN="right" VALIGN="top"><% $svc->{'svc'} |h %></TD>
+ <TD STYLE="padding-bottom:0px; font-style: italic">
+ <% $svc->{'num_cust_svc'}
+ ? $svc->{'num_cust_svc'} . ' ' . emt('services in history')
+ : (defined($svc->{'label'}) ? $svc->{'label'} : emt('(cannot load svc label)')) |h %>
+ </TD>
+ </TR>
+% }
+% }
-% }
</TABLE>
</TD>
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index 001c12876..3f629e12f 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -104,6 +104,14 @@
<TR>
<TD COLSPAN=<%$opt{colspan}%>>
<FONT SIZE=-1>
+% if ( !$cust_pkg->change_to_pkgnum # because on a technical level, change won't propagate,
+% # and there's not really a use case worth making that work
+% and $part_pkg->freq # technically possible to have contract_end w/o freq, but nonsensical
+% and $curuser->access_right('Change package contract end date')
+% ) {
+ (&nbsp;<% pkg_change_contract_end_link($cust_pkg) %>&nbsp;)
+ <BR>
+% }
% if ( $cust_pkg->change_to_pkgnum ) {
% # then you can modify the package change
% if ( $curuser->access_right('Change customer package') ) {
@@ -188,21 +196,6 @@
<% pkg_status_row_if($cust_pkg, emt('Start billing'), 'start_date', %opt) %>
<% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
-% if ( !$opt{no_links}
-% and !$change_from
-% and !$supplemental # can be changed from its main package
-% and $curuser->access_right('Change package start date') )
-% {
-
- <TR>
- <TD COLSPAN=<%$opt{colspan}%>>
- <FONT SIZE=-1>
- (&nbsp;<% pkg_change_start_link($cust_pkg) %>&nbsp;)
- </FONT>
- </TD>
- </TR>
-% }
-
% }
%
% } else { #setup
@@ -286,6 +279,28 @@
<TR>
<TD COLSPAN=<%$opt{colspan}%>>
<FONT SIZE=-1>
+
+% #change date links
+% if ( !$change_from and !$supplemental ) {
+% my $has_date_links = 0;
+% if ( !$cust_pkg->get('setup')
+% and $curuser->access_right('Change package start date')
+% ) {
+ (&nbsp;<% pkg_change_start_link($cust_pkg) %>&nbsp;)
+% $has_date_links = 1;
+% }
+% if ( !$cust_pkg->change_to_pkgnum # because on a technical level, change won't propagate,
+% # and there's not really a use case worth making that work
+% and $curuser->access_right('Change package contract end date')
+% ) {
+ (&nbsp;<% pkg_change_contract_end_link($cust_pkg) %>&nbsp;)
+% $has_date_links = 1;
+% }
+% if ($has_date_links) {
+ <BR>
+% }
+% }
+
% # action links
% if ( $change_from ) {
% # nothing
@@ -715,6 +730,8 @@ sub pkg_uncancel_link {
'actionlabel' => emt('Un-cancel'),
#'color' => #?
'cust_pkg' => shift,
+ 'width' => 960,
+ 'height' => 740,
)
}
@@ -743,7 +760,7 @@ sub pkg_change_later_link {
sub pkg_change_start_link {
my $cust_pkg = shift;
include( '/elements/popup_link-cust_pkg.html',
- 'action' => $p . 'misc/change_pkg_start.html',
+ 'action' => $p . 'misc/change_pkg_date.html?field=start_date',
'label' => emt('Set start date'),
'actionlabel' => emt('Set start of billing for'),
'cust_pkg' => $cust_pkg,
@@ -752,6 +769,18 @@ sub pkg_change_start_link {
)
}
+sub pkg_change_contract_end_link {
+ my $cust_pkg = shift;
+ include( '/elements/popup_link-cust_pkg.html',
+ 'action' => $p . 'misc/change_pkg_date.html?field=contract_end',
+ 'label' => emt('Set contract end'),
+ 'actionlabel' => emt('Set contract end for'),
+ 'cust_pkg' => $cust_pkg,
+ 'width' => 510,
+ 'height' => 310,
+ )
+}
+
sub svc_recharge_link {
include( '/elements/popup_link-cust_svc.html',
'action' => $p. 'misc/recharge_svc.html',
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index 08d468104..9c18fbc15 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -136,29 +136,43 @@
% if ( $item->{'balance_forward'} ) {
<& .balance_forward_row, $item->{'balance'}, $item->{'date'} &>
-% }
+% }
%} # foreach $item
+% if ( $old_history ) {
+<& .hide_history_row, $old_history++ &>
+% }
+
</TABLE>
</TD>
</TR>
</TABLE>
+<SCRIPT SRC="<% $fsurl %>elements/page_pref.js"></SCRIPT>
<SCRIPT TYPE="text/javascript">
-function show_history () {
- //alert('showing history!');
-
+function show_history(show) { // but don't update pref
var balance_forward_row = document.getElementById('balance_forward_row');
- balance_forward_row.style.display = 'none';
+ balance_forward_row.style.display = show ? 'none' : '';
for ( var i = 0; i < <% $old_history %>; i++ ) {
var oldRow = document.getElementById('old_history'+i);
- oldRow.style.display = '';
+ oldRow.style.display = show ? '' : 'none';
}
+}
+
+function update_show_history (show) {
+ show = show ? 1 : 0;
+ show_history(show);
+ // update user pref (blind post, don't care about the output here)
+ set_page_pref('expand_old_history', '<% $custnum %>', show);
}
+$().ready(function() {
+ show_history(<% get_page_pref('expand_old_history', $custnum) %>);
+});
+
</SCRIPT>
<%def .balance_forward_row>
% my( $b, $date ) = @_;
@@ -171,7 +185,7 @@ function show_history () {
<TD CLASS="grid" BGCOLOR="#dddddd">
<I><% mt("Starting balance on [_1]", time2str($date_format, $date) ) |h %></I>
- (<A HREF="javascript:void(0);" onClick="show_history();"><% mt('show prior history') |h %></A>)
+ (<A HREF="javascript:void(0);" onClick="update_show_history(true);"><% mt('show prior history') |h %></A>)
</TD>
<TD CLASS="grid" BGCOLOR="#dddddd"></TD>
@@ -182,6 +196,17 @@ function show_history () {
</TR>
</%def>
+<%def .hide_history_row>
+% my $num = shift;
+ <TR ID="old_history<% $num %>" STYLE="display: none">
+ <TD CLASS="grid" BGCOLOR="#dddddd"></TD>
+ <TD CLASS="grid" BGCOLOR="#dddddd">
+ <I>(<A HREF="#" onclick="update_show_history(false)"><% mt('hide prior history') |h %></A>)</I>
+ </TD>
+ <TD CLASS="grid" BGCOLOR="#dddddd" COLSPAN=5></TD>
+ </TR>
+</%def>
+
<%shared>
my $conf = new FS::Conf;
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
diff --git a/httemplate/view/cust_main/payment_history/pending_payment.html b/httemplate/view/cust_main/payment_history/pending_payment.html
index 31149231b..cf7ef7c08 100644
--- a/httemplate/view/cust_main/payment_history/pending_payment.html
+++ b/httemplate/view/cust_main/payment_history/pending_payment.html
@@ -12,6 +12,7 @@ my %statusaction = (
'new' => 'delete',
'thirdparty' => 'delete',
'pending' => 'complete',
+ 'authorized' => 'complete',
'captured' => 'capture',
);
diff --git a/httemplate/view/cust_pay.html b/httemplate/view/cust_pay.html
index b34a90818..ba4cfdafc 100644
--- a/httemplate/view/cust_pay.html
+++ b/httemplate/view/cust_pay.html
@@ -91,13 +91,14 @@
<TD BGCOLOR="#FFFFFF"><B><% $cust_pay->auth %></B></TD>
</TR>
-% if ( $cust_pay->order_number ) {
- <TR>
- <TD ALIGN="right"><% mt('Order #') |h %></TD>
- <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->order_number %></B></TD>
- </TR>
-% }
+% }
+% # API allows setting this for any payby
+% if ( $cust_pay->order_number ) {
+ <TR>
+ <TD ALIGN="right"><% mt('Order #') |h %></TD>
+ <TD BGCOLOR="#FFFFFF"><B><% $cust_pay->order_number %></B></TD>
+ </TR>
% }
% if ( $cust_pay->payby eq 'CASH' && $cust_pay->payinfo ) {
diff --git a/httemplate/view/elements/svc_edit_link.html b/httemplate/view/elements/svc_edit_link.html
index 3ff2f58b6..2de5ecf9c 100644
--- a/httemplate/view/elements/svc_edit_link.html
+++ b/httemplate/view/elements/svc_edit_link.html
@@ -1,18 +1,18 @@
-% if ( $cancel_date ) {
-<I><% mt("Canceled [_1]", time2str('%b %o %Y', $cancel_date) ) |h %></I>
-% } else {
<SCRIPT>
function areyousure_delete() {
if (confirm(<% mt('Permanently delete this service?') |js_string %>) == true)
window.location.href = '<% $cancel_url %>';
}
</SCRIPT>
+% if ( $cancel_date ) {
+| <I><% mt("Canceled [_1]", time2str('%b %o %Y', $cancel_date) ) |h %></I>
+% } else {
% if ( $curuser->access_right('Provision customer service') ) {
| <A HREF="<% $edit_url %>"><% mt("Edit this [_1]", $label) |h %></A>
% }
-% if ( $curuser->access_right('Unprovision customer service') ) {
+% }
+% if ( $curuser->access_right('Unprovision customer service') ) {
| <A HREF="javascript:areyousure_delete()"><% mt('Unprovision this Service') |h %></A>
-% }
% }
<& /elements/manage_device_link.html,
'svc' => $svc_x,
diff --git a/httemplate/view/quotation.html b/httemplate/view/quotation.html
index 5646fb973..58d398ca2 100755
--- a/httemplate/view/quotation.html
+++ b/httemplate/view/quotation.html
@@ -23,9 +23,13 @@ function areyousure(href, message) {
% if ( $curuser->access_right('One-time charge') ) {
<% $inrow ? ' | ' : '' %>
- <& /elements/one_time_charge_link.html,
- map { $_ => $quotation->$_ } qw( quotationnum custnum prospectnum )
- &>
+% my $query = 'quotationnum=' . $quotation->get('quotationnum');
+% if ($quotation->custnum) {
+% $query .= ';custnum=' . $quotation->custnum;
+% } else {
+% $query .= ';prospectnum=' . $quotation->prospectnum;
+% }
+ <A HREF="<% $fsurl . 'edit/quick-charge.html?' . $query %>"><% emt('One-time charge') %></A>
% $inrow++;
% }
@@ -63,7 +67,9 @@ function areyousure(href, message) {
<BR><BR>
% if ( $curuser->access_right('New customer') && $quotation->quotation_pkg ) {
+% # if we end up with more than one option, combine these links and add an interstitial screen
<A HREF="<%$p%>edit/process/quotation_convert.html?quotationnum=<% $quotation->quotationnum %>">Place order</A>
+ | <A HREF="<%$p%>edit/process/quotation_convert.html?quotationnum=<% $quotation->quotationnum %>&onhold=1">Order on hold</A>
<BR><BR>
% }
diff --git a/httemplate/view/sector_map-png.cgi b/httemplate/view/sector_map-png.cgi
new file mode 100644
index 000000000..7e7e799a8
--- /dev/null
+++ b/httemplate/view/sector_map-png.cgi
@@ -0,0 +1,8 @@
+<%init>
+my ($sectornum) = $cgi->keywords;
+my $sector = FS::tower_sector->by_key($sectornum);
+if ( $sector and length($sector->image) > 0 ) {
+ http_header('Content-Type', 'image/png');
+ $m->print($sector->image);
+}
+</%init>
diff --git a/httemplate/view/svc_acct/basics.html b/httemplate/view/svc_acct/basics.html
index 1b7d56b23..5532c22a5 100644
--- a/httemplate/view/svc_acct/basics.html
+++ b/httemplate/view/svc_acct/basics.html
@@ -1,18 +1,18 @@
<% &ntable("#cccccc") %><TR><TD><% &ntable("#cccccc",2) %>
-<& /view/elements/tr.html, label=>mt('Service'), value=>$part_svc->svc &>
+<& /view/elements/tr.html, label=> $part_svc->part_svc_column('svc')->columnlabel || 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 &>
+<& /view/elements/tr.html, label=> $part_svc->part_svc_column('username')->columnlabel || mt('Username'), value=>$svc_acct->username &>
+<& /view/elements/tr.html, label=> $part_svc->part_svc_column('domsvc')->columnlabel || mt('Domain'), value=>$domain &>
% if ( $opt{'communigate'} ) {
<& /view/elements/tr.html, label=>mt('Aliases'), value=>$svc_acct->cgp_aliases &>
%}
% if ( $svc_acct->pbxsvc ) {
- <& /view/elements/tr.html, label=>mt('PBX'), value=>$svc_acct->pbx_title &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('pbxsvc')->columnlabel || mt('PBX'), value=>$svc_acct->pbx_title &>
%}
% my $show_pw = '';
@@ -38,7 +38,7 @@
% # show nothing
% } else {
<TR>
- <TD ALIGN="right"><% mt('Password') %></TD>
+ <TD ALIGN="right"><% $psc->columnlabel || mt('Password') %></TD>
<TD STYLE="background-color: #ffffff; white-space: nowrap">
<% $show_pw %>
% my $curuser = $FS::CurrentUser::CurrentUser;
@@ -58,12 +58,12 @@
% }
% if ( $conf->exists('security_phrase') ) {
- <& /view/elements/tr.html, label=>mt('Security phrase'), value=>$svc_acct->sec_phrase &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('sec_phrase')->columnlabel || mt('Security phrase'), value=>$svc_acct->sec_phrase &>
% }
% if ( $svc_acct->popnum ) {
% my $svc_acct_pop = qsearchs('svc_acct_pop',{'popnum'=>$svc_acct->popnum});
- <& /view/elements/tr.html, label=>mt('Access number'), value=>$svc_acct_pop->text &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('popnum')->columnlabel || mt('Access number'), value=>$svc_acct_pop->text &>
% }
% if ( $svc_acct->sectornum && $conf->exists('svc_acct-tower_sector') ) {
@@ -72,7 +72,7 @@
% ? '<A HREF="http://'. $tower_sector->ip_addr. '">'
% : '';
<& /view/elements/tr.html,
- label => mt('Tower sector'),
+ label => $part_svc->part_svc_column('sectornum')->columnlabel || mt('Tower sector'),
value => $link. $tower_sector->description. ($link ? '</A>' : ''),
&>
% }
@@ -83,28 +83,28 @@
&>
% if ($svc_acct->uid ne '') {
- <& /view/elements/tr.html, label=>mt('UID'), value=>$svc_acct->uid &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('uid')->columnlabel || mt('UID'), value=>$svc_acct->uid &>
% }
% if ($svc_acct->gid ne '') {
- <& /view/elements/tr.html, label=>mt('GID'), value=>$svc_acct->gid &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('gid')->columnlabel || mt('GID'), value=>$svc_acct->gid &>
% }
% if ($svc_acct->finger ne '') {
- <& /view/elements/tr.html, label=>mt('Real Name'), value=>$svc_acct->finger &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('finger')->columnlabel || mt('Real Name'), value=>$svc_acct->finger &>
% }
% if ($svc_acct->dir ne '') {
- <& /view/elements/tr.html, label=>mt('Home directory'), value=>$svc_acct->dir &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('dir')->columnlabel || mt('Home directory'), value=>$svc_acct->dir &>
% }
% if ($svc_acct->shell ne '') {
- <& /view/elements/tr.html, label=>mt('Shell'), value=>$svc_acct->shell &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('shell')->columnlabel || mt('Shell'), value=>$svc_acct->shell &>
% }
% if ($svc_acct->quota ne '' && ! $opt{'communigate'} ) {
- <& /view/elements/tr.html, label=>mt('Quota'), value=>$svc_acct->quota &>
+ <& /view/elements/tr.html, label=> $part_svc->part_svc_column('quota')->columnlabel || mt('Quota'), value=>$svc_acct->quota &>
% } elsif ( $opt{'communigate'} ) {
@@ -133,7 +133,7 @@ sub slipip {
% if ($svc_acct->slipip) {
<& /view/elements/tr.html,
- label=>mt('IP address'),
+ label=> $part_svc->part_svc_column('slipip')->columnlabel || mt('IP address'),
value=> slipip($svc_acct)
&>
% }
@@ -156,7 +156,7 @@ sub slipip {
&>
% }
-<& /view/elements/tr.html, label=>mt('RADIUS groups'),
+<& /view/elements/tr.html, label=> $part_svc->part_svc_column('usergroup')->columnlabel || mt('RADIUS groups'),
value=>join('<BR>', $svc_acct->radius_groups('long_description')) &>
<& router.html, 'svc_acct' => $svc_acct &>