summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate')
-rw-r--r--httemplate/browse/acct_snarf.html78
-rw-r--r--httemplate/browse/cgp_rule.html3
-rw-r--r--httemplate/browse/msg_template.html2
-rw-r--r--httemplate/browse/part_event.html2
-rwxr-xr-xhttemplate/browse/part_pkg.cgi59
-rw-r--r--httemplate/docs/credits.html1
-rwxr-xr-xhttemplate/edit/REAL_cust_pkg.cgi10
-rw-r--r--httemplate/edit/access_user.html18
-rw-r--r--httemplate/edit/acct_snarf.html50
-rw-r--r--httemplate/edit/bulk-cust_pkg.html60
-rw-r--r--httemplate/edit/cgp_rule-redirect_all.html52
-rw-r--r--httemplate/edit/cgp_rule-vacation.html45
-rwxr-xr-xhttemplate/edit/cust_main.cgi15
-rw-r--r--httemplate/edit/cust_main/billing.html14
-rw-r--r--httemplate/edit/cust_main/bottomfixup.js253
-rw-r--r--httemplate/edit/cust_main/contact.html6
-rw-r--r--httemplate/edit/cust_main/first_pkg.html34
-rw-r--r--httemplate/edit/cust_main/first_pkg/select-part_pkg.html4
-rw-r--r--httemplate/edit/cust_main/top_misc.html32
-rwxr-xr-xhttemplate/edit/cust_pay.cgi16
-rw-r--r--httemplate/edit/cust_pay_pending.html29
-rwxr-xr-xhttemplate/edit/cust_refund.cgi14
-rw-r--r--httemplate/edit/domain_record.html53
-rw-r--r--httemplate/edit/elements/edit.html6
-rw-r--r--httemplate/edit/msg_template.html21
-rw-r--r--httemplate/edit/part_export.cgi20
-rwxr-xr-xhttemplate/edit/part_pkg.cgi72
-rwxr-xr-xhttemplate/edit/part_svc.cgi12
-rwxr-xr-xhttemplate/edit/process/REAL_cust_pkg.cgi2
-rw-r--r--httemplate/edit/process/access_user.html10
-rw-r--r--httemplate/edit/process/acct_snarf.html20
-rw-r--r--httemplate/edit/process/bulk-cust_pkg.cgi9
-rw-r--r--httemplate/edit/process/cgp_rule-redirect_all.html24
-rw-r--r--httemplate/edit/process/cgp_rule-simplified.html53
-rw-r--r--httemplate/edit/process/cgp_rule-vacation.html29
-rwxr-xr-xhttemplate/edit/process/cust_main.cgi9
-rwxr-xr-xhttemplate/edit/process/cust_pay.cgi12
-rwxr-xr-xhttemplate/edit/process/cust_refund.cgi15
-rwxr-xr-xhttemplate/edit/process/domain_record.cgi19
-rwxr-xr-xhttemplate/edit/process/part_pkg.cgi8
-rw-r--r--httemplate/edit/process/prospect_main.html7
-rw-r--r--httemplate/edit/process/quick-cust_pkg.cgi6
-rw-r--r--httemplate/edit/process/rate_time.cgi3
-rwxr-xr-xhttemplate/edit/process/svc_acct.cgi2
-rw-r--r--httemplate/edit/process/svc_domain-defaultrecords.cgi18
-rw-r--r--httemplate/edit/prospect_main-ocr.html86
-rw-r--r--httemplate/edit/prospect_main-upload.html7
-rw-r--r--httemplate/edit/prospect_main.html44
-rw-r--r--httemplate/edit/rate_time.cgi4
-rwxr-xr-xhttemplate/edit/svc_acct.cgi222
-rw-r--r--httemplate/edit/svc_acct/communigate.html249
-rwxr-xr-xhttemplate/edit/svc_domain.cgi272
-rw-r--r--httemplate/edit/svc_domain/communigate-acct_defaults.html223
-rw-r--r--httemplate/edit/svc_domain/communigate-basics.html82
-rw-r--r--httemplate/elements/city.html2
-rw-r--r--httemplate/elements/contact.html55
-rw-r--r--httemplate/elements/customer-table.html13
-rw-r--r--httemplate/elements/email-link.html16
-rw-r--r--httemplate/elements/freeside.css53
-rw-r--r--httemplate/elements/header.html5
-rw-r--r--httemplate/elements/input-date-field.html50
-rw-r--r--httemplate/elements/menu.html23
-rw-r--r--httemplate/elements/popup_link.html2
-rw-r--r--httemplate/elements/search-cust_main.html5
-rw-r--r--httemplate/elements/select-discount_term.html32
-rw-r--r--httemplate/elements/select-month_year.html2
-rw-r--r--httemplate/elements/select-state.html2
-rw-r--r--httemplate/elements/select-terms.html2
-rw-r--r--httemplate/elements/select-user.html21
-rw-r--r--httemplate/elements/standardize_locations.html18
-rw-r--r--httemplate/elements/standardize_locations.js278
-rw-r--r--httemplate/elements/table-grid.html7
-rw-r--r--httemplate/elements/tr-pkg_svc.html2
-rw-r--r--httemplate/elements/tr-select-cust_tag.html3
-rw-r--r--httemplate/elements/tr-select-discount_term.html25
-rw-r--r--httemplate/elements/xmlhttp.html6
-rw-r--r--httemplate/graph/money_time.cgi2
-rw-r--r--httemplate/misc/batch-cust_pay.html98
-rw-r--r--httemplate/misc/cancel_cust.html42
-rwxr-xr-xhttemplate/misc/change_pkg.cgi12
-rw-r--r--httemplate/misc/choose_tax_location.html90
-rwxr-xr-xhttemplate/misc/cust_main-cancel.cgi31
-rw-r--r--httemplate/misc/cust_main-import.cgi17
-rw-r--r--httemplate/misc/cust_main-import_charges.cgi63
-rwxr-xr-xhttemplate/misc/cust_main-merge.html40
-rw-r--r--httemplate/misc/cust_main_note-import.cgi8
-rw-r--r--httemplate/misc/cust_main_note-import.html12
-rw-r--r--httemplate/misc/cust_pkg-import.html150
-rw-r--r--httemplate/misc/custom_link_proxy.cgi24
-rwxr-xr-xhttemplate/misc/delete-domain_record.cgi2
-rw-r--r--httemplate/misc/email-customers.html109
-rw-r--r--httemplate/misc/merge_cust.html72
-rw-r--r--httemplate/misc/order_pkg.html60
-rw-r--r--httemplate/misc/payment.cgi5
-rw-r--r--httemplate/misc/process/batch-cust_pay.cgi34
-rw-r--r--httemplate/misc/process/cust_main-import_charges.cgi3
-rw-r--r--httemplate/misc/process/cust_main_note-import.cgi41
-rw-r--r--httemplate/misc/process/cust_pay-import.cgi2
-rw-r--r--httemplate/misc/process/cust_pkg-import.html10
-rwxr-xr-xhttemplate/misc/process/delete-customer.cgi2
-rw-r--r--httemplate/misc/process/email-customers.html2
-rw-r--r--httemplate/misc/process/payment.cgi26
-rwxr-xr-xhttemplate/misc/timeworked.html24
-rwxr-xr-xhttemplate/misc/unprovision.cgi32
-rw-r--r--httemplate/misc/xmlhttp-cust_main-address_standardize.html1
-rw-r--r--httemplate/misc/xmlhttp-cust_main-censustract.html21
-rw-r--r--httemplate/misc/xmlhttp-cust_main-discount_terms.cgi24
-rw-r--r--httemplate/misc/xmlhttp-cust_main-search.cgi8
-rwxr-xr-xhttemplate/search/477.html8
-rwxr-xr-xhttemplate/search/477partIA_detail.html65
-rwxr-xr-xhttemplate/search/477partIA_summary.html9
-rwxr-xr-xhttemplate/search/477partVI_census.html (renamed from httemplate/search/477partVI.html)16
-rw-r--r--httemplate/search/cdr.html50
-rwxr-xr-xhttemplate/search/cust_bill.html12
-rw-r--r--httemplate/search/cust_bill_pkg.cgi21
-rw-r--r--httemplate/search/cust_bill_pkg_discount.html30
-rwxr-xr-xhttemplate/search/cust_credit.html148
-rwxr-xr-xhttemplate/search/cust_main.cgi10
-rwxr-xr-xhttemplate/search/cust_main.html12
-rwxr-xr-xhttemplate/search/cust_pay.html (renamed from httemplate/search/cust_pay.cgi)0
-rwxr-xr-xhttemplate/search/cust_pay_pending.html2
-rwxr-xr-xhttemplate/search/cust_pkg.cgi14
-rw-r--r--httemplate/search/cust_pkg_discount.html6
-rw-r--r--httemplate/search/cust_pkg_summary.cgi87
-rw-r--r--httemplate/search/cust_pkg_summary.html24
-rw-r--r--httemplate/search/cust_pkg_susp.cgi107
-rw-r--r--httemplate/search/cust_pkg_susp.html24
-rw-r--r--httemplate/search/cust_pkg_svc.html117
-rw-r--r--httemplate/search/elements/cust_main_dayranges.html6
-rwxr-xr-xhttemplate/search/elements/cust_pay_or_refund.html142
-rw-r--r--httemplate/search/elements/report_cust_pay_or_refund.html149
-rw-r--r--httemplate/search/elements/search-xml.html1
-rwxr-xr-xhttemplate/search/report_477.html2
-rw-r--r--httemplate/search/report_cdr.html84
-rw-r--r--httemplate/search/report_cust_bill.html7
-rw-r--r--httemplate/search/report_cust_bill_pkg_discount.html13
-rw-r--r--httemplate/search/report_cust_credit.html21
-rwxr-xr-xhttemplate/search/report_cust_main.html22
-rw-r--r--httemplate/search/report_cust_pay.html121
-rwxr-xr-xhttemplate/search/report_cust_pkg.html3
-rw-r--r--httemplate/search/report_cust_pkg_discount.html13
-rw-r--r--httemplate/search/report_cust_refund.html121
-rw-r--r--httemplate/search/report_h_cust_pay.html2
-rwxr-xr-xhttemplate/search/report_receivables.html19
-rwxr-xr-xhttemplate/search/report_svc_broadband.html100
-rw-r--r--httemplate/search/rt_transaction.html24
-rwxr-xr-xhttemplate/search/svc_acct.cgi6
-rwxr-xr-xhttemplate/search/svc_broadband.cgi75
-rwxr-xr-xhttemplate/view/cust_bill.cgi19
-rwxr-xr-xhttemplate/view/cust_main.cgi30
-rw-r--r--httemplate/view/cust_main/billing.html10
-rw-r--r--httemplate/view/cust_main/contacts.html14
-rw-r--r--httemplate/view/cust_main/custom.html21
-rwxr-xr-xhttemplate/view/cust_main/packages.html5
-rw-r--r--httemplate/view/cust_main/packages/package.html1
-rw-r--r--httemplate/view/cust_main/packages/services.html44
-rw-r--r--httemplate/view/cust_main/packages/status.html6
-rw-r--r--httemplate/view/cust_main/payment_history.html44
-rw-r--r--httemplate/view/cust_main/payment_history/attempted_payment.html41
-rw-r--r--httemplate/view/cust_main/payment_history/payment.html5
-rw-r--r--httemplate/view/cust_main/payment_history/pending_payment.html61
-rw-r--r--httemplate/view/cust_main/payment_history/voided_payment.html8
-rw-r--r--httemplate/view/cust_pay.html34
-rw-r--r--httemplate/view/elements/svc_Common.html16
-rw-r--r--httemplate/view/image.cgi31
-rw-r--r--httemplate/view/svc_acct/communigate.html39
-rw-r--r--httemplate/view/svc_domain/acct_defaults.html8
-rw-r--r--httemplate/view/svc_domain/dns.html93
-rw-r--r--httemplate/view/svc_pbx.cgi72
-rw-r--r--httemplate/view/svc_phone.cgi17
170 files changed, 4818 insertions, 1599 deletions
diff --git a/httemplate/browse/acct_snarf.html b/httemplate/browse/acct_snarf.html
new file mode 100644
index 0000000..f610994
--- /dev/null
+++ b/httemplate/browse/acct_snarf.html
@@ -0,0 +1,78 @@
+<% include('elements/browse.html',
+ 'title' => "Remote POP accounts for $svc_label: $svc_value",
+ 'name_singular' => 'Remote POP account',
+ 'html_init' => $html_init,
+ 'query' => { 'table' => 'acct_snarf',
+ 'hashref' => { 'svcnum' => $svcnum },
+ #'order_by' => 'ORDER BY priority DESC',
+ },
+ 'count_query' => $count_query,
+ 'header' => [ 'Name',
+ 'Mail server',
+ 'Username',
+ #'Password',
+ 'Poll every',
+ #'Options',
+ 'Leave',
+ 'APOP',
+ 'TLS',
+ 'Mailbox',
+ '', #delete
+ ],
+ 'fields' => [ 'snarfname',
+ 'machine',
+ 'username',
+ sub { FS::acct_snarf->check_freq_labels->{shift->check_freq} },
+ 'leave',
+ 'apop',
+ 'tls',
+ 'mailbox',
+ ],
+ #'align'
+ 'links' => [ $edit_sub, $edit_sub, $edit_sub, '',
+ '', '', '', '', $del_sub ],
+ )
+%>
+<%init>
+
+$cgi->param('svcnum') =~ /^(\d+)$/ or die 'no svcnum';
+my $svcnum = $1;
+
+#agent virt so you can't do cross-agent snarfing
+my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svcnum })
+ or die 'unknown svcnum';
+my $part_svc = $cust_svc->part_svc;
+
+my $count_query = "SELECT COUNT(*) FROM acct_snarf WHERE svcnum = $svcnum";
+
+my($svc_label, $svc_value, $svcdb) = $cust_svc->label;
+
+my $view = FS::UI::Web::svc_url( 'm' => $m,
+ 'action' => 'view',
+ 'part_svc' => $part_svc,
+ 'svc' => $cust_svc,
+ );
+
+my $html_init =
+ qq(<A HREF="$view">View this $svc_label</A><BR><BR>).
+ qq!<A HREF="${p}edit/acct_snarf.html?svcnum=$svcnum">Add new remote POP account</A><BR>!.
+ '<BR>'.
+ qq!
+ <SCRIPT>
+ function areyousure_delete(href) {
+ areyousure(href,"Are you sure you want to delete this remote POP account?");
+ }
+ function areyousure(href,message) {
+ if (confirm(message) == true)
+ window.location.href = href;
+ }
+ </SCRIPT>
+!;
+
+my $edit_sub = [ $p.'edit/acct_snarf.html?', 'snarfnum' ];
+my $del_sub = sub {
+ my $snarfnum = shift->snarfnum;
+ [ "javascript:areyousure_delete('${p}misc/delete-acct_snarf.html?$snarfnum')", '' ];
+};
+
+</%init>
diff --git a/httemplate/browse/cgp_rule.html b/httemplate/browse/cgp_rule.html
index 8a427b8..8ea7571 100644
--- a/httemplate/browse/cgp_rule.html
+++ b/httemplate/browse/cgp_rule.html
@@ -44,11 +44,12 @@ my $html_init =
if ( $part_svc->svcdb eq 'svc_domain' ) {
- #areyousure for adding these?
+ #XXX add areyousure javscript confirmation for adding these
foreach my $line ( FS::Conf->new->config('cgp_rule-domain_templates') ) {
$line =~ /^\s*(\d+)\s+(.+)\s*$/ or next;
my($t_svcnum, $t_name) = ( $1, $2 );
+ next if $t_svcnum == $svcnum;
$html_init .=
qq!<A HREF="${p}misc/clone-cgp_rule.html?clone=$t_svcnum;svcnum=$svcnum">!
."Add $t_name rule</A><BR>";
diff --git a/httemplate/browse/msg_template.html b/httemplate/browse/msg_template.html
index 0cd33c7..252ee1f 100644
--- a/httemplate/browse/msg_template.html
+++ b/httemplate/browse/msg_template.html
@@ -9,7 +9,7 @@
'disableable' => 1,
'disabled_statuspos' => 2,
'agent_virt' => 1,
- 'agent_null_right' => 'Edit global templates',
+ 'agent_null_right' => ['Edit global templates','Configuration'],
'agent_pos' => 3,
'header' => [ 'Name' ],
'fields' => [ 'msgname' ],
diff --git a/httemplate/browse/part_event.html b/httemplate/browse/part_event.html
index 3d7c245..f68f06b 100644
--- a/httemplate/browse/part_event.html
+++ b/httemplate/browse/part_event.html
@@ -150,7 +150,7 @@ my $html_init =
foreach my $part_event ( qsearch('part_event', {'diabled'=>''}) ) {
$html_init .= '<OPTION VALUE="'. $part_event->eventpart. '">'.
- $part_event->event. '</OPTION>';
+ $part_event->eventpart. ': '. $part_event->event. '</OPTION>';
}
$html_init .= '</SELECT><INPUT TYPE="submit" VALUE="Clone existing event">'.
diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi
index 42eb5df..26e0170 100755
--- a/httemplate/browse/part_pkg.cgi
+++ b/httemplate/browse/part_pkg.cgi
@@ -32,6 +32,7 @@ my $acl_edit = $curuser->access_right($edit);
my $acl_edit_global = $curuser->access_right($edit_global);
my $acl_config = $curuser->access_right('Configuration'); #to edit services
#and agent types
+ #and bulk change
die "access denied"
unless $acl_edit || $acl_edit_global;
@@ -96,6 +97,13 @@ $select = "
*,
( $count_cust_pkg
+ AND ( setup IS NULL OR setup = 0 )
+ AND ( cancel IS NULL OR cancel = 0 )
+ AND ( susp IS NULL OR susp = 0 )
+ ) AS num_not_yet_billed,
+
+ ( $count_cust_pkg
+ AND setup IS NOT NULL AND setup != 0
AND ( cancel IS NULL OR cancel = 0 )
AND ( susp IS NULL OR susp = 0 )
) AS num_active,
@@ -195,6 +203,9 @@ push @fields, sub {
my $part_pkg = shift;
(my $plan = $plan_labels{$part_pkg->plan} ) =~ s/ /&nbsp;/g;
my $is_recur = ( $part_pkg->freq ne '0' );
+ my @discounts = sort { $a->months <=> $b->months }
+ map { $_->discount }
+ $part_pkg->part_pkg_discount;
[
[
@@ -238,6 +249,28 @@ push @fields, sub {
}
$part_pkg->bill_part_pkg_link
),
+ ( scalar(@discounts)
+ ? [
+ { data => '<b>Discounts</b>',
+ align=>'center', #?
+ colspan=>2,
+ }
+ ]
+ : ()
+ ),
+ ( scalar(@discounts)
+ ? map {
+ [
+ { data => $_->months. ':',
+ align => 'right',
+ },
+ { data => $_->amount ? '$'. $_->amount : $_->percent. '%'
+ }
+ ]
+ }
+ @discounts
+ : ()
+ ),
];
# $plan_labels{$part_pkg->plan}.'<BR>'.
@@ -284,6 +317,7 @@ if ( $acl_edit_global ) {
#if ( $cgi->param('active') ) {
push @header, 'Customer<BR>packages';
my %col = (
+ 'not yet billed' => '009999', #teal? cyan?
'active' => '00CC00',
'suspended' => 'FF9900',
'cancelled' => 'FF0000',
@@ -292,8 +326,8 @@ if ( $acl_edit_global ) {
);
my $cust_pkg_link = $p. 'search/cust_pkg.cgi?pkgpart=';
push @fields, sub { my $part_pkg = shift;
- [
- map {
+ [
+ map( {
my $magic = $_;
my $label = $_;
if ( $magic eq 'active' && $part_pkg->freq == 0 ) {
@@ -301,6 +335,7 @@ if ( $acl_edit_global ) {
#$label = 'one-time charge',
$label = 'charge',
}
+ $label= 'not yet billed' if $magic eq 'not_yet_billed';
[
{
@@ -325,8 +360,24 @@ if ( $acl_edit_global ) {
),
},
],
- } (qw( active suspended cancelled ))
- ]; };
+ } (qw( not_yet_billed active suspended cancelled ))
+ ),
+ ($acl_config ?
+ [ {},
+ { 'data' => '<FONT SIZE="-1">[ '.
+ include('/elements/popup_link.html',
+ 'label' => 'change',
+ 'action' => "${p}edit/bulk-cust_pkg.html?".
+ 'pkgpart='.$part_pkg->pkgpart,
+ 'actionlabel' => 'Change Packages',
+ 'width' => 569,
+ 'height' => 210,
+ ).' ]</FONT>',
+ 'align' => 'left',
+ }
+ ] : () ),
+ ];
+ };
$align .= 'r';
#}
diff --git a/httemplate/docs/credits.html b/httemplate/docs/credits.html
index 6500e73..a3e7695 100644
--- a/httemplate/docs/credits.html
+++ b/httemplate/docs/credits.html
@@ -75,6 +75,7 @@ Kelly Hickel<BR>
Mark James<BR>
Frederico Caldeira Knabben<BR>
Greg Kuhnert<BR>
+Erik Levinson<BR>
Randall Lucas<BR>
Foteos Macrides<BR>
Roger Mangraviti<BR>
diff --git a/httemplate/edit/REAL_cust_pkg.cgi b/httemplate/edit/REAL_cust_pkg.cgi
index 77ab1fe..ba217eb 100755
--- a/httemplate/edit/REAL_cust_pkg.cgi
+++ b/httemplate/edit/REAL_cust_pkg.cgi
@@ -55,12 +55,16 @@
<& .row_edit, cust_pkg=>$cust_pkg, column=>'setup', label=>'Setup' &>
<& .row_edit, cust_pkg=>$cust_pkg, column=>'last_bill', label=>$last_bill_or_renewed &>
<& .row_edit, cust_pkg=>$cust_pkg, column=>'bill', label=>$next_bill_or_prepaid_until &>
- <& .row_display, cust_pkg=>$cust_pkg, column=>'adjourn', label=>'Adjournment', note=>'(will <b>suspend</b> this package when the date is reached)' &>
- <& .row_display, cust_pkg=>$cust_pkg, column=>'susp', label=>'Suspension' &>
+%#if ( $cust_pkg->contract_end or $part_pkg->option('contract_end_months',1) ) {
+ <& .row_edit, cust_pkg=>$cust_pkg, column=>'contract_end',label=>'Contract end' &>
+%#}
+ <& .row_display, cust_pkg=>$cust_pkg, column=>'adjourn', label=>'Adjournment', note=>'(will <b>suspend</b> this package when the date is reached)' &>
+ <& .row_display, cust_pkg=>$cust_pkg, column=>'susp', label=>'Suspension' &>
<& .row_display, cust_pkg=>$cust_pkg, column=>'expire', label=>'Expiration', note=>'(will <b>cancel</b> this package when the date is reached)' &>
<& .row_display, cust_pkg=>$cust_pkg, column=>'cancel', label=>'Cancellation' &>
+
<%def .row_edit>
<%args>
$cust_pkg
@@ -133,7 +137,7 @@
my $conf = new FS::Conf;
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
-my $format = $date_format. ' %T %z (%Z)';
+my $format = $date_format. ' %T'; # %z (%Z)';
</%shared>
<%init>
diff --git a/httemplate/edit/access_user.html b/httemplate/edit/access_user.html
index 22cf896..86ce253 100644
--- a/httemplate/edit/access_user.html
+++ b/httemplate/edit/access_user.html
@@ -20,9 +20,8 @@
'user_custnum' => 'Customer (optional)',
'disabled' => 'Disable employee',
},
- 'edit_callback' => sub { my( $c, $o ) = @_;
- $o->set('_password', '');
- },
+ 'edit_callback' => \&edit_callback,
+ 'field_callback'=> \&field_callback,
'viewall_dir' => 'browse',
'html_bottom' =>
sub {
@@ -62,4 +61,17 @@ my $check_user_custnum_search = <<END;
</SCRIPT>
END
+sub edit_callback {
+ my ($c, $o, $f, $opt) = @_;
+ $o->set('_password', '');
+}
+
+sub field_callback {
+ my ($c, $o, $f) = @_;
+ if($f->{'type'} eq 'password' and $o->is_system_user) {
+ $f->{'type'} = 'hidden';
+ $f->{'disabled'} = 1;
+ }
+}
+
</%init>
diff --git a/httemplate/edit/acct_snarf.html b/httemplate/edit/acct_snarf.html
new file mode 100644
index 0000000..1c815b2
--- /dev/null
+++ b/httemplate/edit/acct_snarf.html
@@ -0,0 +1,50 @@
+<% include('elements/edit.html',
+ 'name_singular' => 'remote email address',
+ 'table' => 'acct_snarf',
+ 'labels' => { 'snarfnum' => 'Remote email address',
+ #'svcnum' => 'Local account',
+ 'snarfname' => 'Name',
+ 'machine' => 'Mail server',
+ 'protocol' => 'Protocol',
+ 'username' => 'Username',
+ '_password' => 'Password',
+ 'check_freq' => 'Poll every',
+ 'leavemail' => 'Leave',
+ 'apop' => 'Use APOP',
+ 'tls' => 'TLS',
+ 'mailbox' => 'Mailbox',
+ },
+ 'fields' => [
+ { field=>'svcnum', type=>'hidden', },
+ { field=>'protocol', type=>'hidden', },
+ 'snarfname',
+ 'machine',
+ 'username',
+ { 'field'=>'_password', type=>'password', },
+ { 'field' => 'check_freq',
+ 'type' => 'select',
+ 'options' => [ keys %$cf_labels ],
+ 'labels' => $cf_labels,
+ },
+ { field=>'leavemail', type=>'checkbox', value=>'Y' },
+ { field=>'apop', type=>'checkbox', value=>'Y' },
+ { field=>'tls', type=>'checkbox', value=>'Y' },
+ 'mailbox',
+ ],
+ 'new_callback' => sub { my( $cgi, $acct_snarf ) = @_;
+ $acct_snarf->svcnum($cgi->param('svcnum'));
+ $acct_snarf->protocol('POP');
+ },
+ #'viewall_url' => $viewall_url,
+ 'menubar' => [],
+ )
+%>
+<%init>
+
+my %opt = @_;
+
+#my $viewall_url = $p. "browse/$table.html?svcnum=$svcnum";
+
+my $cf_labels = FS::acct_snarf->check_freq_labels;
+
+</%init>
diff --git a/httemplate/edit/bulk-cust_pkg.html b/httemplate/edit/bulk-cust_pkg.html
new file mode 100644
index 0000000..2ff38ca
--- /dev/null
+++ b/httemplate/edit/bulk-cust_pkg.html
@@ -0,0 +1,60 @@
+<% include('/elements/header-popup.html', 'Bulk package change') %>
+
+<% include('/elements/init_overlib.html') %>
+
+<% include('/elements/progress-init.html',
+ 'OneTrueForm',
+ [qw( old_pkgpart new_pkgpart )],
+ 'process/bulk-cust_pkg.cgi',
+ $p.'browse/part_pkg.cgi',
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+function areyousure() {
+ var warning = 'Change these packages?';
+ if(confirm(warning)) {
+ process();
+ }
+}
+</SCRIPT>
+<FORM NAME="OneTrueForm">
+% #false laziness with bulk-cust_svc.html
+% $cgi->param('pkgpart') =~ /^(\d+)$/
+% or die "illegal pkgpart: ". $cgi->param('pkgpart');
+%
+% my $old_pkgpart = $1;
+% my $src_part_pkg = qsearchs('part_pkg', { 'pkgpart' => $old_pkgpart } )
+% or die "unknown pkgpart: $old_pkgpart";
+%
+
+
+<INPUT NAME="old_pkgpart" TYPE="hidden" VALUE="<% $old_pkgpart %>">
+Change <B><% $src_part_pkg->pkg_comment %></B><BR>
+
+to new package definition
+<SELECT NAME="new_pkgpart">
+% foreach my $dest_part_pkg ( qsearch('part_pkg', { 'disabled' => '' } ) ) {
+
+ <OPTION VALUE="<% $dest_part_pkg->pkgpart %>"><% $dest_part_pkg->pkgpart %>: <% $dest_part_pkg->pkg %>
+% }
+
+</SELECT>
+<BR>
+<BR>
+%#<INPUT TYPE="checkbox" NAME="keep_dates" CHECKED> Preserve all billing dates <I>(strongly recommended)</I>
+%#<BR>
+%#<BR>
+
+<INPUT TYPE="button" VALUE="Bulk change packages" onclick="areyousure()">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/cgp_rule-redirect_all.html b/httemplate/edit/cgp_rule-redirect_all.html
index 898eef8..c8c9e01 100644
--- a/httemplate/edit/cgp_rule-redirect_all.html
+++ b/httemplate/edit/cgp_rule-redirect_all.html
@@ -1,37 +1,49 @@
<% include('/elements/header-popup.html', 'Redirect all mail') %>
+<% include('/elements/error.html') %>
+
<FORM NAME="RedirectAllForm" ACTION="process/cgp_rule-redirect_all.html" METHOD=POST>
-%# XXX upstream Redirect 1
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $opt{'svcnum'} %>">
<% ntable("#cccccc", 2) %>
<TR>
<TD ALIGN="right">Redirect all mail to</TD>
- <TD><textarea name="RedirectText" rows="5" cols="50"></textarea></TD>
+ <TD><textarea name="RedirectText" rows="5" cols="50"><% $mirror_or_redir ? $mirror_or_redir->params : '' %></textarea></TD>
</TR>
<% include('/elements/tr-checkbox.html',
- 'name' => 'RedirKeep',
+ 'field' => 'RedirKeep',
'label' => 'Keep a copy',
'value' => 1,
- 'curr_value' => '', #XXX
+ 'curr_value' => ( $cgi->param('error')
+ ? scalar($cgi->param('RedirKeep'))
+ : ( ($redir_keep || !$cgp_rule) ? '' : 1 )
+ ),
)
%>
<% include('/elements/tr-checkbox.html',
- 'name' => 'RedirHuman',
+ 'field' => 'RedirHuman',
'label' => 'Do not redirect automatic messages',
'value' => 1,
- 'curr_value' => '', #XXX
+ 'curr_value' => ( $cgi->param('error')
+ ? scalar($cgi->param('RedirHuman'))
+ : ( $redir_human ? 1 : '' )
+ ),
)
%>
<% include('/elements/tr-checkbox.html',
- 'name' => 'KeepToAndCc',
+ 'field' => 'KeepToAndCc',
'label' => 'Preserve To/Cc fields',
'value' => 1,
- 'curr_value' => '', #XXX
+ 'curr_value' => ( $cgi->param('error')
+ ? scalar($cgi->param('KeepToAndCc'))
+ : ( $mirror_or_redir &&
+ $mirror_or_redir->action eq 'Mirror To' )
+ ),
)
%>
@@ -39,7 +51,6 @@
<BR>
<INPUT TYPE="submit" VALUE="Redirect all mail">
-%#XXX Add/Edit
</FORM>
@@ -52,6 +63,27 @@ my %opt = @_;
my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } )
or die "unknown svcnum";
-#XXX look for existing redirect all rule
+#look for existing rule
+my $cgp_rule = qsearchs('cgp_rule', { 'svcnum' => $svc_acct->svcnum,
+ 'name' => '#Redirect'
+ }
+ );
+
+my( $redir_human, $mirror_or_redir, $redir_keep ) = ( '', '', '' );
+if ( $cgp_rule ) {
+ $redir_human = qsearchs('cgp_rule_condition', {
+ 'rulenum' => $cgp_rule->rulenum,
+ 'conditionname' => 'Human Generated',
+ });
+ $mirror_or_redir = qsearchs({
+ 'table' => 'cgp_rule_action',
+ 'hashref' => { 'rulenum' => $cgp_rule->rulenum, },
+ 'extra_sql' => " AND action IN ('Mirror To', 'Redirect To') ",
+ });
+ $redir_keep = qsearchs('cgp_rule_action', {
+ 'rulenum' => $cgp_rule->rulenum,
+ 'action' => 'Discard',
+ });
+}
</%init>
diff --git a/httemplate/edit/cgp_rule-vacation.html b/httemplate/edit/cgp_rule-vacation.html
index efdc541..8c28885 100644
--- a/httemplate/edit/cgp_rule-vacation.html
+++ b/httemplate/edit/cgp_rule-vacation.html
@@ -1,35 +1,35 @@
<% include('/elements/header-popup.html', 'Vacation rule') %>
+<% include('/elements/error.html') %>
+
<FORM NAME="VacationForm" ACTION="process/cgp_rule-vacation.html" METHOD=POST>
-%# XXX upstream Vacation 1
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $opt{'svcnum'} %>">
<% ntable("#cccccc", 2) %>
<TR>
<TD ALIGN="right">Vacation message</TD>
- <TD><textarea name="VacationText" rows="5" cols="50"></textarea></TD>
+ <TD><textarea name="VacationText" rows="5" cols="50"><% $reply_with ? $reply_with->params : '' %></textarea></TD>
</TR>
<% include('/elements/tr-input-date-field.html', {
- 'label' => 'Ends',
- 'name' => 'vacationTill',
- 'value' => '', #XXX
+ 'label' => 'Ends',
+ 'name' => 'vacationTill',
+ 'format' => '%d %b %Y',
+ 'value' => ( $cgi->param('error')
+ ? scalar($cgi->param('vacationTill'))
+ : ( $curr_date ? $curr_date->params : '' )
+ ),
})
%>
-%# XXX upstream:
-%# VacationTill 1
-%# vacationDay
-%# vacationMonth
-%# vacationYear
-%#XXX Clear 'Replied Addresses' List
+%#Clear 'Replied Addresses' List ?
</TABLE>
<BR>
-<INPUT TYPE="submit" VALUE="Add vacation message">
-%#XXX Add/Edit
+<INPUT TYPE="submit" VALUE="<% $cgp_rule ? 'Edit' : 'Add' %> vacation message">
</FORM>
@@ -42,6 +42,23 @@ my %opt = @_;
my $svc_acct = qsearchs('svc_acct', { 'svcnum' => $opt{'svcnum'} } )
or die "unknown svcnum";
-#XXX look for existing vacation rule
+#look for existing rule
+my $cgp_rule = qsearchs('cgp_rule', { 'svcnum' => $svc_acct->svcnum,
+ 'name' => '#Vacation'
+ }
+ );
+
+my( $curr_date, $reply_with ) = ( '', '' );
+if ( $cgp_rule ) {
+ $curr_date = qsearchs('cgp_rule_condition', {
+ 'rulenum' => $cgp_rule->rulenum,
+ 'conditionname' => 'Current Date',
+ 'op' => 'less than',
+ });
+ $reply_with = qsearchs('cgp_rule_action', {
+ 'rulenum' => $cgp_rule->rulenum,
+ 'action' => 'Reply with',
+ });
+}
</%init>
diff --git a/httemplate/edit/cust_main.cgi b/httemplate/edit/cust_main.cgi
index 57dc359..07629dc 100755
--- a/httemplate/edit/cust_main.cgi
+++ b/httemplate/edit/cust_main.cgi
@@ -179,6 +179,8 @@ function samechanged(what) {
<% include('cust_main/first_pkg.html', $cust_main,
'pkgpart_svcpart' => $pkgpart_svcpart,
+ 'disable_empty' =>
+ scalar( $cgi->param('lock_pkgpart') =~ /^(\d+)$/ ),
#svc_acct
'username' => $username,
'password' => $password,
@@ -307,11 +309,18 @@ if ( $cgi->param('error') ) {
$stateid = '';
$payinfo = '';
+ if ( $cgi->param('lock_pkgpart') =~ /^(\d+)$/ ) {
+ my $pkgpart = $1;
+ my $part_pkg = qsearchs('part_pkg', { 'pkgpart' => $pkgpart } )
+ or die "unknown pkgpart $pkgpart";
+ my $svcpart = $part_pkg->svcpart;
+ $pkgpart_svcpart = $pkgpart.'_'.$svcpart;
+ }
+
}
-my $error = $cgi->param('error');
-$cgi->delete_all();
-$cgi->param('error', $error);
+my %keep = map { $_=>1 } qw( error tagnum lock_agentnum lock_pkgpart );
+$cgi->delete( grep !$keep{$_}, $cgi->param );
my $title = $custnum ? 'Edit Customer' : 'Add Customer';
$title .= ": ". $cust_main->name if $custnum;
diff --git a/httemplate/edit/cust_main/billing.html b/httemplate/edit/cust_main/billing.html
index ad83778..d121982 100644
--- a/httemplate/edit/cust_main/billing.html
+++ b/httemplate/edit/cust_main/billing.html
@@ -240,7 +240,7 @@
% qq!<INPUT TYPE="hidden" NAME="BILL_exp_year" VALUE="2037">!.
%
% qq!<TR><TD ALIGN="right" WIDTH="200">Attention </TD>!.
-% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payname" VALUE="!. ( $payby eq 'BILL' ? $cust_main->payname : '' ). qq!"></TD></TR>!.
+% qq!<TD WIDTH="408"><INPUT TYPE="text" NAME="BILL_payname" VALUE="!. encode_entities( $payby eq 'BILL' ? $cust_main->payname : '' ). qq!"></TD></TR>!.
%
% '<TR><TD>&nbsp;</TD></TR>'.
% '<TR><TD>&nbsp;</TD></TR>'.
@@ -417,6 +417,18 @@
%>
</TD>
</TR>
+ <TR>
+ <TD ALIGN="right" WIDTH="200">Credit limit </TD>
+ <TD WIDTH="408">
+ <SCRIPT TYPE="text/javascript">
+function toggle(obj) {
+ obj.form.credit_limit.disabled = obj.checked;
+}
+ </SCRIPT>
+ <INPUT TYPE="text" NAME="credit_limit" VALUE=<% sprintf('"%.2f"', $cust_main->credit_limit) %><% length($cust_main->credit_limit) ? '' : ' DISABLED' %>>
+ <INPUT TYPE="checkbox" NAME="no_credit_limit" VALUE=1 onclick="toggle(this)"<% length($cust_main->credit_limit) ? '' : ' CHECKED'%>> Unlimited
+ </TD>
+ </TR>
% if ( $conf->exists('voip-cust_cdr_spools') ) {
<TR>
diff --git a/httemplate/edit/cust_main/bottomfixup.js b/httemplate/edit/cust_main/bottomfixup.js
index 5d06f3c..942fc0e 100644
--- a/httemplate/edit/cust_main/bottomfixup.js
+++ b/httemplate/edit/cust_main/bottomfixup.js
@@ -20,223 +20,14 @@ function bottomfixup(what) {
}
//this part does USPS address correction
-
- // XXX should this be first and should we update the form fields that are
- // displayed???
-
- var cf = document.CustomerForm;
-
- var state_el = cf.elements['state'];
- var ship_state_el = cf.elements['ship_state'];
-
- //address_standardize(
- var cust_main = new Array(
- 'company', cf.elements['company'].value,
- 'address1', cf.elements['address1'].value,
- 'address2', cf.elements['address2'].value,
- 'city', cf.elements['city'].value,
- 'state', state_el.options[ state_el.selectedIndex ].value,
- 'zip', cf.elements['zip'].value,
-
- 'ship_company', cf.elements['ship_company'].value,
- 'ship_address1', cf.elements['ship_address1'].value,
- 'ship_address2', cf.elements['ship_address2'].value,
- 'ship_city', cf.elements['ship_city'].value,
- 'ship_state', ship_state_el.options[ ship_state_el.selectedIndex ].value,
- 'ship_zip', cf.elements['ship_zip'].value
- );
-
- address_standardize( cust_main, update_address );
+ standardize_locations();
}
-var standardize_address;
-
-function update_address(arg) {
-
- var argsHash = eval('(' + arg + ')');
-
- var changed = argsHash['address_standardized'];
- var ship_changed = argsHash['ship_address_standardized'];
- var error = argsHash['error'];
- var ship_error = argsHash['ship_error'];
-
-
- //yay closures
- standardize_address = function () {
-
- var cf = document.CustomerForm;
- var state_el = cf.elements['state'];
- var ship_state_el = cf.elements['ship_state'];
-
- if ( changed ) {
- cf.elements['company'].value = argsHash['new_company'];
- cf.elements['address1'].value = argsHash['new_address1'];
- cf.elements['address2'].value = argsHash['new_address2'];
- cf.elements['city'].value = argsHash['new_city'];
- setselect(cf.elements['state'], argsHash['new_state']);
- cf.elements['zip'].value = argsHash['new_zip'];
- }
-
- if ( ship_changed ) {
- cf.elements['ship_company'].value = argsHash['new_ship_company'];
- cf.elements['ship_address1'].value = argsHash['new_ship_address1'];
- cf.elements['ship_address2'].value = argsHash['new_ship_address2'];
- cf.elements['ship_city'].value = argsHash['new_ship_city'];
- setselect(cf.elements['ship_state'], argsHash['new_ship_state']);
- cf.elements['ship_zip'].value = argsHash['new_ship_zip'];
- }
-
- post_standardization();
-
- }
-
-
-
- if ( changed || ship_changed ) {
-
-% if ( $conf->exists('cust_main-auto_standardize_address') ) {
-
- standardize_address();
-
-% } else {
-
- // popup a confirmation popup
-
- var confirm_change =
- '<CENTER><BR><B>Confirm address standardization</B><BR><BR>' +
- '<TABLE>';
-
- if ( changed ) {
-
- confirm_change = confirm_change +
- '<TR><TH>Entered billing address</TH>' +
- '<TH>Standardized billing address</TH></TR>';
- // + '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
-
- if ( argsHash['company'] || argsHash['new_company'] ) {
- confirm_change = confirm_change +
- '<TR><TD>' + argsHash['company'] +
- '</TD><TD>' + argsHash['new_company'] + '</TD></TR>';
- }
-
- confirm_change = confirm_change +
- '<TR><TD>' + argsHash['address1'] +
- '</TD><TD>' + argsHash['new_address1'] + '</TD></TR>' +
- '<TR><TD>' + argsHash['address2'] +
- '</TD><TD>' + argsHash['new_address2'] + '</TD></TR>' +
- '<TR><TD>' + argsHash['city'] + ', ' + argsHash['state'] + ' ' + argsHash['zip'] +
- '</TD><TD>' + argsHash['new_city'] + ', ' + argsHash['new_state'] + ' ' + argsHash['new_zip'] + '</TD></TR>' +
- '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
-
- }
-
- if ( ship_changed ) {
-
- confirm_change = confirm_change +
- '<TR><TH>Entered service address</TH>' +
- '<TH>Standardized service address</TH></TR>';
- // + '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
-
- if ( argsHash['ship_company'] || argsHash['new_ship_company'] ) {
- confirm_change = confirm_change +
- '<TR><TD>' + argsHash['ship_company'] +
- '</TD><TD>' + argsHash['new_ship_company'] + '</TD></TR>';
- }
-
- confirm_change = confirm_change +
- '<TR><TD>' + argsHash['ship_address1'] +
- '</TD><TD>' + argsHash['new_ship_address1'] + '</TD></TR>' +
- '<TR><TD>' + argsHash['ship_address2'] +
- '</TD><TD>' + argsHash['new_ship_address2'] + '</TD></TR>' +
- '<TR><TD>' + argsHash['ship_city'] + ', ' + argsHash['ship_state'] + ' ' + argsHash['ship_zip'] +
- '</TD><TD>' + argsHash['new_ship_city'] + ', ' + argsHash['new_ship_state'] + ' ' + argsHash['new_ship_zip'] + '</TD></TR>' +
- '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
-
- }
-
- var addresses = 'address';
- var height = 268;
- if ( changed && ship_changed ) {
- addresses = 'addresses';
- height = 396; // #what
- }
-
- confirm_change = confirm_change +
- '<TR><TD>' +
- '<BUTTON TYPE="button" onClick="post_standardization();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' +
- '</TD><TD>' +
- '<BUTTON TYPE="button" onClick="standardize_address();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' +
- '</TD></TR>' +
- '<TR><TD COLSPAN=2 ALIGN="center">' +
- '<BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' +
-
- '</TABLE></CENTER>';
-
- overlib( confirm_change, CAPTION, 'Confirm address standardization', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, height, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 );
-
-% }
-
- } else {
-
- post_standardization();
-
- }
-
-
-}
-
-function post_standardization() {
-
- var cf = document.CustomerForm;
-
-% if ( $conf->exists('enable_taxproducts') ) {
-
- if ( new String(cf.elements['<% $taxpre %>zip'].value).length < 10 )
- {
-
- var country_el = cf.elements['<% $taxpre %>country'];
- var country = country_el.options[ country_el.selectedIndex ].value;
- var geocode = cf.elements['geocode'].value;
-
- if ( country == 'CA' || country == 'US' ) {
-
- var state_el = cf.elements['<% $taxpre %>state'];
- var state = state_el.options[ state_el.selectedIndex ].value;
-
- var url = "cust_main/choose_tax_location.html" +
- "?data_vendor=cch-zip" +
- ";city=" + cf.elements['<% $taxpre %>city'].value +
- ";state=" + state +
- ";zip=" + cf.elements['<% $taxpre %>zip'].value +
- ";country=" + country +
- ";geocode=" + geocode +
- ";";
-
- // popup a chooser
- OLgetAJAX( url, update_geocode, 300 );
-
- } else {
-
- cf.elements['geocode'].value = 'DEFAULT';
- post_geocode();
-
- }
-
- } else {
-
- cf.elements['geocode'].value = '';
- post_geocode();
-
- }
-
-% } else {
-
- post_geocode();
-
-% }
-
-}
+<% include( '/elements/standardize_locations.js',
+ 'callback', 'post_geocode();'
+ )
+%>
function post_geocode() {
@@ -263,29 +54,6 @@ function post_geocode() {
}
-function update_geocode() {
-
- //yay closures
- set_geocode = function (what) {
-
- var cf = document.CustomerForm;
-
- //alert(what.options[what.selectedIndex].value);
- var argsHash = eval('(' + what.options[what.selectedIndex].value + ')');
- cf.elements['<% $taxpre %>city'].value = argsHash['city'];
- setselect(cf.elements['<% $taxpre %>state'], argsHash['state']);
- cf.elements['<% $taxpre %>zip'].value = argsHash['zip'];
- cf.elements['geocode'].value = argsHash['geocode'];
- post_geocode();
-
- }
-
- // popup a chooser
-
- overlib( OLresponseAJAX, CAPTION, 'Select tax location', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 );
-
-}
-
var set_censustract;
function update_censustract(arg) {
@@ -381,19 +149,8 @@ function copyelement(from, to) {
//alert(from + " (" + from.type + "): " + to.name + " => " + to.value);
}
-function setselect(el, value) {
-
- for ( var s = 0; s < el.options.length; s++ ) {
- if ( el.options[s].value == value ) {
- el.selectedIndex = s;
- }
- }
-
-}
<%init>
my $conf = new FS::Conf;
-my $taxpre = $conf->exists('tax-ship_address') ? 'ship_' : '';
-
</%init>
diff --git a/httemplate/edit/cust_main/contact.html b/httemplate/edit/cust_main/contact.html
index feb61db..99bc558 100644
--- a/httemplate/edit/cust_main/contact.html
+++ b/httemplate/edit/cust_main/contact.html
@@ -3,8 +3,8 @@
<TR>
<TH ALIGN="right"><%$r%>Contact&nbsp;name<BR>(last,&nbsp;first)</TH>
<TD COLSPAN=5>
- <INPUT TYPE="text" NAME="<%$pre%>last" VALUE="<% $cust_main->get($pre.'last') %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>> ,
- <INPUT TYPE="text" NAME="<%$pre%>first" VALUE="<% $cust_main->get($pre.'first') %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>>
+ <INPUT TYPE="text" NAME="<%$pre%>last" VALUE="<% $cust_main->get($pre.'last') |h %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>> ,
+ <INPUT TYPE="text" NAME="<%$pre%>first" VALUE="<% $cust_main->get($pre.'first') |h %>" onChange="<% $onchange %>" <%$disabled%> <%$style%>>
</TD>
% if ( $conf->exists('show_ss') && !$pre ) {
@@ -21,7 +21,7 @@
<TR>
<TD ALIGN="right">Company</TD>
<TD COLSPAN=7>
- <INPUT TYPE="text" NAME="<%$pre%>company" VALUE="<% $cust_main->get($pre.'company') %>" SIZE=70 onChange="<% $onchange %>" <%$disabled%> <%$style%>>
+ <INPUT TYPE="text" NAME="<%$pre%>company" VALUE="<% $cust_main->get($pre.'company') |h %>" SIZE=70 onChange="<% $onchange %>" <%$disabled%> <%$style%>>
</TD>
</TR>
diff --git a/httemplate/edit/cust_main/first_pkg.html b/httemplate/edit/cust_main/first_pkg.html
index 0de33c0..7c131ea 100644
--- a/httemplate/edit/cust_main/first_pkg.html
+++ b/httemplate/edit/cust_main/first_pkg.html
@@ -1,3 +1,9 @@
+% if ( $cgi->param('lock_pkgpart') =~ /^([\d, ]+)$/ ) {
+
+ <INPUT TYPE="hidden" NAME="lock_pkgpart" VALUE="<% $1 %>">
+
+% }
+%
% if ( @part_pkg ) {
<BR><BR>
@@ -28,6 +34,11 @@ if ( scalar(@agents) == 1 ) {
# $pkgpart->{PKGPART} is true iff $custnum may purchase PKGPART
$pkgpart = $agents[0]->pkgpart_hashref;
$agentnum = $agents[0]->agentnum;
+} elsif ( $cgi->param('lock_agentnum') =~ /^(\d+)$/
+ && $FS::CurrentUser::CurrentUser->agentnum($1) ) {
+ $agentnum = $1;
+ my $agent = (grep { $_->agentnum == $agentnum } @agents)[0];
+ $pkgpart = $agent->pkgpart_hashref;
} else {
#can't know (agent not chosen), so, allow all
$agentnum = 'all';
@@ -39,9 +50,28 @@ if ( scalar(@agents) == 1 ) {
}
#eslaf
+my @part_pkg = ();
+if ( $cgi->param('lock_pkgpart') =~ /^([\d, ]+)$/ ) {
+
+ my $lock_pkgpart = $1;
+
+ @part_pkg = qsearch({
+ 'table' => 'part_pkg',
+ 'hashref' => { 'disabled' => '' },
+ 'extra_sql' => "AND pkgpart IN ($lock_pkgpart)",
+ 'order_by' => 'ORDER BY pkg', # case?
+ });
+
+} else {
+
+ @part_pkg =
+ qsearch( 'part_pkg', { 'disabled' => '' }, '', 'ORDER BY pkg' ); # case?
+
+}
+
my @first_svc = ( 'svc_acct', 'svc_phone' );
-my @part_pkg =
+@part_pkg =
grep { $_->svcpart(\@first_svc)
&& ( $pkgpart->{ $_->pkgpart }
|| $agentnum eq 'all'
@@ -50,6 +80,6 @@ my @part_pkg =
)
)
}
- qsearch( 'part_pkg', { 'disabled' => '' }, '', 'ORDER BY pkg' ); # case?
+ @part_pkg;
</%init>
diff --git a/httemplate/edit/cust_main/first_pkg/select-part_pkg.html b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html
index 871e1cd..20f0e19 100644
--- a/httemplate/edit/cust_main/first_pkg/select-part_pkg.html
+++ b/httemplate/edit/cust_main/first_pkg/select-part_pkg.html
@@ -147,7 +147,9 @@ foreach my $part_pkg ( @part_pkg ) {
$layermap{$pkgpart_svcpart} = $svcdb{$pkgpart};
}
-my @options = ( '', map $pkgpart_svcpart{ $_->pkgpart }, @part_pkg );
+my @options = ();
+push @options, '' unless $opt{'disable_empty'};
+push @options, map $pkgpart_svcpart{ $_->pkgpart }, @part_pkg;
my %labels = ( '' => ( $opt{'empty_label'} || '(none)' ),
map { $pkgpart_svcpart{ $_->pkgpart } => $_->pkg_comment }
@part_pkg
diff --git a/httemplate/edit/cust_main/top_misc.html b/httemplate/edit/cust_main/top_misc.html
index 441a363..a2381f3 100644
--- a/httemplate/edit/cust_main/top_misc.html
+++ b/httemplate/edit/cust_main/top_misc.html
@@ -8,14 +8,30 @@
%>
%# agent
-<% include('/elements/tr-select-agent.html',
- 'curr_value' => $cust_main->agentnum,
- 'label' => "<B>${r}Agent</B>",
- 'empty_label' => 'Select agent',
- 'disable_empty' => ( $cust_main->agentnum ? 1 : 0 ),
- 'viewall_right' => 'None', #override default 'View customers of all agents'
- )
-%>
+% if ( $cgi->param('lock_agentnum') =~ /^(\d+)$/ && $curuser->agentnum($1) ) {
+%
+% my $agentnum = $1;
+% $cust_main->agentnum($agentnum);
+
+ <INPUT TYPE="hidden" NAME="lock_agentnum" VALUE="<% $agentnum %>">
+ <INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $agentnum %>">
+ <TR>
+ <TD ALIGN="right">Agent</TD>
+ <TD CLASS="fsdisabled"><% $cust_main->agent->agent |h %></TD>
+ </TR>
+
+% } else {
+
+ <% include('/elements/tr-select-agent.html',
+ 'curr_value' => $cust_main->agentnum,
+ 'label' => "<B>${r}Agent</B>",
+ 'empty_label' => 'Select agent',
+ 'disable_empty' => ( $cust_main->agentnum ? 1 : 0 ),
+ 'viewall_right' => 'None', #override default 'View customers of all agents'
+ )
+ %>
+
+% }
%# agent_custid
% if ( $conf->exists('cust_main-edit_agent_custid') ) {
diff --git a/httemplate/edit/cust_pay.cgi b/httemplate/edit/cust_pay.cgi
index cc4ec60..8e1c779 100755
--- a/httemplate/edit/cust_pay.cgi
+++ b/httemplate/edit/cust_pay.cgi
@@ -46,6 +46,12 @@ Payment
<TD><INPUT TYPE="text" NAME="paid" VALUE="<% $paid %>" SIZE=8 MAXLENGTH=8> by <B><% FS::payby->payname($payby) %></B></TD>
</TR>
+ <% include('/elements/tr-select-discount_term.html',
+ 'custnum' => $custnum,
+ 'cgi' => $cgi
+ )
+ %>
+
% if ( $payby eq 'BILL' ) {
<TR>
<TD ALIGN="right">Check #</TD>
@@ -103,9 +109,6 @@ my $conf = new FS::Conf;
my $money_char = $conf->config('money_char') || '$';
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Post payment');
-
my($link, $linknum, $paid, $payby, $payinfo, $_date);
if ( $cgi->param('error') ) {
$link = $cgi->param('link');
@@ -132,6 +135,13 @@ if ( $cgi->param('error') ) {
die "illegal query ". $cgi->keywords;
}
+my @rights = ('Post payment');
+push @rights, 'Post check payment' if $payby eq 'BILL';
+push @rights, 'Post cash payment' if $payby eq 'CASH';
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right(\@rights);
+
my $paybatch = "webui-$_date-$$-". rand() * 2**32;
my $title = 'Post '. FS::payby->payname($payby). ' payment';
diff --git a/httemplate/edit/cust_pay_pending.html b/httemplate/edit/cust_pay_pending.html
index 0916a1c..0056bb9 100644
--- a/httemplate/edit/cust_pay_pending.html
+++ b/httemplate/edit/cust_pay_pending.html
@@ -8,6 +8,10 @@
<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>
+% } elsif ( $action eq 'capture' ) {
+
+ <CENTER><FONT SIZE="+1"><B>Captured payment not recorded in database - check logs for errors.</B></FONT></CENTER>
+
% }
<BR>
@@ -91,7 +95,9 @@
</TD>
</TR>
-% } elsif ( $action eq 'complete' ) {
+% } else {
+
+%# if ( $action eq 'complete' ) {
<INPUT TYPE="hidden" NAME="action" VALUE="">
@@ -99,15 +105,18 @@
<TD ALIGN="center">
<BUTTON TYPE="button" onClick="document.pendingform.action.value = 'insert_cust_pay'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/tick.png" ALT=""-->Yes, transaction completed sucessfully.</BUTTON>
</TD>
- <TD>&nbsp;&nbsp;&nbsp;</TD>
- <TD ALIGN="center">
- <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'decline'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was declined</BUTTON>
- </TD>
- <TD>&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>
+
+% if ( $action eq 'complete' ) {
+ <TD>&nbsp;&nbsp;&nbsp;</TD>
+ <TD ALIGN="center">
+ <BUTTON TYPE="button" onClick="document.pendingform.action.value = 'decline'; document.pendingform.submit();"><!--IMG SRC="<%$p%>images/cross.png" ALT=""-->No, transaction was declined</BUTTON>
+ </TD>
+ <TD>&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><TD COLSPAN=5></TD></TR>
diff --git a/httemplate/edit/cust_refund.cgi b/httemplate/edit/cust_refund.cgi
index 59417b4..612e337 100755
--- a/httemplate/edit/cust_refund.cgi
+++ b/httemplate/edit/cust_refund.cgi
@@ -130,9 +130,6 @@
<%init>
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Refund payment');
-
my $conf = new FS::Conf;
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
@@ -143,6 +140,17 @@ my $payinfo = $cgi->param('payinfo');
my $reason = $cgi->param('reason');
my $link = $cgi->param('popup') ? 'popup' : '';
+my @rights = ();
+push @rights, 'Post refund' if $payby /^(BILL|CASH)$/;
+push @rights, 'Post check refund' if $payby eq 'BILL';
+push @rights, 'Post cash refund ' if $payby eq 'CASH';
+push @rights, 'Refund payment' if $payby /^(CARD|CHEK)$/;
+push @rights, 'Refund credit card payment' if $payby eq 'CARD';
+push @rights, 'Refund Echeck payment' if $payby eq 'CHEK';
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right(\@rights);
+
my( $paynum, $cust_pay ) = ( '', '' );
if ( $cgi->param('paynum') =~ /^(\d+)$/ ) {
$paynum = $1;
diff --git a/httemplate/edit/domain_record.html b/httemplate/edit/domain_record.html
new file mode 100644
index 0000000..3ea6c77
--- /dev/null
+++ b/httemplate/edit/domain_record.html
@@ -0,0 +1,53 @@
+<% include('/elements/header-popup.html', 'Edit nameservice record') %>
+
+<% include('/elements/error.html') %>
+
+<FORM METHOD="POST" ACTION="process/domain_record.cgi">
+
+<INPUT TYPE="hidden" NAME="recnum" VALUE="<% $opt{'recnum'} %>">
+
+<% ntable("#cccccc", 2) %>
+
+ <tr>
+ <td>
+ <INPUT TYPE="text" NAME="reczone" VALUE="<% $domain_record->reczone %>">
+ <BR>
+ <FONT SIZE="-1"><I>Zone</I></FONT>
+ </TD>
+ <TD>
+ <INPUT TYPE="hidden" NAME="recaf" VALUE="IN">
+ <SELECT NAME="rectype">
+% foreach ( @{ FS::domain_record->rectypes } ) {
+ <OPTION VALUE="<%$_%>"
+ <% $_ eq $domain_record->rectype ? 'SELECTED' : '' %>
+ >IN <%$_%></OPTION>
+% }
+ </SELECT><BR>
+ <FONT SIZE="-1"><I>Type</I></FONT>
+ </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="recdata" VALUE="<% $domain_record->recdata |h %>">
+ <BR>
+ <FONT SIZE="-1"><I>Data</I></FONT>
+ </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="ttl" size="6" VALUE="<% $domain_record->ttl %>">
+ <BR>
+ <FONT SIZE="-1"><I>TTL</I></FONT>
+ </TD>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Edit record">
+
+</FORM>
+
+<%init>
+
+my %opt = @_;
+
+my $domain_record = qsearchs('domain_record', { 'recnum' => $opt{'recnum'} } )
+ or die "unknown recnum";
+
+</%init>
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index b19b361..3d82847 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -292,7 +292,11 @@ Example:
%
% #select-table
% $include_common{$_} = $f->{$_}
-% foreach grep exists($f->{$_}), qw( value_col extra_sql );
+% foreach grep exists($f->{$_}), qw( value_col );
+% $include_common{$_} = ref( $f->{$_} ) eq 'CODE'
+% ? &{ $f->{$_} }( $cgi, $object ) #, $f )
+% : $f->{$_}
+% foreach grep exists($f->{$_}), qw( extra_sql );
%
% #select-table, checkboxes-table
% $include_common{$_} = $f->{$_}
diff --git a/httemplate/edit/msg_template.html b/httemplate/edit/msg_template.html
index 67eae18..be917d6 100644
--- a/httemplate/edit/msg_template.html
+++ b/httemplate/edit/msg_template.html
@@ -5,17 +5,19 @@
'viewall_dir' => 'browse',
'agent_virt' => 1,
'agent_null' => 1,
- 'agent_null_right' => 'Edit global templates',
+ 'agent_null_right' => ['Edit global templates', 'Configuration'],
'fields' => [ 'msgname',
- 'subject',
- 'from_addr',
+ { field=>'from_addr', size=>60, },
+ { field=>'bcc_addr', size=>60, },
+ { field=>'subject', size=>80, },
{ field=>'body', type=>'htmlarea', width=>763 },
],
'labels' => { 'msgnum' => 'Template',
'msgname' => 'Template name',
- 'from_addr' => 'Return address',
- 'subject' => 'Message subject',
+ 'from_addr' => 'From: ',
+ 'bcc_addr' => 'Bcc: ',
+ 'subject' => 'Subject: ',
'body' => 'Message template',
},
'html_foot' => "</TD>$sidebar</TR></TABLE>",
@@ -46,11 +48,13 @@ my %substitutions = (
'$classname' => 'Customer class',
'$categoryname' => 'Customer category',
'$balance' => 'Current balance',
+ '$credit_limit' => 'Credit limit',
'$invoicing_list_emailonly' => 'Billing email address',
'$cust_status' => 'Status',
'$ucfirst_cust_status' => 'Status, capitalized',
'$cust_statuscolor' => 'Status color code',
'$company_name' => 'Our company name',
+ '$company_address'=> 'Our company address',
],
'contact' => [ # duplicate this for shipping
'$name' => 'Company and contact name',
@@ -76,8 +80,8 @@ my %substitutions = (
],
'cust_pkg' => [
'$pkgnum' => 'Package#',
- '$pkg_label' => 'Package label (short)',
- '$pkg_label_long' => 'Package label (long)',
+ '$pkg' => 'Package description',
+ '$pkg_label' => 'Description + comment',
'$status' => 'Status',
'$statuscolor' => 'Status color code',
'$start_ymd' => 'Start date',
@@ -92,8 +96,10 @@ my %substitutions = (
'$location_label' => 'Service location',
],
'svc_acct' => [
+ '$svcnum' => 'Service#',
'$username' => 'Login name',
'$password' => 'Password',
+ '$domain' => 'Domain name',
],
'cust_pay' => [
'$paynum' => 'Payment#',
@@ -101,6 +107,7 @@ my %substitutions = (
'$payby' => 'Payment method',
'$date' => 'Payment date',
'$payinfo' => 'Card/account# (masked)',
+ '$error' => 'Decline reason',
],
);
my @c = @{ $substitutions{'contact'} };
diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi
index 8b697e1..a2fad56 100644
--- a/httemplate/edit/part_export.cgi
+++ b/httemplate/edit/part_export.cgi
@@ -77,7 +77,16 @@ my $widget = new HTML::Widgets::SelectLayers(
? $optinfo->{default}
: ''
);
- $html .= qq!<TR><TD ALIGN="right">$label</TD><TD>!;
+ # 'freeform': disables table formatting of options. Instead, each
+ # option can define "before" and "after" strings which are inserted
+ # around the selector.
+ my $freeform = $optinfo->{freeform};
+ if ( $freeform ) {
+ $html .= $optinfo->{before} || '';
+ }
+ else {
+ $html .= qq!<TR><TD ALIGN="right">$label</TD><TD>!;
+ }
if ( $type eq 'select' ) {
my $size = defined($optinfo->{size}) ? " SIZE=" . $optinfo->{size} : '';
my $multi = defined($optinfo->{multi}) ? ' MULTIPLE' : '';
@@ -108,7 +117,7 @@ my $widget = new HTML::Widgets::SelectLayers(
$html .= qq!<TEXTAREA NAME="$option" COLS=80 ROWS=8 WRAP="virtual">!.
encode_entities($value). '</TEXTAREA>';
} elsif ( $type eq 'text' ) {
- $html .= qq!<INPUT TYPE="text" NAME="$option" VALUE="!.
+ $html .= qq!<INPUT TYPE="text" NAME="$option" VALUE="!. #"
encode_entities($value). '" SIZE=64>';
} elsif ( $type eq 'checkbox' ) {
$html .= qq!<INPUT TYPE="checkbox" NAME="$option" VALUE="1"!;
@@ -117,7 +126,12 @@ my $widget = new HTML::Widgets::SelectLayers(
} else {
$html .= "unknown type $type";
}
- $html .= '</TD></TR>';
+ if ( $freeform ) {
+ $html .= $optinfo->{after} || '';
+ }
+ else {
+ $html .= '</TD></TR>';
+ }
}
$html .= '</TABLE>';
diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi
index deefa9c..be8b0f6 100755
--- a/httemplate/edit/part_pkg.cgi
+++ b/httemplate/edit/part_pkg.cgi
@@ -45,6 +45,7 @@
'agentnum' => 'Agent',
'setup_fee' => 'Setup fee',
'recur_fee' => 'Recurring fee',
+ 'discountnum' => 'Offer discounts for longer terms',
'bill_dst_pkgpart' => 'Include line item(s) from package',
'svc_dst_pkgpart' => 'Include services of package',
'report_option' => 'Report classes',
@@ -94,6 +95,7 @@
type => 'selectlayers-select',
options => [ keys %plan_labels ],
labels => \%plan_labels,
+ onchange => 'aux_planchanged(what);',
},
{ field => 'setup_fee',
type => 'money',
@@ -195,6 +197,21 @@
'multiple' => 1,
},
+ { 'type' => 'tablebreak-tr-title',
+ 'value' => 'Term discounts',
+ },
+ { 'field' => 'discountnum',
+ 'type' => 'select-table',
+ 'table' => 'discount',
+ 'name_col' => 'name',
+ 'hashref' => { %$discountnum_hashref },
+ #'extra_sql' => 'AND (months IS NOT NULL OR months != 0)',
+ 'empty_label'=> 'Select discount',
+ 'm2_label' => 'Offer discounts for longer terms',
+ 'm2m_method' => 'part_pkg_discount',
+ 'm2m_dstcol' => 'discountnum',
+ 'm2_error_callback' => $discount_error_callback,
+ },
{ 'type' => 'tablebreak-tr-title',
'value' => 'Pricing add-ons',
@@ -202,6 +219,10 @@
},
{ 'field' => 'bill_dst_pkgpart',
'type' => 'select-part_pkg',
+ 'extra_sql' => sub { $pkgpart
+ ? "AND pkgpart != $pkgpart"
+ : ''
+ },
'm2_label' => 'Include line item(s) from package',
'm2m_method' => 'bill_part_pkg_link',
'm2m_dstcol' => 'dst_pkgpart',
@@ -224,6 +245,10 @@
{ 'field' => 'svc_dst_pkgpart',
'label' => 'Also include services from package: ',
'type' => 'select-part_pkg',
+ 'extra_sql' => sub { $pkgpart
+ ? "AND pkgpart != $pkgpart"
+ : ''
+ },
'm2_label' => 'Include services of package: ',
'm2m_method' => 'svc_part_pkg_link',
'm2m_dstcol' => 'dst_pkgpart',
@@ -300,6 +325,8 @@ my @taxproductnums = ( qw( setup recur ), sort (keys %taxproductnums) );
my %options = ();
my $recur_disabled = 1;
+my $pkgpart = '';
+
my $error_callback = sub {
my($cgi, $object, $fields, $opt ) = @_;
@@ -333,6 +360,8 @@ my $error_callback = sub {
$object->set($_ => scalar($cgi->param($_)) )
foreach (qw( setup_fee recur_fee ));
+ $pkgpart = $object->pkgpart;
+
};
my $new_hashref_callback = sub { { 'plan' => 'flat' }; };
@@ -382,17 +411,22 @@ my $edit_callback = sub {
$object->set($_ => $object->option($_))
foreach (qw( setup_fee recur_fee ));
+ $pkgpart = $object->pkgpart;
+
};
my $new_callback = sub {
my( $cgi, $object, $fields ) = @_;
my $conf = new FS::Conf;
+
if ( $conf->exists('agent_defaultpkg') ) {
#my @all_agent_types = map {$_->typenum} qsearch('agent_type',{});
@agent_type = map {$_->typenum} qsearch('agent_type',{});
}
+ $options{'suspend_bill'}=1 if $conf->exists('part_pkg-default_suspend_bill');
+
};
my $clone_callback = sub {
@@ -426,6 +460,23 @@ my $clone_callback = sub {
$recur_disabled = $object->freq ? 0 : 1;
};
+my $discount_error_callback = sub {
+ my( $cgi, $object ) = @_;
+ map {
+ if ( /^discountnum(\d+)$/ &&
+ ( my $discountnum = $cgi->param("discountnum$1") ) )
+ {
+ new FS::part_pkg_discount {
+ 'pkgpart' => $object->pkgpart,
+ 'discountnum' => $discountnum,
+ };
+ } else {
+ ();
+ }
+ }
+ $cgi->param;
+};
+
my $m2_error_callback_maker = sub {
my $link_type = shift; #yay closures
return sub {
@@ -484,6 +535,22 @@ my $javascript = <<'END';
}
+ function aux_planchanged(what) {
+
+ alert('called!');
+ var plan = what.options[what.selectedIndex].value;
+ var table = document.getElementById('TableNumber7') // XXX NOT ROBUST
+
+ if ( plan == 'flat' || plan == 'prorate' || plan == 'subscription' ) {
+ //table.disabled = false;
+ table.style.visibility = '';
+ } else {
+ //table.disabled = true;
+ table.style.visibility = 'hidden';
+ }
+
+ }
+
</SCRIPT>
END
@@ -736,4 +803,9 @@ my $field_callback = sub {
}
};
+my $discountnum_hashref = {
+ 'disabled' => '',
+ 'months' => { 'op' => '>', 'value' => 1 },
+ };
+
</%init>
diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi
index 6fe015a..940ea8d 100755
--- a/httemplate/edit/part_svc.cgi
+++ b/httemplate/edit/part_svc.cgi
@@ -291,12 +291,22 @@ that field.
% (grep(/^$rvalue$/, split(',',$value)) ? ' SELECTED>' : '>' ).
% $record->$select_label(). '</OPTION>';
% } #next $record
-% } else { # select_list
+% } elsif ( $def->{select_list} ) {
% foreach my $item ( @{$def->{select_list}} ) {
% $html .= qq!<OPTION VALUE="$item"!.
% (grep(/^$item$/, split(',',$value)) ? ' SELECTED>' : '>' ).
% $item. '</OPTION>';
% } #next $item
+% } elsif ( $def->{select_hash} ) {
+% if ( ref($def->{select_hash}) eq 'ARRAY' ) {
+% tie my %hash, 'Tie::IxHash', @{ $def->{select_hash} };
+% $def->{select_hash} = \%hash;
+% }
+% foreach my $key ( keys %{$def->{select_hash}} ) {
+% $html .= qq!<OPTION VALUE="$key"!.
+% (grep(/^$key$/, split(',',$value)) ? ' SELECTED>' : '>' ).
+% $def->{select_hash}{$key}. '</OPTION>';
+% } #next $key
% } #endif
% $html .= '</SELECT>';
%
diff --git a/httemplate/edit/process/REAL_cust_pkg.cgi b/httemplate/edit/process/REAL_cust_pkg.cgi
index 570f0e0..3a62ee0 100755
--- a/httemplate/edit/process/REAL_cust_pkg.cgi
+++ b/httemplate/edit/process/REAL_cust_pkg.cgi
@@ -20,7 +20,7 @@ my $pkgnum = $cgi->param('pkgnum') or die;
my $old = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
my %hash = $old->hash;
$hash{$_}= $cgi->param($_) ? parse_datetime($cgi->param($_)) : ''
- foreach qw( start_date setup bill last_bill adjourn expire );
+ foreach qw( start_date setup bill last_bill adjourn expire contract_end );
my @errors = ();
diff --git a/httemplate/edit/process/access_user.html b/httemplate/edit/process/access_user.html
index e6258a9..8e7e70a 100644
--- a/httemplate/edit/process/access_user.html
+++ b/httemplate/edit/process/access_user.html
@@ -10,6 +10,7 @@
'process_m2m' => { 'link_table' => 'access_usergroup',
'target_table' => 'access_group',
},
+ 'precheck_callback'=> \&precheck_callback,
)
%>
% }
@@ -23,4 +24,13 @@ if ( FS::Conf->new->exists('disable_acl_changes') ) {
die "shouldn't be reached";
}
+sub precheck_callback {
+ my $cgi = shift;
+ my $o = FS::access_user->new({username => $cgi->param('username')});
+ if( $o->is_system_user and !$cgi->param('usernum') ) {
+ $cgi->param('username','');
+ return "username '".$o->username."' reserved for system account."
+ }
+ return '';
+}
</%init>
diff --git a/httemplate/edit/process/acct_snarf.html b/httemplate/edit/process/acct_snarf.html
new file mode 100644
index 0000000..332ac52
--- /dev/null
+++ b/httemplate/edit/process/acct_snarf.html
@@ -0,0 +1,20 @@
+<% include( 'elements/process.html',
+ 'table' => 'acct_snarf',
+ 'redirect' => $redirect,
+ 'noerror_callback' => sub {
+ my( $cgi, $object ) = @_;
+ my $error = $object->svc_export;
+ #shit, not a good place for error handling :/
+ die $error if $error;
+ },
+ )
+%>
+<%init>
+
+my $redirect = sub {
+ my($cgi, $new) = @_;
+ my $svcnum = $new->svcnum;
+ popurl(3)."browse/acct_snarf.html?svcnum=$svcnum;snarfnum=";
+};
+
+</%init>
diff --git a/httemplate/edit/process/bulk-cust_pkg.cgi b/httemplate/edit/process/bulk-cust_pkg.cgi
new file mode 100644
index 0000000..ede3ee8
--- /dev/null
+++ b/httemplate/edit/process/bulk-cust_pkg.cgi
@@ -0,0 +1,9 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $server = new FS::UI::Web::JSRPC 'FS::cust_pkg::process_bulk_cust_pkg', $cgi;
+
+</%init>
diff --git a/httemplate/edit/process/cgp_rule-redirect_all.html b/httemplate/edit/process/cgp_rule-redirect_all.html
new file mode 100644
index 0000000..162d857
--- /dev/null
+++ b/httemplate/edit/process/cgp_rule-redirect_all.html
@@ -0,0 +1,24 @@
+<% include('cgp_rule-simplified.html',
+ 'name' => '#Redirect',
+ 'priority' => 1,
+ 'redirect' => 'cgp_rule-redirect_all.html',
+ 'conditions' => [
+ ( $cgi->param('RedirHuman')
+ ? { conditionname => 'Human Generated', }
+ : ()
+ ),
+ ],
+ 'actions' => [
+ { action => ( $cgi->param('KeepToAndCc')
+ ? 'Mirror To'
+ : 'Redirect To'
+ ),
+ params => scalar($cgi->param('RedirectText')),
+ },
+ ( $cgi->param('RedirKeep')
+ ? ()
+ : ( { 'action' => 'Discard' } )
+ ),
+ ],
+ )
+%>
diff --git a/httemplate/edit/process/cgp_rule-simplified.html b/httemplate/edit/process/cgp_rule-simplified.html
new file mode 100644
index 0000000..60769d4
--- /dev/null
+++ b/httemplate/edit/process/cgp_rule-simplified.html
@@ -0,0 +1,53 @@
+% if ( $error ) { #redirect back to edit...
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(3).'edit/'.$opt{'redirect'}.'?'. $cgi->query_string) %>
+% } 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();
+ </SCRIPT>
+
+ </BODY>
+ </HTML>
+% }
+<%init>
+
+my %opt = @_;
+
+my %hash = (
+ 'svcnum' => scalar($cgi->param('svcnum')),
+ 'name' => $opt{'name'},
+);
+
+my $cgp_rule = qsearchs('cgp_rule', \%hash);
+
+my $error = '';
+if ( $cgp_rule ) { #updating
+ $error = $cgp_rule->delete;
+}
+
+$cgp_rule = new FS::cgp_rule { %hash, 'priority' => $opt{'priority'} };
+$error ||= $cgp_rule->insert;
+
+foreach my $condition ( @{ $opt{'conditions'} } ) {
+ my $cgp_rule_condition = new FS::cgp_rule_condition {
+ %$condition,
+ 'rulenum' => $cgp_rule->rulenum,
+ };
+ $error ||= $cgp_rule_condition->insert;
+}
+
+foreach my $action ( @{ $opt{'actions'} } ) {
+ my $cgp_rule_action = new FS::cgp_rule_action {
+ %$action,
+ 'rulenum' => $cgp_rule->rulenum,
+ };
+ $error ||= $cgp_rule_action->insert;
+}
+
+unless ( $error ) {
+ my $export_error = $cgp_rule->svc_export;
+ die $export_error if $export_error; #error handling sucks wrt this... shouldn't happen though
+}
+
+</%init>
diff --git a/httemplate/edit/process/cgp_rule-vacation.html b/httemplate/edit/process/cgp_rule-vacation.html
new file mode 100644
index 0000000..f10d72b
--- /dev/null
+++ b/httemplate/edit/process/cgp_rule-vacation.html
@@ -0,0 +1,29 @@
+<% include('cgp_rule-simplified.html',
+ 'name' => '#Vacation',
+ 'priority' => 2,
+ 'redirect' => 'cgp_rule-vacation.html',
+ 'conditions' => [
+ { conditionname => 'Human Generated', },
+ { conditionname => 'From',
+ op => 'not in',
+ params => '#RepliedAddresses',
+ },
+ ( $cgi->param('VacationTill')
+ ? ( { conditionname => 'Current Date',
+ op => 'less than', #is less?
+ params => scalar($cgi->param('VacationTill')),
+ }
+ )
+ : ()
+ ),
+ ],
+ 'actions' => [
+ { action => 'Reply with',
+ params => scalar($cgi->param('VacationText')),
+ },
+ { action => "Remember 'From' in",
+ params => 'RepliedAddresses',
+ },
+ ],
+ )
+%>
diff --git a/httemplate/edit/process/cust_main.cgi b/httemplate/edit/process/cust_main.cgi
index 3158d7b..24cecea 100755
--- a/httemplate/edit/process/cust_main.cgi
+++ b/httemplate/edit/process/cust_main.cgi
@@ -73,6 +73,10 @@ if ( defined($cgi->param('same')) && $cgi->param('same') eq "Y" ) {
);
}
+if ( $cgi->param('no_credit_limit') ) {
+ $new->setfield('credit_limit', '');
+}
+
$new->tagnum( [ $cgi->param('tagnum') ] );
my %usedatetime = ( 'birthdate' => 1 );
@@ -247,6 +251,11 @@ if ( $new->custnum eq '' ) {
$new->payinfo($new_account.'@'.$new_aba);
}
+ if ( ! $conf->exists('cust_main-edit_signupdate') or
+ ! $new->signupdate ) {
+ $new->signupdate($old->signupdate);
+ }
+
warn "$me calling $new -> replace( $old, \ @invoicing_list )" if $DEBUG;
local($FS::cust_main::DEBUG) = $DEBUG if $DEBUG;
local($FS::Record::DEBUG) = $DEBUG if $DEBUG;
diff --git a/httemplate/edit/process/cust_pay.cgi b/httemplate/edit/process/cust_pay.cgi
index df506c6..d6bbf06 100755
--- a/httemplate/edit/process/cust_pay.cgi
+++ b/httemplate/edit/process/cust_pay.cgi
@@ -27,9 +27,6 @@
%}
<%init>
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Post payment');
-
$cgi->param('linknum') =~ /^(\d+)$/
or die "Illegal linknum: ". $cgi->param('linknum');
my $linknum = $1;
@@ -47,11 +44,18 @@ my $new = new FS::cust_pay ( {
map {
$_, scalar($cgi->param($_));
} qw( paid payby payinfo paybatch
- pkgnum
+ pkgnum discount_term
)
#} fields('cust_pay')
} );
+my @rights = ('Post payment');
+push @rights, 'Post check payment' if $new->payby eq 'BILL';
+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 );
</%init>
diff --git a/httemplate/edit/process/cust_refund.cgi b/httemplate/edit/process/cust_refund.cgi
index 5749e53..389bc99 100755
--- a/httemplate/edit/process/cust_refund.cgi
+++ b/httemplate/edit/process/cust_refund.cgi
@@ -28,8 +28,21 @@ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
my $link = $cgi->param('popup') ? 'popup' : '';
+my $payby = $cgi->param('payby');
+
+my @rights = ();
+push @rights, 'Post refund' if $payby /^(BILL|CASH)$/;
+push @rights, 'Post check refund' if $payby eq 'BILL';
+push @rights, 'Post cash refund ' if $payby eq 'CASH';
+push @rights, 'Refund payment' if $payby /^(CARD|CHEK)$/;
+push @rights, 'Refund credit card payment' if $payby eq 'CARD';
+push @rights, 'Refund Echeck payment' if $payby eq 'CHEK';
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right(\@rights);
+
my $error = '';
-if ( $cgi->param('payby') =~ /^(CARD|CHEK)$/ ) {
+if ( $payby =~ /^(CARD|CHEK)$/ ) {
my %options = ();
my $bop = $FS::payby::payby2bop{$1};
$cgi->param('refund') =~ /^(\d*)(\.\d{2})?$/
diff --git a/httemplate/edit/process/domain_record.cgi b/httemplate/edit/process/domain_record.cgi
index 2e427e4..8369f71 100755
--- a/httemplate/edit/process/domain_record.cgi
+++ b/httemplate/edit/process/domain_record.cgi
@@ -1,8 +1,14 @@
%if ( $error ) {
% errorpage($error);
-%} else {
+%} elsif ( $recnum ) { #editing
+<% header('Nameservice record changed') %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+ </BODY></HTML>
+%} else { #adding
% my $svcnum = $new->svcnum;
-<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum") %>
+<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum#dns") %>
%}
<%init>
@@ -11,7 +17,7 @@ die "access denied"
my $recnum = $cgi->param('recnum');
-my $old = qsearchs('agent',{'recnum'=>$recnum}) if $recnum;
+my $old = qsearchs('domain_record',{'recnum'=>$recnum}) if $recnum;
my $new = new FS::domain_record ( {
map {
@@ -21,10 +27,11 @@ my $new = new FS::domain_record ( {
my $error;
if ( $recnum ) {
- $error=$new->replace($old);
+ $new->svcnum( $old->svcnum );
+ $error = $new->replace($old);
} else {
- $error=$new->insert;
- $recnum=$new->getfield('recnum');
+ $error = $new->insert;
+ #$recnum = $new->getfield('recnum');
}
</%init>
diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi
index c0febf8..97ae4e7 100755
--- a/httemplate/edit/process/part_pkg.cgi
+++ b/httemplate/edit/process/part_pkg.cgi
@@ -103,7 +103,7 @@ my $args_callback = sub {
$options{"usage_taxproductnum_$_"} = $value;
}
- foreach ( $cgi->param('report_option') ) {
+ foreach ( grep $_, $cgi->param('report_option') ) {
$error ||= "Illegal optional report class: $_" unless ( $_ =~ /^\d*$/ );
$options{"report_option_$_"} = 1;
}
@@ -160,6 +160,12 @@ my @process_m2m = (
'target_table' => 'tax_class',
'params' => \@tax_overrides,
},
+ { 'link_table' => 'part_pkg_discount',
+ 'target_table' => 'discount',
+ 'params' => [ map $cgi->param($_),
+ grep /^discountnum/, $cgi->param
+ ],
+ },
{ 'link_table' => 'part_pkg_link',
'target_table' => 'part_pkg',
'base_field' => 'src_pkgpart',
diff --git a/httemplate/edit/process/prospect_main.html b/httemplate/edit/process/prospect_main.html
index 34d2642..ca4dfab 100644
--- a/httemplate/edit/process/prospect_main.html
+++ b/httemplate/edit/process/prospect_main.html
@@ -4,7 +4,7 @@
'agent_virt' => 1,
'process_o2m' => {
'table' => 'contact',
- 'fields' => [qw( first last title comment )],
+ 'fields' => \@contact_fields,
},
'redirect' => popurl(3). 'view/prospect_main.html?',
)
@@ -31,4 +31,9 @@ my $args_callback = sub {
};
+my @contact_fields = qw( first last title comment emailaddress );
+foreach my $phone_type ( qsearch({table=>'phone_type', order_by=>'weight'}) ) {
+ push @contact_fields, 'phonetypenum'.$phone_type->phonetypenum;
+}
+
</%init>
diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi
index 2fde17f..599f760 100644
--- a/httemplate/edit/process/quick-cust_pkg.cgi
+++ b/httemplate/edit/process/quick-cust_pkg.cgi
@@ -66,6 +66,10 @@ my $cust_pkg = new FS::cust_pkg {
'discountnum_amount' => scalar($cgi->param('discountnum_amount')),
'discountnum_percent' => scalar($cgi->param('discountnum_percent')),
'discountnum_months' => scalar($cgi->param('discountnum_months')),
+ 'contract_end' => ( scalar($cgi->param('contract_end'))
+ ? parse_datetime($cgi->param('contract_end'))
+ : ''
+ ),
#'discountnum_disabled' => scalar($cgi->param('discountnum_disabled')),
};
@@ -74,7 +78,7 @@ my %opt = ( 'cust_pkg' => $cust_pkg );
if ( $locationnum == -1 ) {
my $cust_location = new FS::cust_location {
map { $_ => scalar($cgi->param($_)) }
- qw( custnum address1 address2 city county state zip country )
+ qw( custnum address1 address2 city county state zip country geocode )
};
$opt{'cust_location'} = $cust_location;
}
diff --git a/httemplate/edit/process/rate_time.cgi b/httemplate/edit/process/rate_time.cgi
index 4fa78ce..2b00be3 100644
--- a/httemplate/edit/process/rate_time.cgi
+++ b/httemplate/edit/process/rate_time.cgi
@@ -4,7 +4,6 @@
% } else {
<% $cgi->redirect(popurl(3). "browse/rate_time.html" ) %>
% }
-%# dumper_html(\%vars, \%old_ints, {$rate_time->intervals}) %>
<%init>
my $error = '';
die "access denied"
@@ -87,7 +86,7 @@ if(!$error) {
sub l2wtime {
my ($d, $h, $m, $a) = @_;
- $h += 24*$d + 12*$a;
+ $h = ($h % 12) + 24*$d + 12*$a;
$m += 60*$h;
return 60*$m
}
diff --git a/httemplate/edit/process/svc_acct.cgi b/httemplate/edit/process/svc_acct.cgi
index 0b272b5..ba21ab4 100755
--- a/httemplate/edit/process/svc_acct.cgi
+++ b/httemplate/edit/process/svc_acct.cgi
@@ -70,7 +70,7 @@ if ( $svcnum ) {
grep { $new->$_ }
qw( seconds upbytes downbytes totalbytes );
- $error ||= "invalid $_" foreach grep { $hash{$_} !~ /^\d+$/ } keys %hash;
+ $error ||= "invalid $_" foreach grep { $hash{$_} !~ /^-?\d+$/ } keys %hash;
$error ||= $new->set_usage(\%hash); #unoverlimit and trigger radius changes
last; #once is enough
}
diff --git a/httemplate/edit/process/svc_domain-defaultrecords.cgi b/httemplate/edit/process/svc_domain-defaultrecords.cgi
new file mode 100644
index 0000000..ec3d221
--- /dev/null
+++ b/httemplate/edit/process/svc_domain-defaultrecords.cgi
@@ -0,0 +1,18 @@
+% if ( $error ) {
+% errorpage($error);
+% } else {
+<% $cgi->redirect(popurl(3). "view/svc_domain.cgi?$svcnum#dns") %>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice');
+
+my $svcnum = scalar($cgi->param('svcnum'));
+
+my $svc_domain = qsearchs('svc_domain', { 'svcnum' => $svcnum })
+ or die 'unknown svc_domain.svcnum';
+
+my $error = $svc_domain->insert_defaultrecords;
+
+</%init>
diff --git a/httemplate/edit/prospect_main-ocr.html b/httemplate/edit/prospect_main-ocr.html
new file mode 100644
index 0000000..41fc4c1
--- /dev/null
+++ b/httemplate/edit/prospect_main-ocr.html
@@ -0,0 +1,86 @@
+<% include("/elements/header.html", 'Upload business card' ) %>
+
+% if ( $error ) {
+ <FONT SIZE="+1" COLOR="#ff0000">Error: <% $error %></FONT>
+ <BR><BR>
+% } else {
+
+ <FORM ACTION="prospect_main.html" METHOD="POST">
+ <INPUT TYPE="hidden" NAME="session" VALUE="<% $session %>">
+
+ <TABLE>
+
+% my $num = 0;
+% foreach my $line ( @lines ) {
+ <TR>
+ <TD>
+ <INPUT TYPE="hidden" NAME="val<%$num%>" VALUE="<% $line |h %>">
+ <SELECT NAME="sel<%$num%>">
+ <OPTION VALUE="">
+ <OPTION VALUE="name">Name
+ <OPTION VALUE="contactnum0_title">Title
+ <OPTION VALUE="company">Company
+ <OPTION VALUE="contactnum0_emailaddress">Email
+ <OPTION VALUE="address1">Address (1)
+ <OPTION VALUE="address2">Address (2)
+ <OPTION VALUE="city_state_zip">City, State, Zip
+% my @phone_types = qsearch({table=>'phone_type',order_by=>'weight'});
+% foreach my $phone_type ( @phone_types ) {
+% next if $phone_type->typename eq 'Home';
+ <OPTION VALUE="contactnum0_phonetypenum<% $phone_type->phonetypenum %>"><% $phone_type->typename |h %> phone
+% }
+ <OPTION VALUE="contactnum0_comment">Comment
+ </SELECT>
+ </TD>
+ <TD><% $line %></TD>
+
+% unless ( $num++) {
+
+ <TD ROWSPAN="9999"><IMG SRC="<%$p%>view/image.cgi?type=png;prefname=bizcard<%$session%>" WIDTH=604 HEIGHT=328></IMG></TD>
+
+% }
+
+ </TR>
+% }
+
+ </TABLE>
+
+ <BR>
+ <INPUT TYPE="submit" VALUE="Create prospect">
+
+% }
+<% include('/elements/footer.html') %>
+<%init>
+
+my $fh = $cgi->upload('card');
+
+my $error = '';
+my @lines = ();
+my $session = '';
+if ( defined $fh ) {
+
+ local $/;
+ my $logo_data = <$fh>;
+
+ $session = int(rand(4294967296)); #XXX
+ my $pref = new FS::access_user_pref({
+ 'usernum' => $FS::CurrentUser::CurrentUser->usernum,
+ 'prefname' => "bizcard$session",
+ 'prefvalue' => encode_base64($logo_data),
+ 'expiration' => time + 3600, #1h? 1m?
+ });
+ my $pref_error = $pref->insert;
+ if ( $pref_error ) {
+ die "FATAL: couldn't set preview cookie: $pref_error\n";
+ }
+
+ @lines = eval { ocr_image($logo_data); };
+ $error = $@ if $error;
+
+} else {
+
+ $error = 'No file uploaded';
+
+}
+
+</%init>
diff --git a/httemplate/edit/prospect_main-upload.html b/httemplate/edit/prospect_main-upload.html
new file mode 100644
index 0000000..24b1caa
--- /dev/null
+++ b/httemplate/edit/prospect_main-upload.html
@@ -0,0 +1,7 @@
+<% include("/elements/header.html", 'Upload business card' ) %>
+
+ <FORM ACTION="prospect_main-ocr.html" METHOD="POST" ENCTYPE="multipart/form-data">
+ <INPUT TYPE="file" NAME="card">
+ <BR><INPUT TYPE="submit" NAME="submit" VALUE="Upload">
+
+<% include('/elements/footer.html') %>
diff --git a/httemplate/edit/prospect_main.html b/httemplate/edit/prospect_main.html
index e867907..c260eb8 100644
--- a/httemplate/edit/prospect_main.html
+++ b/httemplate/edit/prospect_main.html
@@ -5,6 +5,7 @@
'agentnum' => 'Agent',
'company' => 'Company',
'contactnum' => 'Contact',
+ 'locationnum' => '&nbsp;',
},
'fields' => [
{ 'field' => 'agentnum',
@@ -34,6 +35,7 @@
'empty_label' => 'No address',
},
],
+ 'new_callback' => $new_callback,
'edit_callback' => $edit_callback,
'error_callbacck' => $error_callback,
'agent_virt' => 1,
@@ -62,6 +64,48 @@ if ( $cgi->param('error') ) {
}
+my $new_callback = sub {
+ my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_;
+
+ if ( $cgi->param('session') =~ /^(\w+)$/ ) {
+ my $session = $1;
+
+ #add a link to the image.cgi for this card
+ $opt_hashref->{'html_bottom'} .=
+ qq(<BR><IMG SRC="${p}view/image.cgi?type=png;prefname=bizcard$session" ).
+ ' WIDTH=604 HEIGHT=328><BR>';
+
+ #fill in the incoming params: name, address1/address2, city_state_zip
+ foreach my $param ( grep /^sel\d+$/, $cgi->param ) {
+ $param =~ /^sel(\d+)$/ or die 'again, wtf (daily)';
+ my $num = $1;
+ my $field = $cgi->param($param);
+ my $value = $cgi->param("val$num");
+ $cgi->param($field => $value);
+ }
+
+ if ( $cgi->param('company') ) {
+ $prospect_main->company( $cgi->param('company') );
+ }
+
+ if ( $cgi->param('name') =~ /^(.*\S+)\s+(\w+)\s*$/ ) {
+ $cgi->param('contactnum0_first' => $1);
+ $cgi->param('contactnum0_last' => $2);
+ }
+
+ if ( grep $cgi->param($_), qw( address1 address2 city_state_zip ) ) {
+ $cgi->param('locationnum', -1);
+ if ( $cgi->param('city_state_zip') =~ /^(\s*)([\w\s]+)[\., ]+(\w{2})[, ]+(\d{5}(-\d{4})?)/ ) {
+ $cgi->param('city' => $2);
+ $cgi->param('state' => $3);
+ $cgi->param('zip' => $4);
+ }
+ }
+
+ }
+
+};
+
my $edit_callback = sub {
#my( $cgi, $prospect_main, $fields_listref, $opt_hashref ) = @_;
my( $cgi, $prospect_main ) = @_;
diff --git a/httemplate/edit/rate_time.cgi b/httemplate/edit/rate_time.cgi
index eca8fbb..7ee39ef 100644
--- a/httemplate/edit/rate_time.cgi
+++ b/httemplate/edit/rate_time.cgi
@@ -39,7 +39,7 @@ my $day = [ 0 => 'Sun',
4 => 'Thu',
5 => 'Fri',
6 => 'Sat', ];
-my $hour = [ map( {$_, sprintf('%02d',$_) } 0..11 )];
+my $hour = [ map( {$_, sprintf('%02d',$_) } 12, 1..11 )];
my $min = [ map( {$_, sprintf('%02d',$_) } 0,30 )];
my $ampm = [ 0 => 'AM', 1 => 'PM' ];
@@ -57,7 +57,7 @@ if($ratetimenum) {
else {
foreach my $interval ($rate_time->intervals) {
push @data, [ map { int($_/86400) % 7,
- int($_/3600) % 12,
+ (int($_/3600) % 12 || 12),
int($_/60) % 60,
int($_/43200) % 2, }
( $interval->stime, $interval->etime )
diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi
index 59b5d10..0a191b4 100755
--- a/httemplate/edit/svc_acct.cgi
+++ b/httemplate/edit/svc_acct.cgi
@@ -255,222 +255,12 @@ Service # <% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR>
</TR>
% }
-
-% if ( $communigate
-% && $part_svc->part_svc_column('cgp_type')->columnflag ne 'F' )
-% {
-
-% # settings
-
- <TR>
- <TD ALIGN="right">Mailbox type</TD>
- <TD>
- <SELECT NAME="cgp_type">
-% foreach my $option (qw( MultiMailbox TextMailbox MailDirMailbox
-% AGrade BGrade CGrade )) {
- <OPTION VALUE="<% $option %>"
- <% $option eq $svc_acct->cgp_type() ? 'SELECTED' : '' %>
- ><% $option %>
-% }
- </SELECT>
- </TD>
- </TR>
-
-% } else {
- <INPUT TYPE="hidden" NAME="cgp_type" VALUE="<% $svc_acct->cgp_type() %>">
-% }
-
-
-% #false laziness w/svc_domain
-% if ( $communigate
-% && $part_svc->part_svc_column('cgp_accessmodes')->columnflag ne 'F' )
-% {
-
- <TR>
- <TD ALIGN="right">Enabled services</TD>
- <TD>
- <% include( '/elements/communigate_pro-accessmodes.html',
- 'curr_value' => $svc_acct->cgp_accessmodes,
- )
- %>
- </TD>
- </TR>
-
-% } else {
- <INPUT TYPE="hidden" NAME="cgp_accessmodes" VALUE="<% $svc_acct->cgp_accessmodes() |h %>">
-% }
-
-
-% 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';
- <TR>
- <TD ALIGN="right"><% $quota_label %></TD>
- <TD><INPUT TYPE="text" NAME="quota" VALUE="<% $svc_acct->quota %>"></TD>
- </TR>
-% }
-
-% tie my %cgp_label, 'Tie::IxHash',
-% 'file_quota' => 'File storage limit',
-% 'file_maxnum' => 'Number of files limit',
-% 'file_maxsize' => 'File size limit',
-% ;
-%
-% foreach my $key (keys %cgp_label) {
-%
-% if ( !$communigate || $part_svc->part_svc_column($key)->columnflag eq 'F' ){
- <INPUT TYPE="hidden" NAME="<%$key%>" VALUE="<% $svc_acct->$key() |h %>">
-% } else {
-
- <TR>
- <TD ALIGN="right"><% $cgp_label{$key} %></TD>
- <TD><INPUT TYPE="text" NAME="<% $key %>" VALUE="<% $svc_acct->$key() |h %>"></TD>
- </TR>
-
-% }
-% }
-
-% if ( $communigate ) {
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'Password recovery',
- 'field' => 'password_recover',
- 'curr_value' => $svc_acct->password_recover,
- 'value' => 'Y',
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Allowed mail rules',
- 'field' => 'cgp_rulesallowed',
- 'options' => [ '', 'No', 'Filter Only', 'All But Exec', 'Any' ],
- 'labels' => {
- '' => 'default (No)', #No always the default?
- },
- 'curr_value' => $svc_acct->cgp_rulesallowed,
- )
- %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'RPOP modifications',
- 'field' => 'cgp_rpopallowed',
- 'curr_value' => $svc_acct->cgp_rpopallowed,
- 'value' => 'Y',
- )
- %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'Accepts mail to "all"',
- 'field' => 'cgp_mailtoall',
- 'curr_value' => $svc_acct->cgp_mailtoall,
- 'value' => 'Y',
- )
- %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'Add trailer to sent mail',
- 'field' => 'cgp_addmailtrailer',
- 'curr_value' => $svc_acct->cgp_addmailtrailer,
- 'value' => 'Y',
- )
- %>
-
-% #preferences
-
-%# false laziness w/svc_domain acct_def
- <TR>
- <TD ALIGN="right">Message delete method</TD>
- <TD>
- <SELECT NAME="cgp_deletemode">
-% for ( 'Move To Trash', 'Immediately', 'Mark' ) {
- <OPTION VALUE="<% $_ %>"
- <% $_ eq $svc_acct->cgp_deletemode ? 'SELECTED' : '' %>
- ><% $_ %>
-% }
- </SELECT>
- </TD>
- </TR>
-
- <% include('/elements/tr-select.html',
- 'label' => 'On logout remove trash',
- 'field' => 'cgp_emptytrash',
- 'options' => $svc_acct->cgp_emptytrash_values,
- 'labels' => {
- '' => 'default (92 days)', #right?
- },
- 'curr_value' => $svc_acct->cgp_emptytrash,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Language',
- 'field' => 'cgp_language',
- 'options' => [ '', qw( English Arabic Chinese Dutch French German Hebrew Italian Japanese Portuguese Russian Slovak Spanish Thai ) ],
- 'labels' => {
- '' => 'default (English)',
- },
- 'curr_value' => $svc_acct->cgp_language,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Time zone',
- 'field' => 'cgp_timezone',
- 'options' => $svc_acct->cgp_timezone_values,
- 'labels' => {
- '' => 'default (HostOS)',
- },
- 'curr_value' => $svc_acct->cgp_timezone,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Layout',
- 'field' => 'cgp_skinname',
- 'options' => [ '', '***', 'GoldFleece', 'Skin2' ],
- 'labels' => {
- '' => 'default (***)',
- },
- 'curr_value' => $svc_acct->cgp_skinname,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Pronto style',
- 'field' => 'cgp_prontoskinname',
- 'options' => [ '', 'Pronto', 'Pronto-darkflame', 'Pronto-steel', 'Pronto-twilight', ],
-
- 'curr_value' => $svc_acct->cgp_prontoskinname,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Send read receipts',
- 'field' => 'cgp_sendmdnmode',
- 'options' => [ '', 'Never', 'Manually', 'Automatically' ],
- 'labels' => {
- '' => 'default (Automatically)',
- },
- 'curr_value' => $svc_acct->cgp_language,
- )
- %>
-
-%#XXX vacation message, redirect all mail, mail rules
-
-% } else {
-
-% for (qw( password_recover cgp_rulesallowed cgp_rpopallowed cgp_mailtoall
-% cgp_addmailtrailer
-% cgp_deletemode cgp_emptytrash cgp_language cgp_timezone
-% cgp_skinname cgp_sendmdnmode
-% ) ) {
-
- <INPUT TYPE="hidden" NAME="<% $_ %>" VALUE="<% $svc_acct->$_() %>">
-% }
-
-% }
-
+<% include('svc_acct/communigate.html',
+ 'svc_acct' => $svc_acct,
+ 'part_svc' => $part_svc,
+ 'communigate' => $communigate,
+ )
+%>
% if ( $part_svc->part_svc_column('slipip')->columnflag =~ /^[FA]$/ ) {
<INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>">
diff --git a/httemplate/edit/svc_acct/communigate.html b/httemplate/edit/svc_acct/communigate.html
new file mode 100644
index 0000000..6370a54
--- /dev/null
+++ b/httemplate/edit/svc_acct/communigate.html
@@ -0,0 +1,249 @@
+% if ( $communigate
+% && $part_svc->part_svc_column('cgp_type')->columnflag ne 'F' )
+% {
+
+% # settings
+
+ <TR>
+ <TD ALIGN="right">Mailbox type</TD>
+ <TD>
+ <SELECT NAME="cgp_type">
+% foreach my $option (qw( MultiMailbox TextMailbox MailDirMailbox
+% AGrade BGrade CGrade )) {
+ <OPTION VALUE="<% $option %>"
+ <% $option eq $svc_acct->cgp_type() ? 'SELECTED' : '' %>
+ ><% $option %>
+% }
+ </SELECT>
+ </TD>
+ </TR>
+
+% } else {
+ <INPUT TYPE="hidden" NAME="cgp_type" VALUE="<% $svc_acct->cgp_type() %>">
+% }
+
+
+% #false laziness w/svc_domain
+% if ( $communigate
+% && $part_svc->part_svc_column('cgp_accessmodes')->columnflag ne 'F' )
+% {
+
+ <TR>
+ <TD ALIGN="right">Enabled services</TD>
+ <TD>
+ <% include( '/elements/communigate_pro-accessmodes.html',
+ 'curr_value' => $svc_acct->cgp_accessmodes,
+ )
+ %>
+ </TD>
+ </TR>
+
+% } else {
+ <INPUT TYPE="hidden" NAME="cgp_accessmodes" VALUE="<% $svc_acct->cgp_accessmodes() |h %>">
+% }
+
+
+% 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';
+ <TR>
+ <TD ALIGN="right"><% $quota_label %></TD>
+ <TD><INPUT TYPE="text" NAME="quota" VALUE="<% $svc_acct->quota %>"></TD>
+ </TR>
+% }
+
+% tie my %cgp_label, 'Tie::IxHash',
+% 'file_quota' => 'File storage limit',
+% 'file_maxnum' => 'Number of files limit',
+% 'file_maxsize' => 'File size limit',
+% ;
+%
+% foreach my $key (keys %cgp_label) {
+%
+% if ( !$communigate || $part_svc->part_svc_column($key)->columnflag eq 'F' ){
+ <INPUT TYPE="hidden" NAME="<%$key%>" VALUE="<% $svc_acct->$key() |h %>">
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $cgp_label{$key} %></TD>
+ <TD><INPUT TYPE="text" NAME="<% $key %>" VALUE="<% $svc_acct->$key() |h %>"></TD>
+ </TR>
+
+% }
+% }
+
+% if ( $communigate ) {
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'Password recovery',
+ 'field' => 'password_recover',
+ 'curr_value' => $svc_acct->password_recover,
+ 'value' => 'Y',
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Allowed mail rules',
+ 'field' => 'cgp_rulesallowed',
+ 'options' => [ '', 'No', 'Filter Only', 'All But Exec', 'Any' ],
+ 'labels' => {
+ '' => 'default (No)', #No always the default?
+ },
+ 'curr_value' => $svc_acct->cgp_rulesallowed,
+ )
+ %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'RPOP modifications',
+ 'field' => 'cgp_rpopallowed',
+ 'curr_value' => $svc_acct->cgp_rpopallowed,
+ 'value' => 'Y',
+ )
+ %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'Accepts mail to "all"',
+ 'field' => 'cgp_mailtoall',
+ 'curr_value' => $svc_acct->cgp_mailtoall,
+ 'value' => 'Y',
+ )
+ %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'Add trailer to sent mail',
+ 'field' => 'cgp_addmailtrailer',
+ 'curr_value' => $svc_acct->cgp_addmailtrailer,
+ 'value' => 'Y',
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Archive messages after',
+ 'field' => 'cgp_archiveafter',
+ 'options' => [ '', 0, 86400, 172800, 259200, 432000, 604800,
+ 1209600, 2592000, 7776000, 15552000, 31536000,
+ 63072000
+ ],
+ 'labels' => {
+ '' => 'default (730 days)',#730 always default?
+ 0 => 'Never',
+ 86400 => '24 hours',
+ 172800 => '2 days',
+ 259200 => '3 days',
+ 432000 => '5 days',
+ 604800 => '7 days',
+ 1209600 => '2 weeks',
+ 2592000 => '30 days',
+ 7776000 => '90 days',
+ 15552000 => '180 days',
+ 31536000 => '365 days',
+ 63072000 => '730 days',
+ },
+ 'curr_value' => $svc_acct->cgp_archiveafter,
+ )
+ %>
+
+% #preferences
+
+%# false laziness w/svc_domain acct_def
+ <TR>
+ <TD ALIGN="right">Message delete method</TD>
+ <TD>
+ <SELECT NAME="cgp_deletemode">
+% for ( 'Move To Trash', 'Immediately', 'Mark' ) {
+ <OPTION VALUE="<% $_ %>"
+ <% $_ eq $svc_acct->cgp_deletemode ? 'SELECTED' : '' %>
+ ><% $_ %>
+% }
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'On logout remove trash',
+ 'field' => 'cgp_emptytrash',
+ 'options' => $svc_acct->cgp_emptytrash_values,
+ 'labels' => {
+ '' => 'default (92 days)', #right?
+ },
+ 'curr_value' => $svc_acct->cgp_emptytrash,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Language',
+ 'field' => 'cgp_language',
+ 'options' => [ '', qw( English Arabic Chinese Dutch French German Hebrew Italian Japanese Portuguese Russian Slovak Spanish Thai ) ],
+ 'labels' => {
+ '' => 'default (English)',
+ },
+ 'curr_value' => $svc_acct->cgp_language,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Time zone',
+ 'field' => 'cgp_timezone',
+ 'options' => $svc_acct->cgp_timezone_values,
+ 'labels' => {
+ '' => 'default (HostOS)',
+ },
+ 'curr_value' => $svc_acct->cgp_timezone,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Layout',
+ 'field' => 'cgp_skinname',
+ 'options' => [ '', '***', 'GoldFleece', 'Skin2' ],
+ 'labels' => {
+ '' => 'default (***)',
+ },
+ 'curr_value' => $svc_acct->cgp_skinname,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Pronto style',
+ 'field' => 'cgp_prontoskinname',
+ 'options' => [ '', 'Pronto', 'Pronto-darkflame', 'Pronto-steel', 'Pronto-twilight', ],
+
+ 'curr_value' => $svc_acct->cgp_prontoskinname,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Send read receipts',
+ 'field' => 'cgp_sendmdnmode',
+ 'options' => [ '', 'Never', 'Manually', 'Automatically' ],
+ 'labels' => {
+ '' => 'default (Automatically)',
+ },
+ 'curr_value' => $svc_acct->cgp_language,
+ )
+ %>
+
+% } else {
+
+% for (qw( password_recover cgp_rulesallowed cgp_rpopallowed cgp_mailtoall
+% cgp_addmailtrailer
+% cgp_deletemode cgp_emptytrash cgp_language cgp_timezone
+% cgp_skinname cgp_sendmdnmode
+% ) ) {
+
+ <INPUT TYPE="hidden" NAME="<% $_ %>" VALUE="<% $svc_acct->$_() %>">
+% }
+
+% }
+
+<%init>
+
+my %opt = @_;
+
+my $svc_acct = $opt{'svc_acct'};
+my $part_svc = $opt{'part_svc'};
+
+my $communigate = $opt{'communigate'};
+
+</%init>
diff --git a/httemplate/edit/svc_domain.cgi b/httemplate/edit/svc_domain.cgi
index 5abe9d6..54a933d 100755
--- a/httemplate/edit/svc_domain.cgi
+++ b/httemplate/edit/svc_domain.cgi
@@ -16,6 +16,7 @@
<INPUT TYPE="text" NAME="domain" VALUE="<% $domain %>" SIZE=28 MAXLENGTH=63>
% } else {
<B><% $domain %></B>
+ <INPUT TYPE="hidden" NAME="domain" VALUE="<% $domain %>">
% }
% if ($export) {
@@ -38,269 +39,22 @@ Available top-level domains: <% $export->option('tlds') %>
</TD>
</TR>
-% if ( $communigate ) {
- <TR>
- <TD ALIGN="right">Administrator domain</TD>
- <TD>
- <% include('/elements/select-domain.html',
- 'element_name' => 'parent_svcnum',
- 'curr_value' => $svc_domain->parent_svcnum,
- 'empty_label' => '(none)',
- )
- %>
- </TD>
- </TR>
-% } else {
- <INPUT TYPE="hidden" NAME="parent_svcnum" VALUE="<% $svc_domain->parent_svcnum %>">
-% }
-
-% if ( $communigate
-% && $part_svc->part_svc_column('cgp_aliases')->columnflag !~ /^[FA]$/ ) {
-
- <TR>
- <TD ALIGN="right">Aliases</TD>
- <TD><INPUT TYPE="text" NAME="cgp_aliases" VALUE="<% $svc_domain->cgp_aliases %>"></TD>
- </TR>
-
-% } else {
- <INPUT TYPE="hidden" NAME="cgp_aliases" VALUE="<% $svc_domain->cgp_aliases %>">
-% }
-
-% if ( $part_svc->part_svc_column('max_accounts')->columnflag =~ /^[FA]$/ ) {
- <INPUT TYPE="hidden" NAME="max_accounts" VALUE="<% $svc_domain->max_accounts %>">
-% } else {
- <TR>
- <TD ALIGN="right">Maximum number of accounts</TD>
- <TD>
- <INPUT TYPE="text" NAME="max_accounts" SIZE=5 MAXLENGTH=6 VALUE="<% $svc_domain->max_accounts %>">
- </TD>
- </TR>
-% }
-
-% if ( $communigate
-% && $part_svc->part_svc_column('cgp_accessmodes')->columnflag ne 'F' )
-% {
-
- <TR>
- <TD ALIGN="right">Enabled services</TD>
- <TD>
- <% include( '/elements/communigate_pro-accessmodes.html',
- 'curr_value' => $svc_domain->cgp_accessmodes,
- )
- %>
- </TD>
- </TR>
-
-% } else {
- <INPUT TYPE="hidden" NAME="cgp_accessmodes" VALUE="<% $svc_domain->cgp_accessmodes() |h %>">
-% }
-
-% if ( $communigate
-% && $part_svc->part_svc_column('trailer')->columnflag ne 'F' )
-% {
-
- <TR>
- <TD ALIGN="right">Mail trailer</TD>
- <TD>
- <TEXTAREA NAME="trailer" ROWS=5 COLS=60><% $svc_domain->trailer() |h %></TEXTAREA>
- </TD>
- </TR>
-
-% } else {
- <INPUT TYPE="hidden" NAME="trailer" VALUE="<% $svc_domain->trailer() |h %>">
-% }
-
-
-</TABLE>
-<BR>
-
-% if ( $communigate ) {
-
-Account defaults
-<% ntable("#cccccc",2) %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'Password modification',
- 'field' => 'acct_def_password_selfchange',
- 'curr_value' => $svc_domain->acct_def_password_selfchange,
- 'value' => 'Y',
- )
- %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'Password recovery',
- 'field' => 'acct_def_password_recover',
- 'curr_value' => $svc_domain->acct_def_password_recover,
- 'value' => 'Y',
- )
- %>
-
- <TR>
- <TD ALIGN="right">Enabled services
- </TD>
- <TD><% include('/elements/communigate_pro-accessmodes.html',
- 'element_name_prefix' => 'acct_def_cgp_accessmodes_',
- 'curr_value' => $svc_domain->acct_def_cgp_accessmodes,
- )
- %>
- </TD>
- </TR>
-
- <% include('/elements/tr-input-text.html',
- 'label' => 'Mail storage limit',
- 'field' => 'acct_def_quota',
- 'curr_value' => $svc_domain->acct_def_quota,
- )
- %>
- <% include('/elements/tr-input-text.html',
- 'label' => 'File storage limit',
- 'field' => 'acct_def_file_quota',
- 'curr_value' => $svc_domain->acct_def_file_quota,
- )
- %>
- <% include('/elements/tr-input-text.html',
- 'label' => 'Files limit',
- 'field' => 'acct_def_file_maxnum',
- 'curr_value' => $svc_domain->acct_def_file_maxnum,
- )
- %>
- <% include('/elements/tr-input-text.html',
- 'label' => 'File size limit',
- 'field' => 'acct_def_file_maxsize',
- 'curr_value' => $svc_domain->acct_def_file_maxsize,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Allowed mail rules',
- 'field' => 'acct_def_cgp_rulesallowed',
- 'options' => [ '', 'No', 'Filter Only', 'All But Exec', 'Any' ],
- 'labels' => {
- '' => 'default (No)', #No always the default?
- },
- 'curr_value' => $svc_domain->acct_def_cgp_rulesallowed,
- )
- %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'RPOP modifications',
- 'field' => 'acct_def_cgp_rpopallowed',
- 'curr_value' => $svc_domain->acct_def_cgp_rpopallowed,
- 'value' => 'Y',
- )
- %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'Accepts mail to "all"',
- 'field' => 'acct_def_cgp_mailtoall',
- 'curr_value' => $svc_domain->acct_def_cgp_mailtoall,
- 'value' => 'Y',
- )
- %>
-
- <% include('/elements/tr-checkbox.html',
- 'label' => 'Add trailer to sent mail',
- 'field' => 'acct_def_cgp_addmailtrailer',
- 'curr_value' => $svc_domain->acct_def_cgp_addmailtrailer,
- 'value' => 'Y',
- )
- %>
-
-%# false laziness w/svc_acct acct_def
- <TR>
- <TD ALIGN="right">Message delete method</TD>
- <TD>
- <SELECT NAME="acct_def_cgp_deletemode">
-% for ( 'Move To Trash', 'Immediately', 'Mark' ) {
- <OPTION VALUE="<% $_ %>"
- <% $_ eq $svc_domain->acct_def_cgp_deletemode ? 'SELECTED' : '' %>
- ><% $_ %>
-% }
- </SELECT>
- </TD>
- </TR>
-
- <% include('/elements/tr-select.html',
- 'label' => 'On logout remove trash',
- 'field' => 'acct_def_cgp_emptytrash',
- 'options' => $svc_domain->cgp_emptytrash_values,
- 'labels' => {
- '' => 'default (92 days)', #right?
- },
- 'curr_value' => $svc_domain->acct_def_cgp_emptytrash,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Language',
- 'field' => 'acct_def_cgp_language',
- 'options' => [ '', qw( English Arabic Chinese Dutch French German Hebrew Italian Japanese Portuguese Russian Slovak Spanish Thai ) ],
- 'labels' => {
- '' => 'default (English)',
- },
- 'curr_value' => $svc_domain->acct_def_cgp_language,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Time zone',
- 'field' => 'acct_def_cgp_timezone',
- 'options' => $svc_domain->cgp_timezone_values,
- 'labels' => {
- '' => 'default (HostOS)',
- },
- 'curr_value' => $svc_domain->acct_def_cgp_timezone,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Layout',
- 'field' => 'acct_def_cgp_skinname',
- 'options' => [ '', '***', 'GoldFleece', 'Skin2' ],
- 'labels' => {
- '' => 'default (***)',
- },
- 'curr_value' => $svc_domain->acct_def_cgp_skinname,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Pronto style',
- 'field' => 'acct_def_cgp_prontoskinname',
- 'options' => [ '', 'Pronto', 'Pronto-darkflame', 'Pronto-steel', 'Pronto-twilight', ],
- 'curr_value' => $svc_domain->acct_def_cgp_prontoskinname,
- )
- %>
-
- <% include('/elements/tr-select.html',
- 'label' => 'Send read receipts',
- 'field' => 'acct_def_cgp_sendmdnmode',
- 'options' => [ '', 'Never', 'Manually', 'Automatically' ],
- 'labels' => {
- '' => 'default (Automatically)',
- },
- 'curr_value' => $svc_domain->acct_def_cgp_language,
- )
- %>
-
-% #XXX rules, archive rule, spam foldering rule(s)
+<% include('svc_domain/communigate-basics.html',
+ 'svc_domain' => $svc_domain,
+ 'part_svc' => $part_svc,
+ 'communigate' => $communigate,
+ )
+%>
</TABLE>
<BR>
-% } else {
-
-% foreach my $f (qw( password_selfchange password_recover cgp_accessmodes
-% quota file_quota file_maxnum file_maxsize
-% cgp_rulesallowed cgp_rpopallowed cgp_mailtoall
-% cgp_addmailtrailer
-% cgp_deletemode cgp_emptytrash cgp_language
-% cgp_timezone cgp_skinname cgp_sendmdnmode
-% )) {
- <INPUT TYPE="hidden" NAME="acct_def_<%$f%>" VALUE="<% $svc_domain->get("acct_def_$f") %>">
-% }
-
-% }
+<% include('svc_domain/communigate-acct_defaults.html',
+ 'svc_domain' => $svc_domain,
+ 'part_svc' => $part_svc,
+ 'communigate' => $communigate,
+ )
+%>
<INPUT TYPE="submit" VALUE="Submit">
diff --git a/httemplate/edit/svc_domain/communigate-acct_defaults.html b/httemplate/edit/svc_domain/communigate-acct_defaults.html
new file mode 100644
index 0000000..3426a8e
--- /dev/null
+++ b/httemplate/edit/svc_domain/communigate-acct_defaults.html
@@ -0,0 +1,223 @@
+% if ( $communigate ) {
+
+Account defaults
+<% ntable("#cccccc",2) %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'Password modification',
+ 'field' => 'acct_def_password_selfchange',
+ 'curr_value' => $svc_domain->acct_def_password_selfchange,
+ 'value' => 'Y',
+ )
+ %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'Password recovery',
+ 'field' => 'acct_def_password_recover',
+ 'curr_value' => $svc_domain->acct_def_password_recover,
+ 'value' => 'Y',
+ )
+ %>
+
+ <TR>
+ <TD ALIGN="right">Enabled services
+ </TD>
+ <TD><% include('/elements/communigate_pro-accessmodes.html',
+ 'element_name_prefix' => 'acct_def_cgp_accessmodes_',
+ 'curr_value' => $svc_domain->acct_def_cgp_accessmodes,
+ )
+ %>
+ </TD>
+ </TR>
+
+ <% include('/elements/tr-input-text.html',
+ 'label' => 'Mail storage limit',
+ 'field' => 'acct_def_quota',
+ 'curr_value' => $svc_domain->acct_def_quota,
+ )
+ %>
+ <% include('/elements/tr-input-text.html',
+ 'label' => 'File storage limit',
+ 'field' => 'acct_def_file_quota',
+ 'curr_value' => $svc_domain->acct_def_file_quota,
+ )
+ %>
+ <% include('/elements/tr-input-text.html',
+ 'label' => 'Files limit',
+ 'field' => 'acct_def_file_maxnum',
+ 'curr_value' => $svc_domain->acct_def_file_maxnum,
+ )
+ %>
+ <% include('/elements/tr-input-text.html',
+ 'label' => 'File size limit',
+ 'field' => 'acct_def_file_maxsize',
+ 'curr_value' => $svc_domain->acct_def_file_maxsize,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Allowed mail rules',
+ 'field' => 'acct_def_cgp_rulesallowed',
+ 'options' => [ '', 'No', 'Filter Only', 'All But Exec', 'Any' ],
+ 'labels' => {
+ '' => 'default (No)', #No always the default?
+ },
+ 'curr_value' => $svc_domain->acct_def_cgp_rulesallowed,
+ )
+ %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'RPOP modifications',
+ 'field' => 'acct_def_cgp_rpopallowed',
+ 'curr_value' => $svc_domain->acct_def_cgp_rpopallowed,
+ 'value' => 'Y',
+ )
+ %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'Accepts mail to "all"',
+ 'field' => 'acct_def_cgp_mailtoall',
+ 'curr_value' => $svc_domain->acct_def_cgp_mailtoall,
+ 'value' => 'Y',
+ )
+ %>
+
+ <% include('/elements/tr-checkbox.html',
+ 'label' => 'Add trailer to sent mail',
+ 'field' => 'acct_def_cgp_addmailtrailer',
+ 'curr_value' => $svc_domain->acct_def_cgp_addmailtrailer,
+ 'value' => 'Y',
+ )
+ %>
+
+%# more false laziness w/svc_acct acct_def
+ <% include('/elements/tr-select.html',
+ 'label' => 'Archive messages after',
+ 'field' => 'acct_def_cgp_archiveafter',
+ 'options' => [ '', 0, 86400, 172800, 259200, 432000, 604800,
+ 1209600, 2592000, 7776000, 15552000, 31536000,
+ 63072000
+ ],
+ 'labels' => {
+ '' => 'default (730 days)',#730 always default?
+ 0 => 'Never',
+ 86400 => '24 hours',
+ 172800 => '2 days',
+ 259200 => '3 days',
+ 432000 => '5 days',
+ 604800 => '7 days',
+ 1209600 => '2 weeks',
+ 2592000 => '30 days',
+ 7776000 => '90 days',
+ 15552000 => '180 days',
+ 31536000 => '365 days',
+ 63072000 => '730 days',
+ },
+ 'curr_value' => $svc_domain->acct_def_cgp_archiveafter,
+ )
+ %>
+
+%# false laziness w/svc_acct acct_def
+ <TR>
+ <TD ALIGN="right">Message delete method</TD>
+ <TD>
+ <SELECT NAME="acct_def_cgp_deletemode">
+% for ( 'Move To Trash', 'Immediately', 'Mark' ) {
+ <OPTION VALUE="<% $_ %>"
+ <% $_ eq $svc_domain->acct_def_cgp_deletemode ? 'SELECTED' : '' %>
+ ><% $_ %>
+% }
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'On logout remove trash',
+ 'field' => 'acct_def_cgp_emptytrash',
+ 'options' => $svc_domain->cgp_emptytrash_values,
+ 'labels' => {
+ '' => 'default (92 days)', #right?
+ },
+ 'curr_value' => $svc_domain->acct_def_cgp_emptytrash,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Language',
+ 'field' => 'acct_def_cgp_language',
+ 'options' => [ '', qw( English Arabic Chinese Dutch French German Hebrew Italian Japanese Portuguese Russian Slovak Spanish Thai ) ],
+ 'labels' => {
+ '' => 'default (English)',
+ },
+ 'curr_value' => $svc_domain->acct_def_cgp_language,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Time zone',
+ 'field' => 'acct_def_cgp_timezone',
+ 'options' => $svc_domain->cgp_timezone_values,
+ 'labels' => {
+ '' => 'default (HostOS)',
+ },
+ 'curr_value' => $svc_domain->acct_def_cgp_timezone,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Layout',
+ 'field' => 'acct_def_cgp_skinname',
+ 'options' => [ '', '***', 'GoldFleece', 'Skin2' ],
+ 'labels' => {
+ '' => 'default (***)',
+ },
+ 'curr_value' => $svc_domain->acct_def_cgp_skinname,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Pronto style',
+ 'field' => 'acct_def_cgp_prontoskinname',
+ 'options' => [ '', 'Pronto', 'Pronto-darkflame', 'Pronto-steel', 'Pronto-twilight', ],
+ 'curr_value' => $svc_domain->acct_def_cgp_prontoskinname,
+ )
+ %>
+
+ <% include('/elements/tr-select.html',
+ 'label' => 'Send read receipts',
+ 'field' => 'acct_def_cgp_sendmdnmode',
+ 'options' => [ '', 'Never', 'Manually', 'Automatically' ],
+ 'labels' => {
+ '' => 'default (Automatically)',
+ },
+ 'curr_value' => $svc_domain->acct_def_cgp_language,
+ )
+ %>
+
+</TABLE>
+<BR>
+
+% } else {
+
+% foreach my $f (qw( password_selfchange password_recover cgp_accessmodes
+% quota file_quota file_maxnum file_maxsize
+% cgp_rulesallowed cgp_rpopallowed cgp_mailtoall
+% cgp_addmailtrailer
+% cgp_deletemode cgp_emptytrash cgp_language
+% cgp_timezone cgp_skinname cgp_sendmdnmode
+% )) {
+ <INPUT TYPE="hidden" NAME="acct_def_<%$f%>" VALUE="<% $svc_domain->get("acct_def_$f") %>">
+% }
+
+% }
+
+<%init>
+
+my %opt = @_;
+
+my $svc_domain = $opt{'svc_domain'};
+my $part_svc = $opt{'part_svc'};
+
+my $communigate = $opt{'communigate'};
+
+</%init>
diff --git a/httemplate/edit/svc_domain/communigate-basics.html b/httemplate/edit/svc_domain/communigate-basics.html
new file mode 100644
index 0000000..ff401c0
--- /dev/null
+++ b/httemplate/edit/svc_domain/communigate-basics.html
@@ -0,0 +1,82 @@
+% if ( $communigate ) {
+ <TR>
+ <TD ALIGN="right">Administrator domain</TD>
+ <TD>
+ <% include('/elements/select-domain.html',
+ 'element_name' => 'parent_svcnum',
+ 'curr_value' => $svc_domain->parent_svcnum,
+ 'empty_label' => '(none)',
+ )
+ %>
+ </TD>
+ </TR>
+% } else {
+ <INPUT TYPE="hidden" NAME="parent_svcnum" VALUE="<% $svc_domain->parent_svcnum %>">
+% }
+
+% if ( $communigate
+% && $part_svc->part_svc_column('cgp_aliases')->columnflag !~ /^[FA]$/ ) {
+
+ <TR>
+ <TD ALIGN="right">Aliases</TD>
+ <TD><INPUT TYPE="text" NAME="cgp_aliases" VALUE="<% $svc_domain->cgp_aliases %>"></TD>
+ </TR>
+
+% } else {
+ <INPUT TYPE="hidden" NAME="cgp_aliases" VALUE="<% $svc_domain->cgp_aliases %>">
+% }
+
+% if ( $part_svc->part_svc_column('max_accounts')->columnflag =~ /^[FA]$/ ) {
+ <INPUT TYPE="hidden" NAME="max_accounts" VALUE="<% $svc_domain->max_accounts %>">
+% } else {
+ <TR>
+ <TD ALIGN="right">Maximum number of accounts</TD>
+ <TD>
+ <INPUT TYPE="text" NAME="max_accounts" SIZE=5 MAXLENGTH=6 VALUE="<% $svc_domain->max_accounts %>">
+ </TD>
+ </TR>
+% }
+
+% if ( $communigate
+% && $part_svc->part_svc_column('cgp_accessmodes')->columnflag ne 'F' )
+% {
+
+ <TR>
+ <TD ALIGN="right">Enabled services</TD>
+ <TD>
+ <% include( '/elements/communigate_pro-accessmodes.html',
+ 'curr_value' => $svc_domain->cgp_accessmodes,
+ )
+ %>
+ </TD>
+ </TR>
+
+% } else {
+ <INPUT TYPE="hidden" NAME="cgp_accessmodes" VALUE="<% $svc_domain->cgp_accessmodes() |h %>">
+% }
+
+% if ( $communigate
+% && $part_svc->part_svc_column('trailer')->columnflag ne 'F' )
+% {
+
+ <TR>
+ <TD ALIGN="right">Mail trailer</TD>
+ <TD>
+ <TEXTAREA NAME="trailer" ROWS=5 COLS=60><% $svc_domain->trailer() |h %></TEXTAREA>
+ </TD>
+ </TR>
+
+% } else {
+ <INPUT TYPE="hidden" NAME="trailer" VALUE="<% $svc_domain->trailer() |h %>">
+% }
+
+<%init>
+
+my %opt = @_;
+
+my $svc_domain = $opt{'svc_domain'};
+my $part_svc = $opt{'part_svc'};
+
+my $communigate = $opt{'communigate'};
+
+</%init>
diff --git a/httemplate/elements/city.html b/httemplate/elements/city.html
index 61d0578..956d353 100644
--- a/httemplate/elements/city.html
+++ b/httemplate/elements/city.html
@@ -64,7 +64,7 @@ Example:
<%$pre%>city_select_changed(what.form.<% $pre %>city_select);
what.form.<% $pre %>city.style.display = 'none';
what.form.<% $pre %>city_select.style.display = '';
- } else {
+ } else if ( what.form.<% $pre %>city.style.display == 'none' ) {
// turn on the text city, turn off the select
what.form.<%$ pre %>city.value = saved_<%$pre%>city;
what.form.<% $pre %>city.style.display = '';
diff --git a/httemplate/elements/contact.html b/httemplate/elements/contact.html
index a7a33b1..eea3694 100644
--- a/httemplate/elements/contact.html
+++ b/httemplate/elements/contact.html
@@ -5,12 +5,38 @@
<TABLE>
<TR>
% foreach my $field ( @fields ) {
+%
+% my $value = '';
+% if ( $field =~ /^phonetypenum(\d+)$/ ) {
+% my $contact_phone = qsearchs('contact_phone', {
+% 'contactnum' => $curr_value,
+% 'phonetypenum' => $1,
+% });
+% if ( $contact_phone ) {
+% $value = $contact_phone->phonenum;
+% $value .= 'x'.$contact_phone->extension
+% if $contact_phone->extension;
+% $value = '+'. $contact_phone->countrycode. " $value"
+% if $contact_phone->countrycode
+% && $contact_phone->countrycode ne '1';
+% }
+% } elsif ( $field eq 'emailaddress' ) {
+% #XXX multiple not yet supported
+% my $contact_email = qsearchs('contact_email', {
+% 'contactnum' => $curr_value,
+% });
+% $value = $contact_email->emailaddress if $contact_email;
+% } else {
+% $value = $contact->get($field);
+% }
+
<TD>
- <INPUT TYPE = "text"
- NAME = "<%$name%>_<%$field%>"
- ID = "<%$id%>_<%$field%>"
+ <INPUT TYPE = "text"
+ NAME = "<%$name%>_<%$field%>"
+ ID = "<%$id%>_<%$field%>"
+ SIZE = "<% $size{$field} || 15 %>"
VALUE = "<% scalar($cgi->param($name."_$field"))
- || $contact->get($field) |h %>"
+ || $value |h %>"
<% $onchange %>
><BR>
<FONT SIZE="-1"><% $label{$field} %></FONT>
@@ -45,12 +71,25 @@ if ( $curr_value ) {
$contact = new FS::contact {};
}
+my %size = ( 'title' => 12 );
+
tie my %label, 'Tie::IxHash',
- 'first' => 'First name',
- 'last' => 'Last name',
- 'title' => 'Title/Position',
- 'comment' => 'Comment',
+ 'first' => 'First name',
+ 'last' => 'Last name',
+ 'title' => 'Title/Position',
+ 'emailaddress' => 'Email',
;
+
+my $first = 0;
+foreach my $phone_type ( qsearch({table=>'phone_type', order_by=>'weight'}) ) {
+ next if $phone_type->typename eq 'Home';
+ my $f = 'phonetypenum'.$phone_type->phonetypenum;
+ $label{$f} = $phone_type->typename. ' phone';
+ $size{$f} = $first++ ? 11 : 15;
+}
+
+$label{'comment'} = 'Comment';
+
my @fields = keys %label;
</%init>
diff --git a/httemplate/elements/customer-table.html b/httemplate/elements/customer-table.html
index f00419f..3c3f8b2 100644
--- a/httemplate/elements/customer-table.html
+++ b/httemplate/elements/customer-table.html
@@ -22,6 +22,7 @@ Example:
###
'name_singular' => 'customer', #label
+ 'custnum_update_callback' => 'name_of_js_callback' #passed a rownum
#listrefs
'types' => ['immutable', ''], # immutable or ''/text
@@ -98,6 +99,9 @@ Example:
if ( name.length > 0 ) {
customer.value = name;
customer.setAttribute('magic', 'nosearch');
+% if ( $opt{custnum_update_callback} ) {
+ <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
+% }
} else {
customer.value = 'Not found';
customer.style.color = '#ff0000';
@@ -162,6 +166,9 @@ Example:
customer_obj.style.display = '';
customer_select.style.display = 'none';
+% if ( $opt{custnum_update_callback} ) {
+ <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
+% }
} else {
@@ -223,6 +230,10 @@ Example:
this.style.display = 'none';
customer_obj.style.display = '';
+% if ( $opt{custnum_update_callback} ) {
+ <% $opt{custnum_update_callback} %>(searchrow, '<% $opt{prefix} %>')
+% }
+
}
}
@@ -314,7 +325,7 @@ Example:
>
% } elsif ($types->[$col] eq 'immutable') {
<% $font %><% $value %><% $font ? '</FONT>' : '' %>
- <INPUT TYPE="hidden" NAME="<% $name %>" VALUE="<% $value %>" >
+ <INPUT TYPE="hidden" ID="<% $name %>" NAME="<% $name %>" VALUE="<% $value %>" >
% } else {
Cannot represent unknown type: <% $types->[$col] %>
% }
diff --git a/httemplate/elements/email-link.html b/httemplate/elements/email-link.html
new file mode 100644
index 0000000..692e5bc
--- /dev/null
+++ b/httemplate/elements/email-link.html
@@ -0,0 +1,16 @@
+% if ( $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices') ) {
+<A HREF="<%$p%>misc/email-customers.html?table=<%$table%>&<%$query%>"><%$label%></A>
+% }
+<%init>
+my %opt = @_;
+my $table = $opt{'table'};
+my $search_hash = $opt{'search_hash'};
+die "'table' required" if !$table;
+die "'search_hash' required" if !$search_hash;
+
+my $uri = new URI;
+$uri->query_form($search_hash);
+my $query = $uri->query;
+my $label = ($opt{'label'} || 'Email a notice to these customers');
+</%init>
+
diff --git a/httemplate/elements/freeside.css b/httemplate/elements/freeside.css
index dfb56e9..6cb1503 100644
--- a/httemplate/elements/freeside.css
+++ b/httemplate/elements/freeside.css
@@ -16,7 +16,7 @@ a[href]:hover {
color: #7e0079;
}
-textarea, input[type="text"] {
+textarea, input[type="text"], input[type="password"] {
border: 1px solid #666666;
padding: 1px;
-moz-border-radius: 2px;
@@ -24,7 +24,7 @@ textarea, input[type="text"] {
border-radius: 2px;
}
-textarea:hover, input[type="text"]:hover {
+textarea:hover, input[type="text"]:hover, input[type="password"]:hover {
border: 1px solid #7e0079;
padding: 1px;
-moz-border-radius: 2px;
@@ -32,7 +32,7 @@ textarea:hover, input[type="text"]:hover {
border-radius: 2px;
}
-textarea:focus, input[type="text"]:focus {
+textarea:focus, input[type="text"]:focus, input[type="password"]:focus {
background-color: #ffffdd;
border: 1px solid #7e0079;
-moz-border-radius: 2px;
@@ -40,6 +40,16 @@ textarea:focus, input[type="text"]:focus {
border-radius: 2px;
}
+.fsdisabled {
+ background-color: #dddddd;
+ color: #666666;
+ border: 1px solid #999999;
+ padding: 1px;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+}
+
input[type="reset"], input[type="submit"], input[type="button"] {
background-color: #dddddd;
border: 1px solid #666666;
@@ -173,6 +183,43 @@ div.fstabcontainer {
filter: progid:DXImageTransform.Microsoft.Shadow(color='#666666', Direction=135, Strength=2);
}
+.fscontainer {
+ overflow:hidden;
+ display:inline-block;
+}
+.fscontainer {
+ display:block;
+}
+
+.fsbox {
+
+ float:left;
+
+ background-color:#ffffff;
+
+ padding:8px;
+ border-top:1px solid #7e0079;
+ border-left:1px solid #7e0079;
+ border-right:1px solid #7e0079;
+ border-bottom:1px solid #7e0079;
+ -moz-border-radius-bottomleft:8px;
+ -moz-border-radius-bottomright:8px;
+ -webkit-border-radius-bottomleft:8px;
+ -webkit-border-radius-bottomright:8px;
+ border-radius-bottomleft:8px;
+ border-radius-bottomright:8px;
+ -moz-box-shadow: #666666 1px 1px 2px;
+ -webkit-box-shadow: #666666 1px 1px 2px;
+ box-shadow: #666666 1px 1px 2px;
+ filter: progid:DXImageTransform.Microsoft.Shadow(color='#666666', Direction=135, Strength=2);
+}
+
+.fsbox .fsbox-title {
+ /*float:left;*/
+ font-size:150%;
+ font-weight:bold;
+}
+
.background {
background-color:#f8f8f8;
}
diff --git a/httemplate/elements/header.html b/httemplate/elements/header.html
index 90ab0b7..c83529e 100644
--- a/httemplate/elements/header.html
+++ b/httemplate/elements/header.html
@@ -15,7 +15,6 @@ Example:
#old-style
include( '/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">
@@ -43,8 +42,8 @@ Example:
<% $head |n %>
</HEAD>
- <BODY BGCOLOR="#f8f8f8" <% $etc |n %> STYLE="margin-top:0; margin-bottom:0; margin-left:0; margin-right:0">
- <table width="100%" CELLPADDING=0 CELLSPACING=0 STYLE="padding-left:0; padding-right:4">
+ <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">
<tr>
<td BGCOLOR="#ffffff"><IMG BORDER=0 ALT="freeside" HEIGHT="36" SRC="<%$fsurl%>view/REAL_logo.cgi"></td>
<td align=left BGCOLOR="#ffffff"> <!-- valign="top" -->
diff --git a/httemplate/elements/input-date-field.html b/httemplate/elements/input-date-field.html
new file mode 100644
index 0000000..2a9bc1d
--- /dev/null
+++ b/httemplate/elements/input-date-field.html
@@ -0,0 +1,50 @@
+% if(!$noinit) {
+<LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2">
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar_stripped.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-en.js"></SCRIPT>
+<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT>
+% }
+
+<INPUT TYPE="text" NAME="<% $name %>" ID="<% $name %>_text" VALUE="<% $value %>">
+<IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $name %>_button" STYLE="cursor: pointer" TITLE="Select date">
+
+<SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "<% $name %>_text",
+ ifFormat: "<% $format %>",
+ button: "<% $name %>_button",
+ align: "BR"
+ });
+</SCRIPT>
+
+<%init>
+
+my($name, $value, $format, $usedatetime, $noinit);
+if ( ref($_[0]) ) {
+ my $opt = shift;
+ $name = $opt->{'name'};
+ $value = $opt->{'value'};
+ $format = $opt->{'format'};
+ $usedatetime = $opt->{'usedatetime'};
+ $noinit = $opt->{'noinit'};
+} else {
+ ($name, $value, $format, $usedatetime) = @_;
+}
+
+my $conf = new FS::Conf;
+
+$format ||= $conf->config('date_format') || '%m/%d/%Y';
+
+if ( $value =~ /\S/ ) {
+ if ( $usedatetime ) {
+ my $dt = DateTime->from_epoch(epoch => $value, time_zone => 'floating');
+ $value = $dt->strftime($format);
+ } elsif ( $value =~ /^\d+$/ ) {
+ $value = time2str($format, $value);
+ }
+} else {
+ $value = '';
+}
+
+</%init>
+
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index a5bcdeb..1909d90 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -185,11 +185,16 @@ foreach my $svcdb ( FS::part_svc->svc_tables() ) {
];
}
- if ( $svcdb eq 'svc_acct' ) {
+ if ( $svcdb eq 'svc_acct' || $svcdb eq 'svc_broadband' ) {
$report_svc{"Advanced $lcsname reports"} =
[ $fsurl."search/report_$svcdb.html", '' ];
}
+ if ( $svcdb eq 'svc_phone' ) {
+ $report_svc{"Avaialble phone numbers (DIDs)"} =
+ [ $fsurl."search/phone_avail.html", '' ];
+ }
+
$report_services{$name} = [ \%report_svc, $longname ];
}
@@ -207,7 +212,9 @@ if ( $curuser->access_right('Financial reports') ) {
$report_packages{'separator2'} = '';
}
$report_packages{'All customer packages'} = [ $fsurl.'search/cust_pkg.cgi?pkgnum', 'List all customer packages', ];
+$report_packages{'Package summary'} = [ $fsurl.'search/cust_pkg_summary.html', 'Show package sales summary', ];
$report_packages{'Suspended customer packages'} = [ $fsurl.'search/cust_pkg.cgi?magic=suspended', 'List suspended packages' ];
+$report_packages{'Suspension summary'} = [ $fsurl.'search/cust_pkg_susp.html', 'Show suspension activity', ];
$report_packages{'Customer packages with unconfigured services'} = [ $fsurl.'search/cust_pkg.cgi?APKG_pkgnum', 'List packages which have provisionable services' ];
$report_packages{'FCC Form 477 packages'} = [ $fsurl.'search/report_477.html', 'Summarize packages by census tract for particular types' ]
if $conf->exists('cust_main-require_censustract');
@@ -257,6 +264,7 @@ $report_payments{'Pending Payments'} = [ $fsurl.'search/cust_pay_pending.html?ma
if $curuser->access_right('View customer pending payments');
$report_payments{'Voided Payments'} = [ $fsurl.'search/report_cust_pay.html?void=1', 'Voided payment report (by type and/or date range)' ]
if $curuser->access_right('View customer pending payments');
+$report_payments{'Unapplied Payments'} = [ $fsurl.'search/report_cust_pay.html?unapplied=1', 'Unapplied payment report (by type and/or date range)' ];
$report_payments{'Payment Batches'} = [ $fsurl.'search/pay_batch.html', 'Payment batches (by status and/or date range)' ]
if $conf->exists('batch-enable') || $conf->config('batch-enable_payby');
$report_payments{'Unapplied Payment Aging'} = [ $fsurl.'search/report_unapplied_cust_pay.html', 'Unapplied payment aging report' ];
@@ -270,7 +278,9 @@ if($curuser->access_right('Financial reports')) {
'Rated Call Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg_detail.html', 'Sales report and graph (by agent, package class, usage class and/or date range)' ],
'Employee Commission Report' => [ $fsurl.'search/report_employee_commission.html', '' ],
'Credit Report' => [ $fsurl.'search/report_cust_credit.html', 'Credit report (by employee and/or date range)' ],
+ 'Unapplied Credits' => [ $fsurl.'search/report_cust_credit.html?unapplied=1', 'Unapplied credit report (by type and/or date range)' ],
'Refund Report' => [ $fsurl.'search/report_cust_refund.html', 'Refund report (by type and/or date range)' ],
+ 'Unapplied Refunds' => [ $fsurl.'search/report_cust_refund.html?unapplied=1', 'Unapplied refund report (by type and/or date range)' ],
'Package Costs Report' => [ $fsurl.'graph/report_cust_pkg_cost.html', 'Package setup and recurring costs graph' ],
);
$report_financial{'A/R Aging'} = [ $fsurl.'search/report_receivables.html', 'Accounts Receivable Aging report' ];
@@ -317,6 +327,7 @@ $report_menu{'SQL Query'} = [ $fsurl.'search/report_sql.html', 'SQL Query' ]
tie my %tools_importing, 'Tie::IxHash',
'Customers' => [ $fsurl.'misc/cust_main-import.cgi', '' ],
+ 'Customer packages' => [ $fsurl.'misc/cust_pkg-import.html', '' ],
'Customer comments from CSV file' => [ $fsurl.'misc/cust_main_note-import.html', '' ],
'One-time charges from CSV file' => [ $fsurl.'misc/cust_main-import_charges.cgi', '' ],
'Payments from CSV file' => [ $fsurl.'misc/cust_pay-import.cgi', '' ],
@@ -358,11 +369,13 @@ $tools_menu{'Process payment batches'} = [ $fsurl.'search/pay_batch.cgi?magic=_d
if ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
&& $curuser->access_right('Process batches');
$tools_menu{'Process invoice batches'} = [ $fsurl.'search/bill_batch.cgi' ]
- if ( $conf->exists('invoice_print_pdf') );
+ if $conf->exists('invoice_print_pdf');
$tools_menu{'Job Queue'} = [ $fsurl.'search/queue.html', 'View pending job queue' ]
if $curuser->access_right('Job queue');
$tools_menu{'Ticketing'} = [ \%tools_ticketing, 'Ticketing tools' ]
if $conf->config('ticket_system');
+$tools_menu{'Business card scan'} = [ $fsurl.'edit/prospect_main-upload.html' ]
+ if $curuser->access_right('New prospect');
$tools_menu{'Time Queue'} = [ $fsurl.'search/report_timeworked.html', 'View pending support time' ]
if $curuser->access_right('Time queue');
$tools_menu{'Attachments'} = [ $fsurl.'browse/cust_attachment.html', 'View customer attachments' ]
@@ -398,7 +411,7 @@ if ( $curuser->access_right('Configuration') ) {
#package grouping sub-menu?
$config_pkg{'Package classes'} = [ $fsurl.'browse/pkg_class.html', 'Package classes define groups of packages, for taxation, ordering convenience and reporting.' ];
- $config_pkg{'Package categories'} = [ $fsurl.'browse/pkg_category.html', 'Package categories define groups of package classes.' ];
+ $config_pkg{'Package categories'} = [ $fsurl.'browse/pkg_category.html', 'Package categories define groups of package classes, for invoice sections.' ];
$config_pkg{'Package report classes'} = [ $fsurl.'browse/part_pkg_report_option.html', 'Package classes define optional groups of packages for reporting only.' ];
#eo package grouping sub-menu
@@ -531,8 +544,8 @@ my $wiki = 'http://www.freeside.biz/mediawiki/index.php';
my $doc_link = $conf->config('support-key')
? "$wiki/Supported:Documentation"
: $curuser->access_right('Configuration')
- ? "$wiki/Freeside:1.9:Documentation"
- : "$wiki/Freeside:1.9:Documentation:User";
+ ? "$wiki/Freeside:2.1:Documentation"
+ : "$wiki/Freeside:2.1:Documentation:User";
eval "use RT;"
if $conf->config('ticket_system') eq 'RT_Internal';
diff --git a/httemplate/elements/popup_link.html b/httemplate/elements/popup_link.html
index 49b624c..fbb6ce3 100644
--- a/httemplate/elements/popup_link.html
+++ b/httemplate/elements/popup_link.html
@@ -11,7 +11,7 @@ Example:
'label' => 'click me', # text of <A> tag
#strongly recommended
- 'actionlabel => 'You clicked', # popup title
+ 'actionlabel' => 'You clicked', # popup title
#opt
'width' => 540,
diff --git a/httemplate/elements/search-cust_main.html b/httemplate/elements/search-cust_main.html
index 317922d..e8c645e 100644
--- a/httemplate/elements/search-cust_main.html
+++ b/httemplate/elements/search-cust_main.html
@@ -11,7 +11,7 @@ Example:
);
</%doc>
-<INPUT TYPE="hidden" NAME="<% $field %>" VALUE="<% $value %>">
+<INPUT TYPE="hidden" NAME="<% $field %>" ID="<% $field %>" VALUE="<% $value %>">
<!-- some false laziness w/ misc/batch-cust_pay.html, though not as bad as i'd thought at first... -->
@@ -60,6 +60,9 @@ Example:
function smart_<% $field %>_search(what) {
+ if ( <% $field %>_search_active )
+ return;
+
var customer = what.value;
if ( customer == 'searching...' || customer == ''
diff --git a/httemplate/elements/select-discount_term.html b/httemplate/elements/select-discount_term.html
new file mode 100644
index 0000000..26d877a
--- /dev/null
+++ b/httemplate/elements/select-discount_term.html
@@ -0,0 +1,32 @@
+% if ( scalar(@discount_term) ) {
+ <SELECT NAME="discount_term">
+ <OPTION VALUE="">1 month
+% foreach my $discount_term (@discount_term) {
+% my $sel = ( $cgi->param('discount_term') == $discount_term ) ? 'SELECTED' : '';
+ <OPTION <% $sel %> VALUE="<% $discount_term %>"><% $discount_term. " months" %>
+% }
+ </SELECT>
+% }
+<%init>
+
+my %opt = @_;
+
+my $cgi = $opt{'cgi'};
+
+my @discount_term;
+if ( $opt{discount_term} ) {
+
+ @discount_term = @{ $opt{discount_term} };
+
+} else {
+
+ my $custnum = $opt{'custnum'};
+
+ my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or die "unknown custnum $custnum\n";
+
+ @discount_term = $cust_main->discount_terms;
+
+}
+
+</%init>
diff --git a/httemplate/elements/select-month_year.html b/httemplate/elements/select-month_year.html
index 34476bc..cbf90b6 100644
--- a/httemplate/elements/select-month_year.html
+++ b/httemplate/elements/select-month_year.html
@@ -12,7 +12,7 @@
% if ( $opt{'show_month_abbr'} ) {
% @mon = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
% } else {
-% @mon = ( 1 .. 12 );
+% @mon = ( ( map "0$_", 1 .. 9 ), 10 .. 12 ),
% }
%
% my $date = $opt{'selected_date'} || '';
diff --git a/httemplate/elements/select-state.html b/httemplate/elements/select-state.html
index 9b358e2..2d60fde 100644
--- a/httemplate/elements/select-state.html
+++ b/httemplate/elements/select-state.html
@@ -32,7 +32,7 @@ Example:
% foreach my $state ( keys %states ) {
- <OPTION VALUE="<% $state |h %>"<% $state eq $opt{'state'} ? ' SELECTED' : '' %>><% $states{$state} || '(n/a)' %>
+ <OPTION VALUE="<% $state |h %>"<% $state eq $opt{'state'} ? ' SELECTED' : '' %>><% $states{$state} || '(n/a)' |h %>
% }
diff --git a/httemplate/elements/select-terms.html b/httemplate/elements/select-terms.html
index 52f9fb5..1ca586e 100644
--- a/httemplate/elements/select-terms.html
+++ b/httemplate/elements/select-terms.html
@@ -33,7 +33,7 @@ my $empty_label =
my $empty_value = $opt{'empty_value'} || '';
my @terms = ( 'Payable upon receipt',
- ( map "Net $_", 0, 10, 15, 20, 30, 45, 60 ),
+ ( map "Net $_", 0, 10, 15, 20, 30, 45, 60, 90 ),
);
my @pre_options = $opt{pre_options} ? @{ $opt{pre_options} } : ();
diff --git a/httemplate/elements/select-user.html b/httemplate/elements/select-user.html
index bdb92e7..ec2341b 100644
--- a/httemplate/elements/select-user.html
+++ b/httemplate/elements/select-user.html
@@ -4,8 +4,11 @@
<OPTION VALUE="">all</OPTION>
% }
-% foreach my $otaker ( @{ $opt{'otakers'} } ) {
- <OPTION VALUE="<% shift(@{$opt{'usernums'}}) %>"><% $otaker %></OPTION>
+% foreach my $usernum (
+% sort { $opt{'access_user'}->{$a} cmp $opt{'access_user'}->{$b} }
+% keys %{ $opt{'access_user'} }
+% ) {
+ <OPTION VALUE="<%$usernum%>"><% $opt{'access_user'}->{$usernum} %></OPTION>
% }
</SELECT>
@@ -14,15 +17,15 @@
my %opt = @_;
-unless ( $opt{'otakers'} ) {
+unless ( $opt{'access_user'} ) {
- my $sth = dbh->prepare("SELECT username,usernum FROM access_user".
- " WHERE disabled = '' or disabled IS NULL")
- or die dbh->errstr;
+ my $sth = dbh->prepare("
+ SELECT usernum, username FROM access_user
+ WHERE disabled = '' or disabled IS NULL
+ ") or die dbh->errstr;
$sth->execute or die $sth->errstr;
- for($sth->fetchall_arrayref) {
- $opt{'otakers'} = [ map { $_->[0] } @$_ ];
- $opt{'usernums'} = [ map { $_->[1] } @$_ ];
+ while ( my $row = $sth->fetchrow_arrayref ) {
+ $opt{'access_user'}->{$row->[0]} = $row->[1];
}
}
diff --git a/httemplate/elements/standardize_locations.html b/httemplate/elements/standardize_locations.html
new file mode 100644
index 0000000..9f8b71c
--- /dev/null
+++ b/httemplate/elements/standardize_locations.html
@@ -0,0 +1,18 @@
+<% include('/elements/init_overlib.html') %>
+
+<% include( '/elements/xmlhttp.html',
+ 'url' => $p.'misc/xmlhttp-cust_main-address_standardize.html',
+ 'subs' => [ 'address_standardize' ],
+ #'method' => 'POST', #could get too long?
+ )
+%>
+
+<SCRIPT TYPE="text/javascript">
+ <% include('/elements/standardize_locations.js', %options) %>
+</SCRIPT>
+
+<%init>
+
+my (%options) = @_;
+
+</%init>
diff --git a/httemplate/elements/standardize_locations.js b/httemplate/elements/standardize_locations.js
new file mode 100644
index 0000000..e6a4aa6
--- /dev/null
+++ b/httemplate/elements/standardize_locations.js
@@ -0,0 +1,278 @@
+function standardize_locations() {
+
+ var cf = document.<% $formname %>;
+
+ var state_el = cf.elements['<% $main_prefix %>state'];
+ var ship_state_el = cf.elements['<% $ship_prefix %>state'];
+
+ var address_info = new Array(
+% if ( $onlyship ) {
+ 'onlyship', 1,
+% } else {
+% if ( $withfirm ) {
+ 'company', cf.elements['<% $main_prefix %>company'].value,
+% }
+ 'address1', cf.elements['<% $main_prefix %>address1'].value,
+ 'address2', cf.elements['<% $main_prefix %>address2'].value,
+ 'city', cf.elements['<% $main_prefix %>city'].value,
+ 'state', state_el.options[ state_el.selectedIndex ].value,
+ 'zip', cf.elements['<% $main_prefix %>zip'].value,
+% }
+% if ( $withfirm ) {
+ 'ship_company', cf.elements['<% $ship_prefix %>company'].value,
+% }
+ 'ship_address1', cf.elements['<% $ship_prefix %>address1'].value,
+ 'ship_address2', cf.elements['<% $ship_prefix %>address2'].value,
+ 'ship_city', cf.elements['<% $ship_prefix %>city'].value,
+ 'ship_state', ship_state_el.options[ ship_state_el.selectedIndex ].value,
+ 'ship_zip', cf.elements['<% $ship_prefix %>zip'].value
+ );
+
+ address_standardize( address_info, update_address );
+
+}
+
+var standardize_address;
+
+function update_address(arg) {
+
+ var argsHash = eval('(' + arg + ')');
+
+ var changed = argsHash['address_standardized'];
+ var ship_changed = argsHash['ship_address_standardized'];
+ var error = argsHash['error'];
+ var ship_error = argsHash['ship_error'];
+
+
+ //yay closures
+ standardize_address = function () {
+
+ var cf = document.<% $formname %>;
+ var state_el = cf.elements['<% $main_prefix %>state'];
+ var ship_state_el = cf.elements['<% $ship_prefix %>state'];
+
+% if ( !$onlyship ) {
+ if ( changed ) {
+% if ( $withfirm ) {
+ cf.elements['<% $main_prefix %>company'].value = argsHash['new_company'];
+% }
+ cf.elements['<% $main_prefix %>address1'].value = argsHash['new_address1'];
+ cf.elements['<% $main_prefix %>address2'].value = argsHash['new_address2'];
+ cf.elements['<% $main_prefix %>city'].value = argsHash['new_city'];
+ setselect(cf.elements['<% $main_prefix %>state'], argsHash['new_state']);
+ cf.elements['<% $main_prefix %>zip'].value = argsHash['new_zip'];
+ }
+% }
+
+ if ( ship_changed ) {
+% if ( $withfirm ) {
+ cf.elements['<% $ship_prefix %>company'].value = argsHash['new_ship_company'];
+% }
+ cf.elements['<% $ship_prefix %>address1'].value = argsHash['new_ship_address1'];
+ cf.elements['<% $ship_prefix %>address2'].value = argsHash['new_ship_address2'];
+ cf.elements['<% $ship_prefix %>city'].value = argsHash['new_ship_city'];
+ setselect(cf.elements['<% $ship_prefix %>state'], argsHash['new_ship_state']);
+ cf.elements['<% $ship_prefix %>zip'].value = argsHash['new_ship_zip'];
+ }
+
+ post_standardization();
+
+ }
+
+
+
+ if ( changed || ship_changed ) {
+
+% if ( $conf->exists('cust_main-auto_standardize_address') ) {
+
+ standardize_address();
+
+% } else {
+
+ // popup a confirmation popup
+
+ var confirm_change =
+ '<CENTER><BR><B>Confirm address standardization</B><BR><BR>' +
+ '<TABLE>';
+
+ if ( changed ) {
+
+ confirm_change = confirm_change +
+ '<TR><TH>Entered billing address</TH>' +
+ '<TH>Standardized billing address</TH></TR>';
+ // + '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ if ( argsHash['company'] || argsHash['new_company'] ) {
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['company'] +
+ '</TD><TD>' + argsHash['new_company'] + '</TD></TR>';
+ }
+
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['address1'] +
+ '</TD><TD>' + argsHash['new_address1'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['address2'] +
+ '</TD><TD>' + argsHash['new_address2'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['city'] + ', ' + argsHash['state'] + ' ' + argsHash['zip'] +
+ '</TD><TD>' + argsHash['new_city'] + ', ' + argsHash['new_state'] + ' ' + argsHash['new_zip'] + '</TD></TR>' +
+ '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ }
+
+ if ( ship_changed ) {
+
+ confirm_change = confirm_change +
+ '<TR><TH>Entered service address</TH>' +
+ '<TH>Standardized service address</TH></TR>';
+ // + '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ if ( argsHash['ship_company'] || argsHash['new_ship_company'] ) {
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['ship_company'] +
+ '</TD><TD>' + argsHash['new_ship_company'] + '</TD></TR>';
+ }
+
+ confirm_change = confirm_change +
+ '<TR><TD>' + argsHash['ship_address1'] +
+ '</TD><TD>' + argsHash['new_ship_address1'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['ship_address2'] +
+ '</TD><TD>' + argsHash['new_ship_address2'] + '</TD></TR>' +
+ '<TR><TD>' + argsHash['ship_city'] + ', ' + argsHash['ship_state'] + ' ' + argsHash['ship_zip'] +
+ '</TD><TD>' + argsHash['new_ship_city'] + ', ' + argsHash['new_ship_state'] + ' ' + argsHash['new_ship_zip'] + '</TD></TR>' +
+ '<TR><TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ }
+
+ var addresses = 'address';
+ var height = 268;
+ if ( changed && ship_changed ) {
+ addresses = 'addresses';
+ height = 396; // #what
+ }
+
+ confirm_change = confirm_change +
+ '<TR><TD>' +
+ '<BUTTON TYPE="button" onClick="post_standardization();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' +
+ '</TD><TD>' +
+ '<BUTTON TYPE="button" onClick="standardize_address();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' +
+ '</TD></TR>' +
+ '<TR><TD COLSPAN=2 ALIGN="center">' +
+ '<BUTTON TYPE="button" onClick="document.<% $formname %>.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' +
+
+ '</TABLE></CENTER>';
+
+ overlib( confirm_change, CAPTION, 'Confirm address standardization', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, height, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 );
+
+% }
+
+ } else {
+
+ post_standardization();
+
+ }
+
+
+}
+
+function post_standardization() {
+
+ var cf = document.<% $formname %>;
+
+% if ( $conf->exists('enable_taxproducts') ) {
+
+ if ( new String(cf.elements['<% $taxpre %>zip'].value).length < 10 )
+ {
+
+ var country_el = cf.elements['<% $taxpre %>country'];
+ var country = country_el.options[ country_el.selectedIndex ].value;
+ var geocode = cf.elements['geocode'].value;
+
+ if ( country == 'CA' || country == 'US' ) {
+
+ var state_el = cf.elements['<% $taxpre %>state'];
+ var state = state_el.options[ state_el.selectedIndex ].value;
+
+ var url = "<% $p %>/misc/choose_tax_location.html" +
+ "?data_vendor=cch-zip" +
+ ";city=" + cf.elements['<% $taxpre %>city'].value +
+ ";state=" + state +
+ ";zip=" + cf.elements['<% $taxpre %>zip'].value +
+ ";country=" + country +
+ ";geocode=" + geocode +
+ ";formname=" + '<% $formname %>' +
+ ";";
+
+ // popup a chooser
+ OLgetAJAX( url, update_geocode, 300 );
+
+ } else {
+
+ cf.elements['geocode'].value = 'DEFAULT';
+ <% $post_geocode %>;
+
+ }
+
+ } else {
+
+ cf.elements['geocode'].value = '';
+ <% $post_geocode %>;
+
+ }
+
+% } else {
+
+ <% $post_geocode %>;
+
+% }
+
+}
+
+function update_geocode() {
+
+ //yay closures
+ set_geocode = function (what) {
+
+ var cf = document.<% $formname %>;
+
+ //alert(what.options[what.selectedIndex].value);
+ var argsHash = eval('(' + what.options[what.selectedIndex].value + ')');
+ cf.elements['<% $taxpre %>city'].value = argsHash['city'];
+ setselect(cf.elements['<% $taxpre %>state'], argsHash['state']);
+ cf.elements['<% $taxpre %>zip'].value = argsHash['zip'];
+ cf.elements['geocode'].value = argsHash['geocode'];
+ <% $post_geocode %>;
+
+ }
+
+ // popup a chooser
+
+ overlib( OLresponseAJAX, CAPTION, 'Select tax location', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 );
+
+}
+
+function setselect(el, value) {
+
+ for ( var s = 0; s < el.options.length; s++ ) {
+ if ( el.options[s].value == value ) {
+ el.selectedIndex = s;
+ }
+ }
+
+}
+<%init>
+
+my %opt = @_;
+my $conf = new FS::Conf;
+
+my $withfirm = 1;
+
+my $formname = $opt{form} || 'CustomerForm';
+my $onlyship = $opt{onlyship} || '';
+my $main_prefix = $opt{main_prefix} || '';
+my $ship_prefix = $opt{ship_prefix} || ($onlyship ? '' : 'ship_');
+my $taxpre = $main_prefix;
+$taxpre = $ship_prefix if ( $conf->exists('tax-ship_address') || $onlyship );
+my $post_geocode = $opt{callback} || 'post_geocode();';
+$withfirm = 0 if $opt{no_company};
+
+</%init>
diff --git a/httemplate/elements/table-grid.html b/httemplate/elements/table-grid.html
index e1e6c36..4d7deea 100644
--- a/httemplate/elements/table-grid.html
+++ b/httemplate/elements/table-grid.html
@@ -1,8 +1,7 @@
<STYLE TYPE="text/css">
-.grid table { border: solid; empty-cells: show }
-.grid TH { padding-left: 3px; padding-right: 3px; border: 1px solid #dddddd; border-bottom: dashed 1px black; border-right: none }
-.grid TD { padding-left: 3px; padding-right: 3px; empty-cells: show; border: 1px solid #cccccc; border-bottom: none; border-right: none }
+.grid TH { padding-left: 3px; padding-right: 3px; padding-bottom: 2px; border: none; empty-cells: show }
+.grid TD { padding-left: 3px; padding-right: 3px; padding-bottom: 2px; border: none; empty-cells: show }
.inv table { border: none }
.inv TH { border: none }
@@ -10,7 +9,7 @@
</STYLE>
-<TABLE CLASS="grid" CELLSPACING=<% $opt{cellspacing} %> CELLPADDING=<% $opt{cellpadding} %> BORDER=1 BORDERCOLOR="#000000" <% $opt{bgcolor} %> STYLE="border: solid 1px black; empty-cells: show">
+<TABLE CLASS="grid" CELLSPACING=<% $opt{cellspacing} %> CELLPADDING=<% $opt{cellpadding} %> <% $opt{bgcolor} %> STYLE="border: 1px solid #cccccc;">
<%init>
diff --git a/httemplate/elements/tr-pkg_svc.html b/httemplate/elements/tr-pkg_svc.html
index e66bdf7..e68ed4a 100644
--- a/httemplate/elements/tr-pkg_svc.html
+++ b/httemplate/elements/tr-pkg_svc.html
@@ -84,7 +84,7 @@ my @part_svc = qsearch('part_svc', {}, '', $where);
#my $q_part_pkg = $clone_part_pkg || $part_pkg;
#my %pkg_svc = map { $_->svcpart => $_ } $q_part_pkg->pkg_svc;
-my %pkg_svc = map { $_->svcpart => $_ } $part_pkg->pkg_svc;
+my %pkg_svc = map { $_->svcpart => $_ } $part_pkg->pkg_svc('disable_linked'=>1);
my @fixups = ();
my $count = 0;
diff --git a/httemplate/elements/tr-select-cust_tag.html b/httemplate/elements/tr-select-cust_tag.html
index d88f3a8..b2b6d96 100644
--- a/httemplate/elements/tr-select-cust_tag.html
+++ b/httemplate/elements/tr-select-cust_tag.html
@@ -1,4 +1,4 @@
-% if ( $curuser->access_right('Edit customer tags') && @part_tag ) {
+% if ( ($curuser->access_right('Edit customer tags') && @part_tag) || $is_report ) {
<TR>
<TD ALIGN="right"><% $opt{'label'} || 'Tags' %></TD>
@@ -25,6 +25,7 @@ my $curuser = $FS::CurrentUser::CurrentUser;
my %opt = @_;
my $cgi = $opt{'cgi'};
+my $is_report = $opt{'is_report'};
my @curr_tagnum = ();
if ( $cgi->param('error') ) {
diff --git a/httemplate/elements/tr-select-discount_term.html b/httemplate/elements/tr-select-discount_term.html
new file mode 100644
index 0000000..5858267
--- /dev/null
+++ b/httemplate/elements/tr-select-discount_term.html
@@ -0,0 +1,25 @@
+% if ( scalar(@discount_term) ) {
+ <TR>
+ <TD ALIGN="right">Prepayment for</TD>
+ <TD COLSPAN=2>
+ <% include('select-discount_term.html',
+ 'discount_term' => \@discount_term,
+ 'cgi' => $opt{'cgi'},
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+
+<%init>
+my %opt = @_;
+
+my $custnum = $opt{'custnum'};
+
+my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
+ or die "unknown custnum $custnum\n";
+
+my @discount_term = $cust_main->discount_terms;
+
+</%init>
diff --git a/httemplate/elements/xmlhttp.html b/httemplate/elements/xmlhttp.html
index 2df3c42..ac6f991 100644
--- a/httemplate/elements/xmlhttp.html
+++ b/httemplate/elements/xmlhttp.html
@@ -58,7 +58,11 @@ Example:
return;
if (xmlhttp.status != 200) {
- alert(xmlhttp.status + " status connecting to " + url);
+ if ( xmlhttp.status != 0 ) {
+ //not warning on the 0 errors, they pop up when navagating away
+ // from the page
+ alert(xmlhttp.status + " status connecting to " + url);
+ }
} else {
var data = xmlhttp.responseText;
//alert('received response: ' + data);
diff --git a/httemplate/graph/money_time.cgi b/httemplate/graph/money_time.cgi
index 4e4157e..cde71be 100644
--- a/httemplate/graph/money_time.cgi
+++ b/httemplate/graph/money_time.cgi
@@ -88,7 +88,7 @@ my %link = (
'netsales' => "${p}search/cust_bill.html?agentnum=$agentnum;net=1;",
'credits' => "${p}search/cust_credit.html?agentnum=$agentnum;",
'netcredits' => "${p}search/cust_credit_bill.html?agentnum=$agentnum;",
- 'payments' => "${p}search/cust_pay.cgi?magic=_date;agentnum=$agentnum;",
+ 'payments' => "${p}search/cust_pay.html?magic=_date;agentnum=$agentnum;",
'receipts' => "${p}search/cust_bill_pay.html?agentnum=$agentnum;",
'refunds' => "${p}search/cust_refund.html?magic=_date;agentnum=$agentnum;",
'netrefunds' => "${p}search/cust_credit_refund.html?agentnum=$agentnum;",
diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html
index 505f2d0..610f6e1 100644
--- a/httemplate/misc/batch-cust_pay.html
+++ b/httemplate/misc/batch-cust_pay.html
@@ -13,23 +13,63 @@ function warnUnload() {
}
}
window.onbeforeunload = warnUnload;
+
+function select_discount_term(row, prefix) {
+ var custnum_obj = document.getElementById('custnum'+prefix+row);
+ var select_obj = document.getElementById('discount_term'+prefix+row);
+
+ var value = '';
+ if (select_obj.type == 'hidden') {
+ value = select_obj.value;
+ }
+
+ var term_select = document.createElement('SELECT');
+ term_select.setAttribute('name', 'discount_term'+row);
+ term_select.setAttribute('id', 'discount_term'+row);
+ term_select.setAttribute('rownum', row);
+ term_select.style.display = '';
+ select_obj.parentNode.replaceChild(term_select, select_obj);
+ opt(term_select, '', '1 month');
+
+ function select_discount_term_update(discount_terms) {
+
+ var termArray = eval('(' + discount_terms + ')');
+ for ( var t = 0; t < termArray.length; t++ ) {
+ opt(term_select, termArray[t][0], termArray[t][1]);
+ if (termArray[t][0] == value) {
+ term_select.selectedIndex = t+1;
+ }
+ }
+
+ }
+
+ discount_terms(custnum_obj.value, select_discount_term_update);
+
+}
</SCRIPT>
+<% include('/elements/xmlhttp.html',
+ 'url' => $p. 'misc/xmlhttp-cust_main-discount_terms.cgi',
+ 'subs' => [qw( discount_terms )],
+ )
+%>
+
<FORM ACTION="process/batch-cust_pay.cgi" NAME="OneTrueForm" METHOD="POST" onsubmit="document.OneTrueForm.submit.disabled=true;window.onbeforeunload = null;">
<!-- <B>Batch</B> <INPUT TYPE="text" NAME="paybatch"><BR><BR> -->
<% include( "/elements/customer-table.html",
name_singular => 'payment',
- header => [ '', 'Amount', 'Check #', '' ],
- fields => [ sub {'$'}, 'paid', 'payinfo', 'error', ],
- types => [ 'immutable', '', '', 'immutable', ],
- align => [ 'c', 'r', 'r', 'l' ],
- sizes => [ 0, 8, 10, 0, ],
- colors => [ '', '', '', '#ff0000' ],
- param => { () },
- footer => [ '$', '_TOTAL', '', '' ],
- footer_align => [ 'c', 'r', 'r', '' ],
+ header => \@header,
+ fields => \@fields,
+ types => \@types,
+ align => \@align,
+ sizes => \@sizes,
+ colors => \@colors,
+ param => \%param,
+ footer => \@footer,
+ footer_align => \@footer_align,
+ custnum_update_callback => $custnum_update_callback,
)
%>
@@ -41,6 +81,14 @@ window.onbeforeunload = warnUnload;
</FORM>
+%if ( $cgi->param('error') ) {
+<SCRIPT TYPE="text/javascript">
+% for ( my $row = 0; defined($cgi->param("custnum$row")); $row++ ) {
+ select_discount_term(<% $row %>, '');
+% }
+</SCRIPT>
+%}
+
<% include('/elements/footer.html') %>
<%init>
@@ -48,4 +96,36 @@ window.onbeforeunload = warnUnload;
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Post payment batch');
+my @header = ( '', 'Amount', 'Check #' );
+my @fields = ( sub {'$'}, 'paid', 'payinfo' );
+my @types = ( 'immutable', '', '' );
+my @align = ( 'c', 'r', 'r' );
+my @sizes = ( 0, 8, 10 );
+my @colors = ( '', '', '' );
+my %param = ();
+my @footer = ( '$', '_TOTAL', '' );
+my @footer_align = ( 'c', 'r', 'r' );
+my $custnum_update_callback = '';
+
+if ( FS::Record->scalar_sql('SELECT count(*) FROM part_pkg_discount') ) {
+ push @header, '';
+ push @fields, 'discount_term';
+ push @types, 'immutable';
+ push @align, 'r';
+ push @sizes, '0';
+ push @colors, '';
+ push @footer, '';
+ push @footer_align, '';
+ $custnum_update_callback = 'select_discount_term';
+}
+
+push @header, '';
+push @fields, 'error';
+push @types, 'immutable';
+push @align, 'l';
+push @sizes, '0';
+push @colors, '#ff0000';
+push @footer, '';
+push @footer_align, '';
+
</%init>
diff --git a/httemplate/misc/cancel_cust.html b/httemplate/misc/cancel_cust.html
index 12c37eb..b7ecccd 100644
--- a/httemplate/misc/cancel_cust.html
+++ b/httemplate/misc/cancel_cust.html
@@ -2,18 +2,46 @@
<% include('/elements/error.html') %>
+
<FORM NAME="cust_cancel_popup" ACTION="<% popurl(1) %>cust_main-cancel.cgi" METHOD=POST>
<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
<P ALIGN="center"><B>Permanently delete all services and cancel this customer?</B>
- <% $ban %>
-
-<BR><BR>
-
-<% ntable("#cccccc", 2) %>
+<TABLE BORDER="0" CELLSPACING="2"
+STYLE="margin-left:auto; margin-right:auto">
+<TR>
+ <TD ALIGN="right">
+ <INPUT TYPE="radio" NAME="now_or_later" VALUE="0" onclick="toggle(false)" CHECKED />
+ </TD>
+ <TD ALIGN="left">Cancel now</TD>
+</TR>
+<TR>
+ <TD ALIGN="right">
+ <INPUT TYPE="radio" NAME="now_or_later" VALUE="1" onclick="toggle(true)" />
+ </TD>
+ <TD ALIGN="left">Cancel on date:&nbsp;
+ <% include('/elements/input-date-field.html', {
+ 'name' => 'expire',
+ 'value' => time,
+ } ) %>
+ </TD>
+</TR>
+</TABLE>
+<SCRIPT type="text/javascript">
+function toggle(val) {
+ document.getElementById("expire_text").disabled = !val;
+ document.getElementById("ban").disabled = val;
+ document.getElementById("expire_button").style.visibility =
+ val ? 'visible' : 'hidden';
+}
+toggle(false);
+</SCRIPT>
+<% $ban %>
+<TABLE BGCOLOR="#cccccc", BORDER="0" CELLSPACING="2"
+STYLE="margin-left:auto; margin-right:auto">
<% include('/elements/tr-select-reason.html',
'field' => 'reasonnum',
'reason_class' => 'C',
@@ -50,8 +78,8 @@ die "No customer # $custnum" unless $cust_main;
my $ban = '';
if ( $cust_main->payby =~ /^(CARD|DCRD|CHEK|DCHK)$/ ) {
- $ban = '<BR><P ALIGN="center">'.
- '<INPUT TYPE="checkbox" NAME="ban" VALUE="1"> Ban this customer\'s ';
+ $ban = '<P ALIGN="center">'.
+ '<INPUT TYPE="checkbox" NAME="ban" ID="ban" VALUE="1"> Ban this customer\'s ';
if ( $cust_main->payby =~ /^(CARD|DCRD)$/ ) {
$ban .= 'credit card';
} elsif ( $cust_main->payby =~ /^(CHEK|DCHK)$/ ) {
diff --git a/httemplate/misc/change_pkg.cgi b/httemplate/misc/change_pkg.cgi
index 16b7071..ec10b85 100755
--- a/httemplate/misc/change_pkg.cgi
+++ b/httemplate/misc/change_pkg.cgi
@@ -2,7 +2,7 @@
<% include('/elements/error.html') %>
-<FORM ACTION="<% $p %>edit/process/change-cust_pkg.html" METHOD=POST>
+<FORM NAME="OrderPkgForm" ACTION="<% $p %>edit/process/change-cust_pkg.html" METHOD=POST>
<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
<% ntable('#cccccc') %>
@@ -31,8 +31,16 @@
</TABLE>
+<% include( '/elements/standardize_locations.html',
+ 'form' => "OrderPkgForm",
+ 'onlyship' => 1,
+ 'no_company' => 1,
+ 'callback' => 'document.OrderPkgForm.submit();',
+ )
+%>
+
<BR>
-<INPUT TYPE="submit" VALUE="Change package">
+<INPUT NAME="submitButton" TYPE="button" VALUE="Change package" onClick="this.disabled=true; standardize_locations();">
</FORM>
</BODY>
diff --git a/httemplate/misc/choose_tax_location.html b/httemplate/misc/choose_tax_location.html
new file mode 100644
index 0000000..dce04c7
--- /dev/null
+++ b/httemplate/misc/choose_tax_location.html
@@ -0,0 +1,90 @@
+<FORM NAME="choosegeocodeform">
+<CENTER><BR><B>Choose tax location</B><BR><BR>
+<P>the geocode is:<% $header %></P>
+<P STYLE="<% $style %>"><% $header %></P>
+
+<SELECT NAME='geocodes' ID='geocodes' STYLE="<% $style %>">
+% foreach my $location (@cust_tax_location) {
+% my %value = ( zip => $zip5,
+% map { $_ => $location->$_ }
+% qw ( city state geocode )
+% );
+% map { $value{$_} = $location{$_} } qw ( city state )
+% if $location{country} eq 'CA';
+%
+% my $value = encode_entities(objToJson({ %value })
+% );
+% my $content = '';
+% $content .= $location->$_. '&nbsp;' x ( $max{$_} - length($location->$_) )
+% foreach qw( city county state );
+% $content .= $location->cityflag eq 'I' ? 'Y' : 'N' ;
+% my $selected = '' ;
+% if ($geocode && $location->geocode eq $geocode) {
+% $selected = 'SELECTED';
+% }
+ <OPTION VALUE="<% $value %>" STYLE="<% $style %>" <% $selected %>><% $content %>
+% }
+</SELECT><BR><BR>
+
+<TABLE><TR>
+ <TD> <BUTTON TYPE="button" onClick="set_geocode(document.getElementById('geocodes'));"><IMG SRC="<%$p%>images/tick.png" ALT=""> Set location </BUTTON></TD>
+ <TD><BUTTON TYPE="button" onClick="document.<% $formname %>.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission </BUTTON></TD>
+</TR>
+</TABLE>
+
+</CENTER>
+</FORM>
+<%init>
+
+my $conf = new FS::Conf;
+
+my %location = ();
+
+($location{data_vendor}) = $cgi->param('data_vendor') =~ /^([-\w]+)$/;
+($location{city}) = $cgi->param('city') =~ /^([\w ]+)$/;
+($location{state}) = $cgi->param('state') =~ /^(\w+)$/;
+($location{zip}) = $cgi->param('zip') =~ /^([-\w ]+)$/;
+($location{country}) = $cgi->param('country') =~ /^([\w ]+)$/;
+
+my($geocode) = $cgi->param('geocode') =~ /^([\w]+)$/;
+
+my($formname) = $cgi->param('formname') =~ /^([\w]*)$/;
+$formname ||= 'CustomerForm';
+
+my($zip5, $zip4) = split('-', $location{zip});
+
+#only support US & CA
+my $hashref = { 'data_vendor' => $location{data_vendor} };
+$hashref->{zip} = $location{country} eq 'CA' ? substr($zip5,0,1) : $zip5,
+
+my @keys = keys(%$hashref);
+my @cust_tax_location = ();
+until ( @cust_tax_location ) {
+ @cust_tax_location = qsearch({ table => 'cust_tax_location',
+ hashref => $hashref,
+ order_by => 'LIMIT 50',
+ });
+ last unless scalar(@keys);
+ delete $hashref->{ shift @keys };
+}
+
+my %max = ( city => 4, county => 6, state => 5);
+foreach my $location (@cust_tax_location) {
+ foreach ( qw( city county state ) ) {
+ my $length = length($location->$_);
+ $max{$_} = ($length > $max{$_}) ? $length : $max{$_};
+ }
+}
+foreach ( qw( city county state ) ) {
+ $max{$_} = $location{$_} if $location{$_} > $max{$_};
+ $max{$_}++;
+}
+
+my $header = '&nbsp;&nbsp;';
+$header .= $_. '&nbsp;' x ( $max{lc($_)} - length($_) )
+ foreach qw( City County State );
+$header .= "In city?";
+
+my $style = "font-family:monospace;";
+
+</%init>
diff --git a/httemplate/misc/cust_main-cancel.cgi b/httemplate/misc/cust_main-cancel.cgi
index 009a7d4..44be20c 100755
--- a/httemplate/misc/cust_main-cancel.cgi
+++ b/httemplate/misc/cust_main-cancel.cgi
@@ -1,4 +1,4 @@
-<% header("Customer cancelled") %>
+<% include('/elements/header.html', "Customer cancelled") %>
<SCRIPT TYPE="text/javascript">
window.top.location.reload();
</SCRIPT>
@@ -11,9 +11,11 @@ die "access denied"
my $custnum;
my $ban = '';
+my $expire = '';
if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
$custnum = $1;
$ban = $cgi->param('ban');
+ $expire = $cgi->param('expire');
} else {
my($query) = $cgi->keywords;
$query =~ /^(\d+)$/ || die "Illegal custnum";
@@ -42,11 +44,28 @@ my $cust_main = qsearchs( {
'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
} );
-warn "cancelling $cust_main";
-my @errors = $cust_main->cancel(
- 'ban' => $ban,
- 'reason' => $reasonnum,
-);
+my @errors;
+if($cgi->param('now_or_later')) {
+ $expire = parse_datetime($expire);
+ if($expire) {
+ #warn "setting expire dates on custnum#$custnum\n";
+ my @pkgs = $cust_main->ncancelled_pkgs;
+ @errors = grep {$_} map { $_->cancel(
+ 'reason' => $reasonnum,
+ 'date' => $expire,
+ ) } @pkgs;
+ }
+ else {
+ @errors = ("error parsing expire date: ".$cgi->param('expire'));
+ }
+}
+else {
+ warn "cancelling $cust_main";
+ @errors = $cust_main->cancel(
+ 'ban' => $ban,
+ 'reason' => $reasonnum,
+ );
+}
my $error = join(' / ', @errors) if scalar(@errors);
if ( $error ) {
diff --git a/httemplate/misc/cust_main-import.cgi b/httemplate/misc/cust_main-import.cgi
index 9c1f984..edf4665 100644
--- a/httemplate/misc/cust_main-import.cgi
+++ b/httemplate/misc/cust_main-import.cgi
@@ -30,7 +30,9 @@ Import a file containing customer records.
<SELECT NAME="format">
<!-- <OPTION VALUE="simple">Simple -->
<OPTION VALUE="extended" SELECTED>Extended
+ <OPTION VALUE="extended-plus_options">Extended + options
<OPTION VALUE="extended-plus_company">Extended plus company
+ <OPTION VALUE="extended-plus_company_and_options">Extended plus company and options
<OPTION VALUE="svc_external">External service
<OPTION VALUE="svc_external_svc_phone">External service and phone service
</SELECT>
@@ -89,7 +91,11 @@ Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets.
<b>Extended</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password</i>
<BR><BR>
+<b>Extended plus options</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password, options</i>
+
<b>Extended plus company</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password</i>
+
+<b>Extended plus company and options </b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, username, _password, options</i>
<BR><BR>
<b>External service</b> format has the following field order: <i>agent_custid, refnum<%$req%>, last<%$req%>, first<%$req%>, company, address1<%$req%>, address2, city<%$req%>, state<%$req%>, zip<%$req%>, country, daytime, night, ship_last, ship_first, ship_company, ship_address1, ship_address2, ship_city, ship_state, ship_zip, ship_country, payinfo, paycvv, paydate, invoicing_list, pkgpart, next_bill_date, id, title</i>
@@ -111,7 +117,7 @@ Field information:
of an integer, the string is searched for and if necessary auto-created in the
advertising source table.
- <li><i>payinfo</i>: Credit card number, or leave this, <i>paycvv</i> and <i>paydate</i> blank for email/paper invoicing.
+ <li><i>payinfo</i>: Credit card number, or leave this, <i>paycvv</i> and <i>paydate</i> blank for email/paper invoicing. You may optionally prepend an 'A' or 'D' to the credit card number for automatic or on demand of customer billing respectively
<li><i>paycvv</i>: CVV2 number (three digits on the back of the credit card)
@@ -119,7 +125,7 @@ advertising source table.
<li><i>invoicing_list</i>: Email address for invoices, or POST for postal invoices.
- <li><i>pkgpart</i>: Package definition. Configuration -&gt; Provisioning, services and packages -&gt; View/Edit package definitions
+ <li><i>pkgpart</i>: Package definition. Configuration -&gt; Packages -&gt; Package definitions
<li><i>username</i> and <i>_password</i> are required if <i>pkgpart</i> is specified. (Extended and Extended plus company formats)
@@ -127,6 +133,13 @@ advertising source table.
<li><i>title</i>: External service identifier, text
+ <li><i>options</i>: text containing one or more of
+
+ <ul>
+ <li>taxexempt: this customer does not pay taxes
+ <li>postalinvoice: ensure this customer receives a postal invoice
+ </ul>
+
</ul>
<BR>
diff --git a/httemplate/misc/cust_main-import_charges.cgi b/httemplate/misc/cust_main-import_charges.cgi
index 3801929..c844e0e 100644
--- a/httemplate/misc/cust_main-import_charges.cgi
+++ b/httemplate/misc/cust_main-import_charges.cgi
@@ -1,22 +1,69 @@
-<% include('/elements/header.html', 'Batch Customer Charge') %>
+<% include("/elements/header.html",'Batch Payment Charge') %>
+
+Import a CSV file containing customer payments.
+<BR><BR>
<FORM ACTION="process/cust_main-import_charges.cgi" METHOD="post" ENCTYPE="multipart/form-data">
-Import a CSV file containing customer charges.<BR><BR>
-Default file format is CSV, with the following field order: <i>custnum, amount, description</i><BR><BR>
-If <i>amount</i> is negative, a credit will be applied instead.<BR><BR>
-<BR><BR>
+<% &ntable("#cccccc", 2) %>
+
+<% include('/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+%>
+
+<TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <OPTION VALUE="simple">Simple
+<!-- <OPTION VALUE="extended" SELECTED>Extended -->
+ </SELECT>
+ </TD>
+</TR>
+
+<TR>
+ <TH ALIGN="right">CSV filename</TH>
+ <TD><INPUT TYPE="file" NAME="csvfile"></TD>
+</TR>
-CSV Filename: <INPUT TYPE="file" NAME="csvfile"><BR><BR>
-<INPUT TYPE="submit" VALUE="Import">
+<TR><TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px"><INPUT TYPE="submit" VALUE="Import CSV file"></TD></TR>
+
+</TABLE>
</FORM>
-<% include('/elements/footer.html') %>
+<BR>
+
+Simple file format is CSV, with the following field order: <i>custnum, agent_custid, amount, description</i>
+<BR><BR>
+
+<!-- Extended file format is not yet defined</i>
+<BR><BR> -->
+Field information:
+
+<ul>
+
+ <li><i>custnum</i>: This is the freeside customer number. It may be left blank. If specified, agent_custid must be blank.
+
+ <li><i>agent_custid</i>: This is the reseller's idea of the customer number or identifier. It may be left blank. If specified, custnum must be blank.
+
+ <li><i>amount</i>: A numeric value with at most two digits after the decimal point. If <i>amount</i> is negative, a credit will be applied instead.
+
+ <li><i>description</i>: Text describing the transaction.
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
<%init>
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Import');
</%init>
+
diff --git a/httemplate/misc/cust_main-merge.html b/httemplate/misc/cust_main-merge.html
new file mode 100755
index 0000000..4decbef
--- /dev/null
+++ b/httemplate/misc/cust_main-merge.html
@@ -0,0 +1,40 @@
+% if ( $error ) {
+% $cgi->param('error', $error);
+<% $cgi->redirect(popurl(1). "merge_cust.html?". $cgi->query_string ) %>
+% } else {
+<% include('/elements/header-popup.html', "Customer merged") %>
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.href = '<% $p %>view/cust_main.cgi?<% $new_custnum %>';
+%# parent.nd(1) ?
+ </SCRIPT>
+ </BODY>
+</HTML>
+% }
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Merge customer');
+
+my $error = '';
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die "illegal custnum";
+my $custnum = $1;
+
+my $new_custnum;
+if ( $cgi->param('new_custnum') =~ /^(\d+)$/ ) {
+ $new_custnum = $1;
+
+ my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+ } );
+ die "No customer # $custnum" unless $cust_main;
+
+ $error = $cust_main->merge($new_custnum);
+
+} else {
+ $error = 'Select a customer to merge into';
+}
+
+</%init>
diff --git a/httemplate/misc/cust_main_note-import.cgi b/httemplate/misc/cust_main_note-import.cgi
index b93c5c1..8a94ae4 100644
--- a/httemplate/misc/cust_main_note-import.cgi
+++ b/httemplate/misc/cust_main_note-import.cgi
@@ -108,6 +108,7 @@
% my $fh = $cgi->upload('csvfile');
% my $csv = new Text::CSV_XS;
% my $skip_fuzzies = $cgi->param('fuzzies') ? 0 : 1;
+% my $use_agent_custid = $cgi->param('use_agent_custid') ? 1 : 0;
%
% if ( defined($fh) ) {
<TABLE BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0>
@@ -118,7 +119,7 @@
<TH>First</TH>
<TH>Note to be added</TH>
</TR>
-% my $agentnum => scalar($cgi->param('agentnum')),
+% my $agentnum = scalar($cgi->param('agentnum'));
% my $line;
% my $row = 0;
% while ( defined($line=<$fh>) ) {
@@ -138,7 +139,10 @@
% next unless ( $last || $first || $note );
% my @cust_main = ();
% warn "searching for: $last, $first" if ($first || $last);
-% if ($custnum) {
+% if ($agentnum && $custnum && $use_agent_custid) {
+% @cust_main = qsearch('cust_main', { 'agent' => $agentnum,
+% 'agent_custid' => $custnum } );
+% } elsif ($custnum) { # && !use_agent_custid
% @cust_main = qsearch('cust_main', { 'custnum' => $custnum });
% } else {
% @cust_main = FS::cust_main::smart_search(
diff --git a/httemplate/misc/cust_main_note-import.html b/httemplate/misc/cust_main_note-import.html
index d8fefa7..cc1645d 100644
--- a/httemplate/misc/cust_main_note-import.html
+++ b/httemplate/misc/cust_main_note-import.html
@@ -13,6 +13,13 @@ Anything after the character sequence #! is ignored.
<% &ntable("#cccccc") %>
+<% include('/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+%>
+
<TR>
<TH ALIGN="right">CSV filename</TH>
<TD><INPUT TYPE="file" NAME="csvfile"></TD>
@@ -22,6 +29,11 @@ Anything after the character sequence #! is ignored.
<TD><INPUT TYPE="checkbox" NAME="fuzzies"></TD>
</TR>
+<TR>
+ <TH ALIGN="right">custnum is reseller's customer number</TH>
+ <TD><INPUT TYPE="checkbox" NAME="use_agent_custid"></TD>
+</TR>
+
</TABLE>
<BR><BR>
diff --git a/httemplate/misc/cust_pkg-import.html b/httemplate/misc/cust_pkg-import.html
new file mode 100644
index 0000000..b29884d
--- /dev/null
+++ b/httemplate/misc/cust_pkg-import.html
@@ -0,0 +1,150 @@
+<% include("/elements/header.html",'Batch Package Import') %>
+
+Import a file containing package records.
+<BR><BR>
+
+<% include( '/elements/form-file_upload.html',
+ 'name' => 'PackageImportForm',
+ 'action' => 'process/cust_pkg-import.html',
+ 'num_files' => 1,
+ 'fields' => [ 'agentnum', 'pkgbatch', 'format' ],
+ 'message' => 'Package import successful',
+ 'url' => $p."search/cust_pkg.cgi?pkgbatch=$pkgbatch",
+ )
+%>
+
+<% &ntable("#cccccc", 2) %>
+
+ <% include( '/elements/tr-select-agent.html',
+ #'curr_value' => '', #$agentnum,
+ 'label' => "<B>Agent</B>",
+ 'empty_label' => 'Select agent',
+ )
+ %>
+
+ <INPUT TYPE="hidden" NAME="pkgbatch" VALUE="<% $pkgbatch %>"%>
+
+ <TR>
+ <TH ALIGN="right">Format</TH>
+ <TD>
+ <SELECT NAME="format">
+ <OPTION VALUE="default" SELECTED>Default
+ <OPTION VALUE="default-agent_custid">Default with agent_custid
+ <OPTION VALUE="svc_acct">Account service
+ <OPTION VALUE="svc_acct-agent_custid">Account service with agent_custid
+ <OPTION VALUE="svc_phone">Phone service
+ <OPTION VALUE="svc_phone-agent_custid">Phone service with agent_custid
+ <OPTION VALUE="svc_external">External service
+ <OPTION VALUE="svc_external-agent_custid">External service with agent_custid
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/file-upload.html',
+ 'field' => 'file',
+ 'label' => 'Filename',
+ )
+ %>
+
+ <TR>
+ <TD COLSPAN=2 ALIGN="center" STYLE="padding-top:6px">
+ <INPUT TYPE = "submit"
+ ID = "submit"
+ VALUE = "Import file"
+ onClick = "document.PackageImportForm.submit.disabled=true;"
+ >
+ </TD>
+ </TR>
+
+</TABLE>
+
+</FORM>
+
+<BR>
+Uploaded files can be CSV (comma-separated value) files or Excel spreadsheets. The file should have a .CSV or .XLS extension.
+<BR><BR>
+
+<b>Default</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire</i>
+<BR><BR>
+
+<b>Default with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire</i>
+<BR><BR>
+
+<b>Account service</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, username, _password, domsvc</i>
+<BR><BR>
+
+<b>Account service with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, username, _password, domsvc</i>
+<BR><BR>
+
+<b>Phone sevice</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, countrycode, phonenum, sip_password, pin</i>
+<BR><BR>
+
+<b>Phone service with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, countrycode, phonenum, sip_password, pin</i>
+<BR><BR>
+
+<b>External sevice</b> format has the following field order: <i>custnum<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, id, title</i>
+<BR><BR>
+
+<b>External service with agent_custid</b> format has the following field order: <i>agent_custid<%$req%>, pkgpart<%$req%>, discountnum, start_date, setup, bill, last_bill, susp, adjourn, cancel, expire, id, title</i>
+<BR><BR>
+
+<%$req%> Required fields
+<BR><BR>
+
+Field information:
+
+<ul>
+
+ <li><i>custnum</i>: This specifies an existing customer by custnum.
+
+ <li><i>agent_custid</i>: This specifies an existing customer record by agent_custid.
+
+ <li><i>pkgpart</i>: Package definition. Configuration -&gt; Packages -&gt; Package definitions
+
+ <li><i>discountnum</i>: Optional discount. Configuration -&gt; Packages -&gt; Discounts
+
+ <li><i>start_date</i>: Indicates a future start date; do not fill in for active packages
+
+ <li><i>setup</i>: Indicates setup fee has been charged and package setup on this date
+
+ <li><i>bill</i>: Next bill date
+
+ <li><i>last_bill</i>: Last bill date
+
+ <li><i>susp</i>: Indicates the package is suspended (on the given date).
+
+ <li><i>adjourn</i>: Indicates a future suspension on this date.
+
+ <li><i>cancel</i>: Indicates the package is cancelled (on the given date).
+
+ <li><i>expire</i>: Indicates a future cancellation on this date.
+
+<!--
+ <li><i>username</i> and <i>_password</i> are required if <i>pkgpart</i> is specified. (Extended and Extended plus company formats)
+-->
+
+ <li><i>domsvc</i>: Domain svcnum
+
+ <li><i>id</i>: External service id, integer
+
+ <li><i>title</i>: External service identifier, text
+
+</ul>
+
+<BR>
+
+<% include('/elements/footer.html') %>
+
+<%once>
+
+my $req = qq!<font color="#ff0000">*</font>!;
+
+</%once>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $pkgbatch = time2str('webimport-%Y/%m/%d-%T'. "-$$-". rand() * 2**32, time);
+
+</%init>
diff --git a/httemplate/misc/custom_link_proxy.cgi b/httemplate/misc/custom_link_proxy.cgi
new file mode 100644
index 0000000..e5934e4
--- /dev/null
+++ b/httemplate/misc/custom_link_proxy.cgi
@@ -0,0 +1,24 @@
+% if( $response->is_success ) {
+<% $response->decoded_content %>
+% }
+% else {
+<% $response->error_as_HTML %>
+% }
+<%init>
+
+my( $custnum ) = $cgi->param('custnum');
+my $cust_main = qsearchs('cust_main', { custnum => $custnum } )
+ or die "custnum '$custnum' not found"; # just check for existence
+
+my $conf = new FS::Conf;
+my $url = $conf->config('cust_main-custom_link') . $cust_main->custnum;
+#warn $url;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+ unless $curuser->access_right('View customer');
+
+my $ua = new LWP::UserAgent;
+my $response = $ua->get($url);
+</%init>
diff --git a/httemplate/misc/delete-domain_record.cgi b/httemplate/misc/delete-domain_record.cgi
index 08eedde..200365d 100755
--- a/httemplate/misc/delete-domain_record.cgi
+++ b/httemplate/misc/delete-domain_record.cgi
@@ -1,7 +1,7 @@
% if ( $error ) {
% errorpage($error);
% } else {
-<% $cgi->redirect($p. "view/svc_domain.cgi?". $domain_record->svcnum) %>
+<% $cgi->redirect($p. "view/svc_domain.cgi?". $domain_record->svcnum. '#dns') %>
% }
<%init>
diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html
index 201aed4..759c8bf 100644
--- a/httemplate/misc/email-customers.html
+++ b/httemplate/misc/email-customers.html
@@ -1,69 +1,78 @@
<% include('/elements/header.html', $title) %>
<FORM NAME="OneTrueForm" ACTION="email-customers.html" METHOD="POST">
-% foreach my $key ( keys %search ) {
-% my @values = ref($search{$key}) ? @{$search{$key}} : ( $search{$key} );
-% foreach my $value ( @values ) {
- <INPUT TYPE="hidden" NAME="<% $key %>" VALUE="<% $value %>">
-% }
-% }
+<INPUT TYPE="hidden" NAME="table" VALUE="<% $table %>">
+%# Mixing search params with from address, subject, etc. required special-case
+%# handling of those, risked name conflicts, and caused massive problems with
+%# multi-valued search params. We are no longer in search context, so we
+%# pack the search into a Storable string for later use.
+<INPUT TYPE="hidden" NAME="search" VALUE="<% encode_base64(nfreeze(\%search)) %>">
-% if ( $cgi->param('magic') eq 'send' ) {
+% if ( $cgi->param('action') eq 'send' ) {
<FONT SIZE="+2">Sending notice</FONT>
<% include('/elements/progress-init.html',
'OneTrueForm',
- [ keys(%search), qw( from subject html_body text_body ) ],
+ [ qw( search table from subject html_body text_body msgnum ) ],
'process/email-customers.html',
{ 'message' => "Notice sent" }, #would be nice to show #, but..
)
%>
-% } elsif ( $cgi->param('magic') eq 'preview' ) {
+% } elsif ( $cgi->param('action') eq 'preview' ) {
<FONT SIZE="+2">Preview notice</FONT>
% }
-% if ( $cgi->param('magic') ) {
+% if ( $cgi->param('action') ) {
<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+ <INPUT TYPE="hidden" NAME="msgnum" VALUE="<% $cgi->param('msgnum') %>">
+
+% if ( $msg_template ) {
+ <% include('/elements/tr-fixed.html',
+ 'label' => 'Template:',
+ 'value' => $msg_template->msgname,
+ )
+ %>
+% }
<% include('/elements/tr-fixed.html',
'field' => 'from',
'label' => 'From:',
- 'value' => scalar( $cgi->param('from') ),
+ 'value' => scalar( $from ),
)
%>
<% include('/elements/tr-fixed.html',
'field' => 'subject',
'label' => 'Subject:',
- 'value' => scalar( $cgi->param('subject') ),
+ 'value' => scalar( $subject ),
)
%>
- <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $cgi->param('html_body') |h %>">
+ <INPUT TYPE="hidden" NAME="html_body" VALUE="<% $html_body |h %>">
<TR>
<TD ALIGN="right" VALIGN="top">Message (HTML display): </TD>
- <TD CLASS="background" ALIGN="left"><% $cgi->param('html_body') %></TD>
+ <TD CLASS="background" ALIGN="left"><% $html_body %></TD>
</TR>
% my $text_body = HTML::FormatText->new(leftmargin=>0)->format(
% HTML::TreeBuilder->new_from_content(
-% $cgi->param('html_body')
+% $html_body
% )
% );
<INPUT TYPE="hidden" NAME="text_body" VALUE="<% $text_body |h %>">
<TR>
<TD ALIGN="right" VALIGN="top">Message (Text display): </TD>
- <TD CLASS="background" ALIGN="left"><PRE><% $text_body %></PRE></TD>
+ <TD CLASS="background" STYLE="background-color:white" ALIGN="left"><PRE><% $text_body %></PRE></TD>
</TR>
</TABLE>
-% if ( $cgi->param('magic') eq 'preview' ) {
+% if ( $cgi->param('action') eq 'preview' ) {
<SCRIPT>
function areyousure(href) {
@@ -72,15 +81,29 @@
</SCRIPT>
<BR>
- <INPUT TYPE="hidden" NAME="magic" VALUE="send">
+ <INPUT TYPE="hidden" NAME="action" VALUE="send">
<INPUT TYPE="submit" VALUE="Send notice" onClick="return areyousure()">
% }
% } else {
- <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%">
+<SCRIPT TYPE="text/javascript">
+function toggle(obj) {
+ document.getElementById('table_no_template').style.display = (obj.value == 0) ? '' : 'none';
+}
+</SCRIPT>
+Template:
+ <% include('/elements/select-table.html',
+ 'label' => 'Template:',
+ 'table' => 'msg_template',
+ 'name_col' => 'msgname',
+ 'empty_label' => '(none)',
+ 'onchange' => 'toggle(this)',
+ )
+ %><BR>
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0 WIDTH="100%" id="table_no_template">
<% include('/elements/tr-input-text.html',
'field' => 'from',
'label' => 'From:',
@@ -102,15 +125,14 @@
%#Substitution vars:
- <BR><BR>
- <INPUT TYPE="hidden" NAME="magic" VALUE="preview">
+ <INPUT TYPE="hidden" NAME="action" VALUE="preview">
<INPUT TYPE="submit" VALUE="Preview notice">
% }
</FORM>
-% if ( $cgi->param('magic') eq 'send' ) {
+% if ( $cgi->param('action') eq 'send' ) {
<SCRIPT TYPE="text/javascript">
process();
</SCRIPT>
@@ -123,16 +145,32 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
-my %search = $cgi->Vars;
-delete $search{$_} for qw( magic from subject html_body text_body );
-$search{$_} = [ split(/\0/, $search{$_}) ]
- foreach grep { $_ eq 'payby' || $search{$_} =~ /\0/ } keys %search;
-
-my $title = 'Bulk send customer notices';
+my $table = $cgi->param('table') or die "'table' required";
+my %search;
+if ( $cgi->param('search') ) {
+ %search = %{ thaw(decode_base64($cgi->param('search'))) };
+}
+else {
+ %search = $cgi->Vars;
+ delete $search{$_} for qw( action table from subject html_body text_body );
+ # FS::$table->search is expected to know which parameters might be
+ # multi-valued, and to accept scalar values for them also. No good
+ # solution to this since CGI can't tell whether a parameter _might_
+ # have had multiple values, only whether it does.
+ @search{keys %search} = map { /\0/ ? [ split /\0/, $_ ] : $_ } values %search;
+}
+
+my $title = 'Send bulk customer notices';
my $num_cust;
-if ( $cgi->param('magic') eq 'preview' ) {
- my $sql_query = FS::cust_main->search(\%search);
+my $from = $cgi->param('from') || '';
+my $subject = $cgi->param('subject') || '';
+my $html_body = $cgi->param('html_body') || '';
+
+my $msg_template = '';
+
+if ( $cgi->param('action') eq 'preview' ) {
+ my $sql_query = "FS::$table"->search(\%search);
my $count_query = delete($sql_query->{'count_query'});
my $count_sth = dbh->prepare($count_query)
or die "Error preparing $count_query: ". dbh->errstr;
@@ -140,6 +178,17 @@ if ( $cgi->param('magic') eq 'preview' ) {
or die "Error executing $count_query: ". $count_sth->errstr;
my $count_arrayref = $count_sth->fetchrow_arrayref;
$num_cust = $count_arrayref->[0];
+
+ if ( $cgi->param('msgnum') ) {
+ $msg_template = qsearchs('msg_template',
+ { msgnum => $cgi->param('msgnum') } )
+ or die "template not found: ".$cgi->param('msgnum');
+ $sql_query->{'extra_sql'} .= ' LIMIT 1';
+ $sql_query->{'order_by'} = '';
+ my $cust = qsearchs($sql_query)->cust_main;
+ my %message = $msg_template->prepare( 'cust_main' => $cust );
+ ($from, $subject, $html_body) = @message{'from', 'subject', 'html_body'};
+ }
}
</%init>
diff --git a/httemplate/misc/merge_cust.html b/httemplate/misc/merge_cust.html
new file mode 100644
index 0000000..ad075be
--- /dev/null
+++ b/httemplate/misc/merge_cust.html
@@ -0,0 +1,72 @@
+<% include('/elements/header-popup.html', 'Merge customer' ) %>
+
+<% include('/elements/error.html') %>
+
+<FORM NAME="cust_merge_popup" ID="cust_merge_popup" ACTION="<% popurl(1) %>cust_main-merge.html" METHOD=POST onSubmit="submit_merge(); return false;">
+
+<SCRIPT TYPE="text/javascript">
+
+var submit_interval_id;
+function submit_merge() {
+ document.getElementById('confirm_merge_cust_button').disabled = 'true';
+ smart_new_custnum_search(document.getElementById('new_custnum_search'));
+ submit_interval_id = setInterval( do_submit_merge, 100);
+}
+
+function do_submit_merge() {
+
+ if ( new_custnum_search_active )
+ return;
+
+ document.getElementById('confirm_merge_cust_button').disabled = '';
+
+ clearInterval(submit_interval_id);
+
+ if ( document.cust_merge_popup.new_custnum.value != '' ) {
+ document.cust_merge_popup.submit();
+ }
+
+}
+
+</SCRIPT>
+
+</SCRIPT>
+
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+
+<TABLE BORDER="0" CELLSPACING="2" STYLE="margin-left:auto; margin-right:auto">
+ <% include('/elements/tr-search-cust_main.html',
+ 'label' => 'Merge into: ',
+ 'field' => 'new_custnum',
+ 'find_button' => 1,
+ 'curr_value' => scalar($cgi->param('new_custnum')),
+ )
+ %>
+</TABLE>
+
+<P ALIGN="CENTER">
+%#have merge button start out disabled and enable after you select a target cust
+<INPUT TYPE="submit" NAME="confirm_merge_cust_button" ID="confirm_merge_cust_button" VALUE="Merge customer">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<INPUT TYPE="BUTTON" VALUE="Don't merge" onClick="parent.cClick();">
+
+</FORM>
+</BODY>
+</HTML>
+
+<%init>
+
+$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
+my $custnum = $1;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied" unless $curuser->access_right('Merge customer');
+
+my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+} );
+die "No customer # $custnum" unless $cust_main;
+
+</%init>
+
diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html
index 33b2bb3..b232deb 100644
--- a/httemplate/misc/order_pkg.html
+++ b/httemplate/misc/order_pkg.html
@@ -9,14 +9,14 @@
function enable_order_pkg () {
if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) {
- document.OrderPkgForm.submit.disabled = false;
+ document.OrderPkgForm.submitButton.disabled = false;
if ( document.OrderPkgForm.pkgpart.options[document.OrderPkgForm.pkgpart.selectedIndex].getAttribute('data-can_discount') == 1 ) {
document.OrderPkgForm.discountnum.disabled = false;
} else {
document.OrderPkgForm.discountnum.disabled = true;
}
} else {
- document.OrderPkgForm.submit.disabled = true;
+ document.OrderPkgForm.submitButton.disabled = true;
document.OrderPkgForm.discountnum.disabled = true;
}
}
@@ -38,34 +38,19 @@
)
%>
-%# false laziness w/edit/quick-charge.html
<TR>
<TH ALIGN="right">Start date </TD>
<TD COLSPAN=6>
- <INPUT TYPE = "text"
- NAME = "start_date"
- SIZE = 32
- ID = "start_date_text"
- VALUE = "<% $start_date %>"
- >
- <IMG SRC = "../images/calendar.png"
- ID = "start_date_button"
- STYLE = "cursor: pointer"
- TITLE = "Select date"
- >
+ <% include('/elements/input-date-field.html',{
+ 'name' => 'start_date',
+ 'format' => $date_format,
+ 'value' => $start_date,
+ 'noinit' => 1,
+ }) %>
<FONT SIZE=-1>(leave blank to start immediately)</FONT>
</TD>
</TR>
-<SCRIPT TYPE="text/javascript">
- Calendar.setup({
- inputField: "start_date_text",
- ifFormat: "<% $date_format %>",
- button: "start_date_button",
- align: "BR"
- });
-</SCRIPT>
-
% if ( $cust_main->payby =~ /^(CARD|CHEK)$/ ) {
% my $what = lc(FS::payby->shortname($cust_main->payby));
<TR>
@@ -99,10 +84,30 @@
)
%>
+<TR>
+ <TH ALIGN="right">Contract end date </TD>
+ <TD COLSPAN=6>
+ <% include('/elements/input-date-field.html',{
+ 'name' => 'contract_end',
+ 'format' => $date_format,
+ 'value' => '',
+ 'noinit' => 1,
+ }) %>
+ </TD>
+</TR>
+
</TABLE>
+<% include( '/elements/standardize_locations.html',
+ 'form' => "OrderPkgForm",
+ 'onlyship' => 1,
+ 'no_company' => 1,
+ 'callback' => 'document.OrderPkgForm.submit();',
+ )
+%>
+
<BR>
-<INPUT NAME="submit" TYPE="submit" VALUE="Order Package" <% $pkgpart ? '' : 'DISABLED' %>>
+<INPUT NAME="submitButton" TYPE="button" VALUE="Order Package" onClick = "this.disabled=true; standardize_locations();" <% $pkgpart ? '' : 'DISABLED' %>>
</FORM>
</BODY>
@@ -128,7 +133,10 @@ my $cust_main = qsearchs({
my $pkgpart = scalar($cgi->param('pkgpart'));
my $format = $date_format. ' %T %z (%Z)'; #false laziness w/REAL_cust_pkg.cgi?
-my $start_date = $cust_main->next_bill_date;
-$start_date = $start_date ? time2str($format, $start_date) : '';
+my $start_date = '';
+if( ! $conf->exists('order_pkg-no_start_date') ) {
+ $start_date = $cust_main->next_bill_date;
+ $start_date = $start_date ? time2str($format, $start_date) : '';
+}
</%init>
diff --git a/httemplate/misc/payment.cgi b/httemplate/misc/payment.cgi
index 813b560..bcab68a 100644
--- a/httemplate/misc/payment.cgi
+++ b/httemplate/misc/payment.cgi
@@ -67,6 +67,11 @@
% }
+<% include('/elements/tr-select-discount_term.html',
+ 'custnum' => $custnum,
+ 'cgi' => $cgi
+ )
+%>
% if ( $payby eq 'CARD' ) {
%
diff --git a/httemplate/misc/process/batch-cust_pay.cgi b/httemplate/misc/process/batch-cust_pay.cgi
index 058a225..e51b9e6 100644
--- a/httemplate/misc/process/batch-cust_pay.cgi
+++ b/httemplate/misc/process/batch-cust_pay.cgi
@@ -10,13 +10,33 @@
% #my $row = 0;
% #while ( exists($param->{"custnum$row"}) ) {
% for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
+% my $custnum = $param->{"custnum$row"};
+% my $cust_main;
+% if ( $custnum =~ /^(\d+)$/ and $1 <= 2147483647 ) {
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'custnum' => $1 },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+% }
+% if ( !$cust_main ) { # not found, try agent_custid
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'agent_custid' => $custnum },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+% }
+% $custnum = $cust_main->custnum if $cust_main;
+% # if !$cust_main, then this will throw an error on batch_insert
+%
% push @cust_pay, new FS::cust_pay {
-% 'custnum' => $param->{"custnum$row"},
-% 'paid' => $param->{"paid$row"},
-% 'payby' => 'BILL',
-% 'payinfo' => $param->{"payinfo$row"},
-% 'paybatch' => $paybatch,
-% }
+% 'custnum' => $custnum,
+% 'paid' => $param->{"paid$row"},
+% 'payby' => 'BILL',
+% 'payinfo' => $param->{"payinfo$row"},
+% 'discount_term' => $param->{"discount_term$row"},
+% 'paybatch' => $paybatch,
+% }
% if $param->{"custnum$row"}
% || $param->{"paid$row"}
% || $param->{"payinfo$row"};
@@ -42,6 +62,6 @@
% } else {
%
%
-<% $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %>
+<% $cgi->redirect(popurl(3). "search/cust_pay.html?magic=paybatch;paybatch=$paybatch") %>
% }
diff --git a/httemplate/misc/process/cust_main-import_charges.cgi b/httemplate/misc/process/cust_main-import_charges.cgi
index 3ca6894..bda3e3b 100644
--- a/httemplate/misc/process/cust_main-import_charges.cgi
+++ b/httemplate/misc/process/cust_main-import_charges.cgi
@@ -16,7 +16,8 @@ my $fh = $cgi->upload('csvfile');
my $error = defined($fh)
? FS::cust_main::batch_charge( {
filehandle => $fh,
- 'fields' => [qw( custnum amount pkg )],
+ 'agentnum' => scalar($cgi->param('agentnum')),
+ 'format' => scalar($cgi->param('format')),
} )
: 'No file';
diff --git a/httemplate/misc/process/cust_main_note-import.cgi b/httemplate/misc/process/cust_main_note-import.cgi
index 6aa8b1d..6625e00 100644
--- a/httemplate/misc/process/cust_main_note-import.cgi
+++ b/httemplate/misc/process/cust_main_note-import.cgi
@@ -26,6 +26,7 @@ The following items <% $op eq 'Preview' ? 'would be' : 'were' %> imported. (See
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Import');
+$FS::cust_main::import=1; # the customer records are already in the database
my $date = time;
my $otaker = $FS::CurrentUser::CurrentUser->username;
my $csv = new Text::CSV_XS;
@@ -38,25 +39,27 @@ my @inserted = ();
my @uninserted = ();
for ( my $row = 0; exists($param->{"custnum$row"}); $row++ ) {
if ( $param->{"custnum$row"} ) {
-# my $cust_main_note = new FS::cust_main_note {
-# 'custnum' => $param->{"custnum$row"},
-# '_date' => $date,
-# 'otaker' => $otaker,
-# 'comments' => $param->{"note$row"},
-# };
-# my $error = '';
-# $error = $cust_main_note->insert unless ($op eq "Preview");
- my $cust_main = qsearchs('cust_main',
- { 'custnum' => $param->{"custnum$row"} }
- );
- my $error;
- if ($cust_main) {
- $cust_main->comments
- ? $cust_main->comments($cust_main->comments. " ". $param->{"note$row"})
- : $cust_main->comments($param->{"note$row"});
- $error = $cust_main->replace;
- }else{
- $error = "Can't find customer " . $param->{"custnum$row"};
+ my $error = '';
+ if ( $param->{use_comments} ) { # why? notes are sexier
+ my $cust_main = qsearchs('cust_main',
+ { 'custnum' => $param->{"custnum$row"} }
+ );
+ if ($cust_main) {
+ $cust_main->comments
+ ? $cust_main->comments($cust_main->comments. " ". $param->{"note$row"})
+ : $cust_main->comments($param->{"note$row"});
+ $error = $cust_main->replace;
+ }else{
+ $error = "Can't find customer " . $param->{"custnum$row"};
+ }
+ } else {
+ my $cust_main_note = new FS::cust_main_note {
+ 'custnum' => $param->{"custnum$row"},
+ '_date' => $date,
+ 'otaker' => $otaker,
+ 'comments' => $param->{"note$row"},
+ };
+ $error = $cust_main_note->insert unless ($op eq "Preview");
}
my $result = { 'custnum' => $param->{"custnum$row"},
'last' => $param->{"last$row"},
diff --git a/httemplate/misc/process/cust_pay-import.cgi b/httemplate/misc/process/cust_pay-import.cgi
index d4ff226..92b6e5a 100644
--- a/httemplate/misc/process/cust_pay-import.cgi
+++ b/httemplate/misc/process/cust_pay-import.cgi
@@ -1,4 +1,4 @@
-<% $cgi->redirect(popurl(3). "search/cust_pay.cgi?magic=paybatch;paybatch=$paybatch") %>
+<% $cgi->redirect(popurl(3). "search/cust_pay.html?magic=paybatch;paybatch=$paybatch") %>
<%init>
my $fh = $cgi->upload('csvfile');
diff --git a/httemplate/misc/process/cust_pkg-import.html b/httemplate/misc/process/cust_pkg-import.html
new file mode 100644
index 0000000..1021817
--- /dev/null
+++ b/httemplate/misc/process/cust_pkg-import.html
@@ -0,0 +1,10 @@
+<% $server->process %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Import');
+
+my $server =
+ new FS::UI::Web::JSRPC 'FS::cust_pkg::Import::process_batch_import', $cgi;
+
+</%init>
diff --git a/httemplate/misc/process/delete-customer.cgi b/httemplate/misc/process/delete-customer.cgi
index d509a5e..1201131 100755
--- a/httemplate/misc/process/delete-customer.cgi
+++ b/httemplate/misc/process/delete-customer.cgi
@@ -28,6 +28,6 @@ if ( $cgi->param('new_custnum') ) {
my $cust_main = qsearchs( 'cust_main', { 'custnum' => $custnum } )
or die "Customer not found: $custnum";
-my $error = $cust_main->delete($new_custnum);
+my $error = $cust_main->delete('new_custnum' => $new_custnum);
</%init>
diff --git a/httemplate/misc/process/email-customers.html b/httemplate/misc/process/email-customers.html
index c54bc6d..de2bb92 100644
--- a/httemplate/misc/process/email-customers.html
+++ b/httemplate/misc/process/email-customers.html
@@ -4,6 +4,6 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices');
-my $server = new FS::UI::Web::JSRPC 'FS::cust_main::process_email_search_result', $cgi;
+my $server = new FS::UI::Web::JSRPC 'FS::cust_main_Mixin::process_email_search_result', $cgi;
</%init>
diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi
index 665001e..c1c9071 100644
--- a/httemplate/misc/process/payment.cgi
+++ b/httemplate/misc/process/payment.cgi
@@ -119,19 +119,26 @@ if ( $payby eq 'CHEK' ) {
die "unknown payby $payby";
}
+$cgi->param('discount_term') =~ /^\d*$/
+ or errorpage("illegal discount_term");
+my $discount_term = $1;
+
my $error = '';
my $paynum = '';
if ( $cgi->param('batch') ) {
- $error = $cust_main->batch_card(
- 'payby' => $payby,
- 'amount' => $amount,
- 'payinfo' => $payinfo,
- 'paydate' => "$year-$month-01",
- 'payname' => $payname,
- map { $_ => $cgi->param($_) }
- @{$payby2fields{$payby}}
- );
+ $error = 'Prepayment discounts not supported with batched payments'
+ if $discount_term;
+
+ $error ||= $cust_main->batch_card(
+ 'payby' => $payby,
+ 'amount' => $amount,
+ 'payinfo' => $payinfo,
+ 'paydate' => "$year-$month-01",
+ 'payname' => $payname,
+ map { $_ => $cgi->param($_) }
+ @{$payby2fields{$payby}}
+ );
errorpage($error) if $error;
} else {
@@ -146,6 +153,7 @@ if ( $cgi->param('batch') ) {
'payunique' => $payunique,
'paycvv' => $paycvv,
'paynum_ref' => \$paynum,
+ 'discount_term' => $discount_term,
map { $_ => $cgi->param($_) } @{$payby2fields{$payby}}
);
errorpage($error) if $error;
diff --git a/httemplate/misc/timeworked.html b/httemplate/misc/timeworked.html
index 46063e8..672fad8 100755
--- a/httemplate/misc/timeworked.html
+++ b/httemplate/misc/timeworked.html
@@ -8,22 +8,11 @@
<THEAD>
<TR>
- <TH>Trans</TH>
<TH COLSPAN="2">Ticket</TH>
- <TH>Time</TH>
+ <TH>Hours</TH>
<TH COLSPAN="2">Customer</TH>
<TH>Multiplier</TH>
</TR>
-
- <TR>
- <TH>#</TH>
- <TH>#</TH>
- <TH>Subject</TH>
- <TH>hours</TH>
- <TH>#</TH>
- <TH>Name</TH>
- <TH></TH>
- </TR>
</THEAD>
<TBODY>
@@ -35,9 +24,9 @@
% my ($custnum, $name) = split(':', pop @customers, 2);
% my $link = $p. 'rt/Ticket/Display.html?id='. $ticketmap{$tr_id}.
% '#txn-'. $tr_id;
+% my $clink = $p. 'view/cust_main.cgi?'. $custnum;
<TR>
- <TD><a href="<% $link %>"><% $tr_id %></a></TD>
<TD><a href="<% $link %>"><% $ticketmap{$tr_id} %></a></TD>
<TD><a href="<% $link %>"><% $ticket{$ticketmap{$tr_id}} |h %></a></TD>
@@ -47,8 +36,8 @@
% }
<TD><% sprintf("%0.2f", $seconds/3600) %></TD>
- <TD ALIGN="right"><% $custnum %></TD>
- <TD ALIGN="right"><% $name %></TD>
+ <TD ALIGN="right"><a href="<% $clink %>"><% $custnum %></a></TD>
+ <TD ALIGN="right"><a href="<% $clink %>"><% $name %></a></TD>
<TD>
<INPUT TYPE="hidden" NAME="transactionid<%$tr_id%>" VALUE="1" >
<INPUT TYPE="hidden" NAME="seconds<%$tr_id%>" VALUE="<% $seconds %>" >
@@ -65,10 +54,11 @@
% foreach ( @customers ) {
% ($custnum, $name) = split(':', $_, 2);
+% $clink = $p. 'view/cust_main.cgi?'. $custnum;
<TR>
- <TD ALIGN="right" COLSPAN="5" ><% $custnum %></TD>
- <TD ALIGN="right"><% $name %></TD>
+ <TD ALIGN="right" COLSPAN="4" ><a href="<% $clink %>"><% $custnum %></a></TD>
+ <TD ALIGN="right"><a href="<% $clink %>"><% $name %></a></TD>
<TD>
% $multiplier = $default_multiplier;
diff --git a/httemplate/misc/unprovision.cgi b/httemplate/misc/unprovision.cgi
index 4ab15fd..6f2c238 100755
--- a/httemplate/misc/unprovision.cgi
+++ b/httemplate/misc/unprovision.cgi
@@ -1,6 +1,8 @@
%if ( $error ) {
% errorpage($error);
-%} else {
+%} elsif ( $pkgnum ) {
+<% $cgi->redirect(popurl(2)."search/cust_pkg_svc.html?svcpart=$svcpart;pkgnum=$pkgnum") %>
+%} else { # $custnum should always exist
<% $cgi->redirect(popurl(2)."view/cust_main.cgi?$custnum") %>
%}
<%init>
@@ -9,18 +11,28 @@ die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Unprovision customer service');
#untaint svcnum
-my($query) = $cgi->keywords;
-$query =~ /^(\d+)$/;
-my $svcnum = $1;
+my @svcnums;
+my ($pkgnum, $svcpart, $custnum);
+if( $cgi->param('svcnum') ) {
+ @svcnums = grep { $_ } map { /^(\d+)$/ && $1 } $cgi->param('svcnum');
+ $pkgnum = $cgi->param('pkgnum');
+ $svcpart = $cgi->param('svcpart');
+ $custnum = $cgi->param('custnum');
+}
+else {
+ @svcnums = map { /^(\d+)$/ && $1 } $cgi->keywords;
+}
-#my $svc_acct = qsearchs('svc_acct',{'svcnum'=>$svcnum});
-#die "Unknown svcnum!" unless $svc_acct;
+my $error = '';
+foreach my $svcnum (@svcnums) {
-my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
-die "Unknown svcnum!" unless $cust_svc;
+ my $cust_svc = qsearchs('cust_svc',{'svcnum'=>$svcnum});
+ die "Unknown svcnum!" unless $cust_svc;
-my $custnum = $cust_svc->cust_pkg->custnum;
+ $custnum ||= $cust_svc->cust_pkg->custnum;
-my $error = $cust_svc->cancel;
+ $error .= $cust_svc->cancel;
+
+}
</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-address_standardize.html b/httemplate/misc/xmlhttp-cust_main-address_standardize.html
index 3b9e142..d0627cd 100644
--- a/httemplate/misc/xmlhttp-cust_main-address_standardize.html
+++ b/httemplate/misc/xmlhttp-cust_main-address_standardize.html
@@ -28,6 +28,7 @@ if ( $sub eq 'address_standardize' ) {
} );
foreach my $pre ( '', 'ship_' ) {
+ next unless ($pre || !$arg{onlyship});
my($zip5, $zip4) = split('-',$arg{$pre.'zip'});
diff --git a/httemplate/misc/xmlhttp-cust_main-censustract.html b/httemplate/misc/xmlhttp-cust_main-censustract.html
index 9d588d7..3ba68af 100644
--- a/httemplate/misc/xmlhttp-cust_main-censustract.html
+++ b/httemplate/misc/xmlhttp-cust_main-censustract.html
@@ -36,23 +36,32 @@ if ( $sub eq 'censustract' ) {
my $content = $res->content;
my $p = new HTML::TokeParser \$content;
my $viewstate;
+ my $eventvalidation;
while (my $token = $p->get_tag('input') ) {
- next unless $token->[1]->{name} eq '__VIEWSTATE';
- $viewstate = $token->[1]->{value};
- last;
+ if ($token->[1]->{name} eq '__VIEWSTATE') {
+ $viewstate = $token->[1]->{value};
+ }
+ if ($token->[1]->{name} eq '__EVENTVALIDATION') {
+ $eventvalidation = $token->[1]->{value};
+ }
+ last if $viewstate && $eventvalidation;
}
- unless ($viewstate) {
+ unless ($viewstate && $eventvalidation ) {
- $error = "no __VIEWSTATE found";
+ $error = "either no __VIEWSTATE or __EVENTVALIDATION found";
} else {
my($zip5, $zip4) = split('-',$arg{zip});
+ #ugh workaround a mess at ffiec
+ $arg{year} = " $arg{year}" unless $arg{year} = "2010";
my @ffiec_args = (
__VIEWSTATE => $viewstate,
+ __EVENTVALIDATION => $eventvalidation,
ddlbYear => $arg{year},
+ ddlbYear => ' 2009',
txtAddress => $arg{address},
txtCity => $arg{city},
ddlbState => $arg{state},
@@ -62,6 +71,7 @@ if ( $sub eq 'censustract' ) {
warn join("\n", @ffiec_args )
if $DEBUG;
+ push @{ $ua->requests_redirectable }, 'POST';
$res = $ua->request( POST( $url, \@ffiec_args ) );
warn $res->as_string
if $DEBUG > 1;
@@ -74,6 +84,7 @@ if ( $sub eq 'censustract' ) {
my @id = qw( MSACode StateCode CountyCode TractCode );
$content = $res->content;
+ warn $res->content if $DEBUG > 1;
$p = new HTML::TokeParser \$content;
my $prefix = 'UcGeoResult11_lb';
my $compare =
diff --git a/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi
new file mode 100644
index 0000000..71e2da5
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_main-discount_terms.cgi
@@ -0,0 +1,24 @@
+% if ( $sub eq 'discount_terms' ) {
+%
+% my $return = [];
+% my $custnum = $cgi->param('arg');
+% my $cust_main = '';
+% $cust_main = qsearchs({
+% 'table' => 'cust_main',
+% 'hashref' => { 'custnum' => $custnum },
+% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+% });
+%
+% if ($cust_main) {
+% $return = [ map [ $_, "$_ months" ], $cust_main->discount_terms ];
+% }
+%
+<% objToJson($return) %>
+% }
+<%init>
+
+my $conf = new FS::Conf;
+
+my $sub = $cgi->param('sub');
+
+</%init>
diff --git a/httemplate/misc/xmlhttp-cust_main-search.cgi b/httemplate/misc/xmlhttp-cust_main-search.cgi
index 26e68b5..481bea2 100644
--- a/httemplate/misc/xmlhttp-cust_main-search.cgi
+++ b/httemplate/misc/xmlhttp-cust_main-search.cgi
@@ -2,10 +2,10 @@
%
% my $custnum = $cgi->param('arg');
% my $cust_main = '';
-% if ( $custnum <= 2147483647 ) {
+% if ( $custnum =~ /^(\d+)$/ and $1 <= 2147483647 ) {
% $cust_main = qsearchs({
% 'table' => 'cust_main',
-% 'hashref' => { 'custnum' => $custnum },
+% 'hashref' => { 'custnum' => $1 },
% 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
% });
% }
@@ -22,7 +22,9 @@
% } elsif ( $sub eq 'smart_search' ) {
%
% my $string = $cgi->param('arg');
-% my @cust_main = smart_search( 'search' => $string );
+% my @cust_main = smart_search( 'search' => $string,
+% 'no_fuzzy_on_exact' => 1, #pref?
+% );
% my $return = [ map [ $_->custnum, $_->name ], @cust_main ];
%
<% objToJson($return) %>
diff --git a/httemplate/search/477.html b/httemplate/search/477.html
index 63eab7a..d586406 100755
--- a/httemplate/search/477.html
+++ b/httemplate/search/477.html
@@ -35,7 +35,7 @@
% next unless ( $part{'IIA'} || $part{'IIB'} );
% }
%
-% if ( $part eq 'VI' ) {
+% if ( $part eq 'VI_census' ) {
% next unless $part{'IA'};
% }
%
@@ -55,12 +55,12 @@
% }
% } else {
% if ( $type eq 'xml' ) {
-<<% 'Part_'. uc($part) %>>
+<<% 'Part_'. $part %>>
% }
% my $url = &{$url_mangler}($part);
<% include( "477part${part}.html", 'url' => $url ) %>
% if ( $type eq 'xml' ) {
-</<% 'Part_'. uc($part) %>>
+</<% 'Part_'. $part %>>
% }
% }
% }
@@ -88,6 +88,6 @@ my $url_mangler = sub {
$url =~ s/477\./477part$part./;
$url;
};
-my @parts = qw( IA IIA IIB IV V VI );
+my @parts = qw( IA IIA IIB IV V VI_census );
</%init>
diff --git a/httemplate/search/477partIA_detail.html b/httemplate/search/477partIA_detail.html
index 546d56c..6fea391 100755
--- a/httemplate/search/477partIA_detail.html
+++ b/httemplate/search/477partIA_detail.html
@@ -9,6 +9,7 @@
'disable_total' => 1,
'header' => [ '', @column_option_name ],
'xml_elements' => [ @xml_elements ],
+ 'xml_omit_empty' => 1,
'fields' => [ @fields ],
)
%>
@@ -60,9 +61,19 @@ my $technology = $FS::Report::FCC_477::technology[$tech_code] || 'unknown';
my $html_init = "<H2>Part IA $technology breakdown by speeds</H2>";
my $xml_prefix = 'PartIA_'. chr(65 + $tech_code);
+if ($cgi->param('_type') eq 'xml') {
+ #rotate data pi/2
+ my @temp = @column_option;
+ @column_option = @row_option;
+ @row_option = @temp;
+}
+
my $query = 'SELECT '. join(' UNION ALL SELECT ',@row_option);
my $count_query = 'SELECT '. scalar(@row_option);
+my $xml_element = 'OOPS, I was never set';
+my $rowchar = 101; # 'e' -- rows are columns! (pi/2)
+
my $value = sub {
my ($rowref, $column) = (shift, shift);
my $row = $rowref->[0];
@@ -71,7 +82,7 @@ my $value = sub {
return $row_option_name{$row} || 'no such report option';
} elsif ( $column =~ /^(\d+)$/ ) {
my @report_option = ( $row || '',
- $column_option[$1 - 2] || '',
+ $column_option[$column] || '',
$technology_option[$tech_code] || '',
);
@@ -81,45 +92,35 @@ my $value = sub {
my $percentage = sprintf('%.2f', $count ? 100 * $residential / $count : 0);
my $return = $count;
- $return .= "<BR>$percentage% residential"
- unless $cgi->param('_type') eq 'xml';
+
+ if ($cgi->param('_type') eq 'xml') {
+ $rowchar++ if $column == 0;
+ $xml_element = $xml_prefix. chr($rowchar). ($column+1);
+ $return = '' if $count == 0 and $cgi->param('_type') eq 'xml';
+ } else {
+ $return .= "<BR>$percentage% residential";
+ }
+
return $return;
} else {
return '<FONT SIZE="+1" COLOR="#ff0000">Bad call to column_value</FONT>';
}
};
-my @fields = (
- sub { &{$value}(shift, 'name');},
- sub { &{$value}(shift, 2);},
- sub { &{$value}(shift, 3);},
- sub { &{$value}(shift, 4);},
- sub { &{$value}(shift, 5);},
- sub { &{$value}(shift, 6);},
- sub { &{$value}(shift, 7);},
- sub { &{$value}(shift, 8);},
- sub { &{$value}(shift, 9);},
- );
+my @fields = map { my $ci = $_; sub { &{$value}(shift, $ci); } }
+ ( 'name', (0 .. $#column_option) );
shift @fields if $cgi->param('_type') eq 'xml';
-my $xml_element = sub {
- my ($rowref, $column) = (shift, shift);
- my $row = $rowref->[0];
-
- $row++;
- $xml_prefix. $column. $row;
-
-};
-
-my @xml_elements = (
- sub { &{$xml_element}(shift, 'f') },
- sub { &{$xml_element}(shift, 'g') },
- sub { &{$xml_element}(shift, 'h') },
- sub { &{$xml_element}(shift, 'i') },
- sub { &{$xml_element}(shift, 'j') },
- sub { &{$xml_element}(shift, 'k') },
- sub { &{$xml_element}(shift, 'l') },
- sub { &{$xml_element}(shift, 'm') },
+my @xml_elements = ( # -- columns are rows! (pi/2)
+ sub { return $xml_element; },
+ sub { return $xml_element; },
+ sub { return $xml_element; },
+ sub { return $xml_element; },
+ sub { return $xml_element; },
+ sub { return $xml_element; },
+ sub { return $xml_element; },
+ sub { return $xml_element; },
+ sub { return $xml_element; },
);
</%init>
diff --git a/httemplate/search/477partIA_summary.html b/httemplate/search/477partIA_summary.html
index 269f2ca..eb1c116 100755
--- a/httemplate/search/477partIA_summary.html
+++ b/httemplate/search/477partIA_summary.html
@@ -26,7 +26,7 @@
sub { '100.00' },
sub { '100.00' },
sub { $total_percentage },
- sub { $total_percentage },
+ sub { $above_200_percentage },
],
)
%>
@@ -54,11 +54,13 @@ my @technology_option = &FS::Report::FCC_477::parse_technology_option($cgi);
my $total_count = 0;
my $total_residential = 0;
+my $above_200 = 0;
my $tech_code = $opt{tech_code};
my $technology = $FS::Report::FCC_477::technology[$tech_code] || 'unknown';
my $html_init = "<H2>Part IA $technology totals</H2>";
my $xml_prefix = 'PartIA_'. chr(65 + $tech_code);
+my $not_first_row = 0; # ugh;
foreach my $row ( @row_option ) {
foreach my $column ( @column_option ) {
@@ -70,11 +72,16 @@ foreach my $row ( @row_option ) {
$total_count += $count;
$total_residential += $residential;
+ $above_200 += $residential if $not_first_row;
}
+ $not_first_row++;
}
my $total_percentage =
sprintf("%.2f", $total_count ? 100*$total_residential/$total_count : 0);
+my $above_200_percentage =
+ sprintf("%.2f", $total_count ? 100*$above_200/$total_count : 0);
+
</%init>
diff --git a/httemplate/search/477partVI.html b/httemplate/search/477partVI_census.html
index db572bc..1d625dc 100755
--- a/httemplate/search/477partVI.html
+++ b/httemplate/search/477partVI_census.html
@@ -1,5 +1,13 @@
<% include( 'elements/search.html',
'html_init' => $html_init,
+ 'html_foot' => sub { if (scalar(keys %state_hash) > 1) {
+ '<BR><B>'.
+ 'WARNING: multiple states found'.
+ '</B><BR>';
+ } else {
+ '';
+ }
+ },
'name' => 'regions',
'query' => [ @sql_query ],
'count_query' => $count_query,
@@ -28,12 +36,15 @@
'percentage',
],
'fields' => [
- sub { my $row = shift; substr($row->censustract, 2, 3) },
+ sub { my $row = shift;
+ $state_hash{substr($row->censustract, 0, 2)} = 1;
+ substr($row->censustract, 2, 3)
+ },
sub { my $row = shift; substr($row->censustract, 5) },
'upload',
'download',
'technology_code',
- sub { '' }, # doesn't really work
+ sub { $cgi->param('_type') eq 'xml' ? '0' : '' }, # doesn't really work
'quantity',
sub { my $row = shift; sprintf "%.2f", $row->residential },
],
@@ -64,6 +75,7 @@ my $html_init = '<H2>Part VI</H2>';
my %search_hash = ();
my @sql_query = ();
+my %state_hash = ();
for ( qw(agentnum magic classnum) ) {
$search_hash{$_} = $cgi->param($_) if $cgi->param($_);
diff --git a/httemplate/search/cdr.html b/httemplate/search/cdr.html
index a557596..5544ff5 100644
--- a/httemplate/search/cdr.html
+++ b/httemplate/search/cdr.html
@@ -55,6 +55,8 @@ die "access denied"
my $edit_data = $FS::CurrentUser::CurrentUser->access_right('Edit rating data');
+my $conf = new FS::Conf;
+
my $areboxes = 0;
my $title = 'Call Detail Records';
@@ -145,17 +147,24 @@ foreach my $param ( grep /^termpart\d+status$/, $cgi->param ) {
}
###
-# src/dest/charged_party
+# src/dest/charged_party/svcnum
###
-if ( $cgi->param('src') =~ /^\s*([\d\-\+\ ]+)\s*$/ ) {
- ( my $src = $1 ) =~ s/\D//g;
+my $phonenum = qr/^\s*([\d\-\+\ ]+)\s*$/;
+my $x = qr/\D/;
+if ( $conf->exists('svc_phone-allow_alpha_phonenum') ) {
+ $phonenum = qr/^\s*([\d\-\+\ A-Za-z]+)\s*$/;
+ $x = qr/[^\dA-Za-z]/;
+}
+
+if ( $cgi->param('src') =~ $phonenum ) {
+ ( my $src = $1 ) =~ s/$x//g;
$hashref->{'src'} = $src;
push @search, "src = '$src'";
}
-if ( $cgi->param('dst') =~ /^\s*([\d\-\+ ]+)\s*$/ ) {
- ( my $dst = $1 ) =~ s/\D//g;
+if ( $cgi->param('dst') =~ $phonenum ) {
+ ( my $dst = $1 ) =~ s/$x//g;
$hashref->{'dst'} = $dst;
push @search, "dst = '$dst'";
}
@@ -166,15 +175,32 @@ if ( $cgi->param('dcontext') =~ /^\s*(.+)\s*$/ ) {
push @search, "dcontext = '$dcontext'";
}
-if ( $cgi->param('charged_party') =~ /^\s*([\d\-\+\ ]+)\s*$/ ) {
- ( my $charged_party = $1 ) =~ s/\D//g;
- #$hashref->{'charged_party'} = $charged_party;
- #push @search, "charged_party = '$charged_party'";
- #XXX countrycode
+if ( $cgi->param('charged_party') ) {
+
+ my @cp = map { $_, "1$_" }
+ split(/\s*,\s*/, $cgi->param('charged_party') );
+
+ my $search = 'charged_party IN ('. join(',', map dbh->quote($_), @cp). ')';
+
+ push @search, $search;
+ push @qsearch, $search;
+}
+
+if ( $cgi->param('charged_party_or_src') ) {
+
+ my @cp = map { $_, "1$_" }
+ split(/\s*,\s*/, $cgi->param('charged_party_or_src') );
+ my $in = join(',', map dbh->quote($_), @cp);
+
+ my $search = "( charged_party IN ($in) OR src IN ($in) )";
- my $search = " ( charged_party = '$charged_party'
- OR charged_party = '1$charged_party' ) ";
+ push @search, $search;
+ push @qsearch, $search;
+}
+if ( $cgi->param('svcnum') =~ /^([\d, ]+)$/ ) {
+ my $svcnum = $1;
+ my $search = "svcnum IN ($svcnum)";
push @search, $search;
push @qsearch, $search;
}
diff --git a/httemplate/search/cust_bill.html b/httemplate/search/cust_bill.html
index 1e9ee8d..cf6ce49 100755
--- a/httemplate/search/cust_bill.html
+++ b/httemplate/search/cust_bill.html
@@ -122,8 +122,16 @@ if ( $cgi->param('invnum') =~ /^\s*(FS-)?(\d+)\s*$/ ) {
$search{'newest_percust'} = 1;
$count_query = "SELECT COUNT(DISTINCT cust_bill.custnum), 'N/A', 'N/A'";
}
-
- my $extra_sql = ' WHERE '. FS::cust_bill->search_sql_where( \%search );
+
+ my $payby_sql = '';
+ $payby_sql = ' AND (' .
+ join(' OR ', map { "cust_main.payby = '$_'" } $cgi->param('payby') ) .
+ ')'
+ if $cgi->param('payby');
+
+ my $extra_sql = ' WHERE '.
+ FS::cust_bill->search_sql_where( \%search ).
+ $payby_sql;
unless ( $count_query ) {
$count_query = 'SELECT COUNT(*), '. join(', ',
diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi
index 98a1da9..f2a5ccd 100644
--- a/httemplate/search/cust_bill_pkg.cgi
+++ b/httemplate/search/cust_bill_pkg.cgi
@@ -502,26 +502,29 @@ if ( $cgi->param('nottax') ) {
} elsif ( $cgi->param('istax') ) {
#false laziness w/report_tax.cgi $taxfromwhere
- if ( $conf->exists('tax-pkg_address') ) {
+ if ( scalar( grep( /locationtaxid/, $cgi->param ) ) ||
+ $cgi->param('iscredit') eq 'rate') {
+
+ $join_pkg .=
+ ' LEFT JOIN cust_bill_pkg_tax_rate_location USING ( billpkgnum ) '.
+ ' LEFT JOIN tax_rate_location USING ( taxratelocationnum ) ';
+
+ } elsif ( $conf->exists('tax-pkg_address') ) {
+
$join_pkg .= ' LEFT JOIN cust_bill_pkg_tax_location USING ( billpkgnum )
LEFT JOIN cust_location USING ( locationnum ) ';
#quelle kludge, somewhat false laziness w/report_tax.cgi
s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g for @where;
- } elsif ( scalar( grep( /locationtaxid/, $cgi->param ) ) ||
- $cgi->param('iscredit') eq 'rate') {
- $join_pkg .=
- ' LEFT JOIN cust_bill_pkg_tax_rate_location USING ( billpkgnum ) '.
- ' LEFT JOIN tax_rate_location USING ( taxratelocationnum ) ';
}
if ( $cgi->param('iscredit') ) {
$join_pkg .= ' JOIN cust_credit_bill_pkg USING ( billpkgnum';
- if ( $conf->exists('tax-pkg_address') ) {
+ if ( $cgi->param('iscredit') eq 'rate' ) {
+ $join_pkg .= ', billpkgtaxratelocationnum )';
+ } elsif ( $conf->exists('tax-pkg_address') ) {
$join_pkg .= ', billpkgtaxlocationnum )';
push @where, "billpkgtaxratelocationnum IS NULL";
- } elsif ( $cgi->param('iscredit') eq 'rate' ) {
- $join_pkg .= ', billpkgtaxratelocationnum )';
} else {
$join_pkg .= ' )';
push @where, "billpkgtaxratelocationnum IS NULL";
diff --git a/httemplate/search/cust_bill_pkg_discount.html b/httemplate/search/cust_bill_pkg_discount.html
index 088b291..b472366 100644
--- a/httemplate/search/cust_bill_pkg_discount.html
+++ b/httemplate/search/cust_bill_pkg_discount.html
@@ -85,6 +85,11 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
push @where, "cust_main.agentnum = $1";
}
+#usernum
+if ( $cgi->param('usernum') =~ /^(\d+)$/ ) {
+ push @where, "cust_pkg_discount.usernum = $1";
+}
+
# #classnum
# # not specified: all classes
# # 0: empty class
@@ -110,18 +115,25 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
my $count_query = "SELECT COUNT(*), SUM(amount)";
-my $join_cust = ' JOIN cust_bill_pkg USING ( billpkgnum )
- JOIN cust_bill USING ( invnum )
- LEFT JOIN cust_main USING ( custnum ) ';
+my $join_cust_pkg_discount =
+ 'LEFT JOIN cust_pkg_discount USING (pkgdiscountnum)';
+
+my $join_cust =
+ ' JOIN cust_bill_pkg USING ( billpkgnum )
+ JOIN cust_bill USING ( invnum )
+ LEFT JOIN cust_main USING ( custnum ) ';
-my $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum )
- LEFT JOIN part_pkg USING ( pkgpart ) ';
- #LEFT JOIN part_pkg AS override
- # ON pkgpart_override = override.pkgpart ';
+my $join_pkg =
+ ' LEFT JOIN cust_pkg ON ( cust_bill_pkg.pkgnum = cust_pkg.pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart ) ';
+ #LEFT JOIN part_pkg AS override
+ # ON pkgpart_override = override.pkgpart ';
my $where = ' WHERE '. join(' AND ', @where);
-$count_query .= " FROM cust_bill_pkg_discount $join_cust $join_pkg $where";
+$count_query .=
+ " FROM cust_bill_pkg_discount $join_cust_pkg_discount $join_cust $join_pkg ".
+ $where;
my @select = (
'cust_bill_pkg_discount.*',
@@ -135,7 +147,7 @@ push @select, 'cust_main.custnum',
my $query = {
'table' => 'cust_bill_pkg_discount',
- 'addl_from' => "$join_cust $join_pkg",
+ 'addl_from' => "$join_cust_pkg_discount $join_cust $join_pkg",
'hashref' => {},
'select' => join(', ', @select ),
'extra_sql' => $where,
diff --git a/httemplate/search/cust_credit.html b/httemplate/search/cust_credit.html
index 9a14dce..a3b22b1 100755
--- a/httemplate/search/cust_credit.html
+++ b/httemplate/search/cust_credit.html
@@ -3,47 +3,14 @@
'name' => 'credits',
'query' => $sql_query,
'count_query' => $count_query,
- 'count_addl' => [ '$%.2f total credited (gross)', ],
+ 'count_addl' => \@count_addl,
#'redirect' => $link,
- 'header' => [ 'Amount',
- 'Date',
- 'By',
- 'Reason',
- FS::UI::Web::cust_header(),
- ],
- 'fields' => [
- #'crednum',
- sub { sprintf('$%.2f', shift->amount ) },
- sub { time2str('%b %d %Y', shift->_date ) },
- 'otaker',
- 'reason',
- \&FS::UI::Web::cust_fields,
- ],
- #'align' => 'rrrllll',
- 'align' => 'rrll'.FS::UI::Web::cust_aligns(),
- 'links' => [
- '',
- '',
- '',
- '',
- ( map { $_ ne 'Cust. Status' ? $clink : '' }
- FS::UI::Web::cust_header()
- ),
- ],
- 'color' => [
- '',
- '',
- '',
- '',
- FS::UI::Web::cust_colors(),
- ],
- 'style' => [
- '',
- '',
- '',
- '',
- FS::UI::Web::cust_styles(),
- ],
+ 'header' => \@header,
+ 'fields' => \@fields,
+ 'align' => $align,
+ 'links' => \@links,
+ 'color' => \@color,
+ 'style' => \@style,
)
%>
<%init>
@@ -51,13 +18,74 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+my $money_char = FS::Conf->new->config('money_char') || '$';
+
my $title = 'Credit Search Results';
#my( $count_query, $sql_query );
+my $unapplied = $cgi->param('unapplied');
+$title = "Unapplied $title" if $unapplied;
+my $clink = sub {
+ my $cust_bill = shift;
+ $cust_bill->cust_main_custnum
+ ? [ "${p}view/cust_main.cgi?", 'custnum' ]
+ : '';
+};
+
+my (@header, @fields, $align, @links, @color, @style);
+$align = '';
+
+#amount
+push @header, 'Amount';
+push @fields, sub { $money_char .sprintf('%.2f', shift->amount) };
+$align .= 'r';
+push @links, '';
+push @color, '';
+push @style, '';
+
+# unapplied amount
+if ($unapplied) {
+ push @header, 'Unapplied';
+ push @fields, sub { $money_char .sprintf('%.2f', shift->unapplied_amount) };
+ $align .= 'r';
+ push @links, '';
+ push @color, '';
+ push @style, '';
+}
+
+push @header, 'Date',
+ 'By',
+ 'Reason',
+ FS::UI::Web::cust_header(),
+ ;
+push @fields, sub { time2str('%b %d %Y', shift->_date ) },
+ 'otaker',
+ 'reason',
+ \&FS::UI::Web::cust_fields,
+ ;
+$align .= 'rll'.FS::UI::Web::cust_aligns(),
+push @links, '',
+ '',
+ '',
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ;
+push @color, '',
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ;
+push @style, '',
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ;
+
my @search = ();
-if ( $cgi->param('otaker') && $cgi->param('otaker') =~ /^([\w\.\-]+)$/ ) {
- push @search, "cust_credit.otaker = '$1'";
+if ( $cgi->param('usernum') =~ /^(\d+)$/ ) {
+ push @search, "cust_credit.usernum = $1";
}
if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
@@ -67,6 +95,10 @@ if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
$title = $agent->agent. " $title";
}
+if ( $unapplied ) {
+ push @search, FS::cust_credit->unapplied_sql . ' > 0';
+}
+
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
push @search, "_date >= $beginning ",
"_date <= $ending";
@@ -76,29 +108,33 @@ push @search, FS::UI::Web::parse_lt_gt($cgi, 'amount' );
#here is the agent virtualization
push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+my @select = (
+ 'cust_credit.*',
+ 'cust_main.custnum as cust_main_custnum',
+ FS::UI::Web::cust_sql_fields(),
+);
+
+if ( $unapplied ) {
+ push @select, '('.FS::cust_credit->unapplied_sql .') AS unapplied_amount';
+ push @search, FS::cust_credit->unapplied_sql .' > 0';
+}
+
my $where = 'WHERE '. join(' AND ', @search);
-my $count_query = 'SELECT COUNT(*), SUM(amount) '.
- 'FROM cust_credit LEFT JOIN cust_main USING ( custnum ) '.
+my $count_query = 'SELECT COUNT(*), SUM(amount) ';
+$count_query .= ', SUM(' . FS::cust_credit->unapplied_sql . ') ' if $unapplied;
+$count_query .= 'FROM cust_credit LEFT JOIN cust_main USING ( custnum ) '.
$where;
+my @count_addl = ( $money_char.'%.2f total credited (gross)' );
+push @count_addl, $money_char.'%.2f unapplied' if $unapplied;
+
my $sql_query = {
'table' => 'cust_credit',
- 'select' => join(', ',
- 'cust_credit.*',
- 'cust_main.custnum as cust_main_custnum',
- FS::UI::Web::cust_sql_fields(),
- ),
+ 'select' => join(', ',@select),
'hashref' => {},
'extra_sql' => $where,
'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
};
- my $clink = sub {
- my $cust_bill = shift;
- $cust_bill->cust_main_custnum
- ? [ "${p}view/cust_main.cgi?", 'custnum' ]
- : '';
- };
-
</%init>
diff --git a/httemplate/search/cust_main.cgi b/httemplate/search/cust_main.cgi
index e65dc71..8fbf636 100755
--- a/httemplate/search/cust_main.cgi
+++ b/httemplate/search/cust_main.cgi
@@ -204,7 +204,9 @@
% if ( $cgi->param('search_cust') ) {
% $sortby = \*company_sort;
% $orderby = "ORDER BY LOWER(company || ' ' || last || ' ' || first )";
-% push @cust_main, smart_search( 'search' => $cgi->param('search_cust') );
+% push @cust_main, smart_search( 'search' => $cgi->param('search_cust'),
+% 'no_fuzzy_on_exact' => 1, #pref?
+% );
% }
%
% @cust_main = grep { $_->ncancelled_pkgs || ! $_->all_pkgs } @cust_main
@@ -270,7 +272,7 @@
% $cgi->param('offset', 0);
% print qq!( <a href="!. $cgi->self_url. qq!">hide!;
% }
-% print ' cancelled customers</a> )';
+% print ' canceled customers</a> )';
% }
%
% if ( $cgi->param('referral_custnum') ) {
@@ -634,7 +636,7 @@
% }
%
% if ( $last_type{'Fuzzy'} || $last_type{'All'} ) {
-% push @cust_main, FS::cust_main->fuzzy_search( { 'last' => $last } );
+% push @cust_main, FS::cust_main::Search->fuzzy_search( { 'last' => $last } );
% }
%
% #if ($last_type{'Sound-alike'}) {
@@ -681,7 +683,7 @@
% }
%
% if ( $company_type{'Fuzzy'} || $company_type{'All'} ) {
-% push @cust_main, FS::cust_main->fuzzy_search( { 'company' => $company } );
+% push @cust_main, FS::cust_main::Search->fuzzy_search( { 'company' => $company } );
% }
%
% if ($company_type{'Sound-alike'}) {
diff --git a/httemplate/search/cust_main.html b/httemplate/search/cust_main.html
index 270fc38..04ecf89 100755
--- a/httemplate/search/cust_main.html
+++ b/httemplate/search/cust_main.html
@@ -44,8 +44,10 @@ my %search_hash = ();
#scalars
my @scalars = qw (
- agentnum status cancelled_pkgs cust_fields flattened_pkgs custbatch usernum
- no_censustract paydate_year paydate_month invoice_terms
+ agentnum status address paydate_year paydate_month invoice_terms
+ no_censustract with_geocode custbatch usernum
+ cancelled_pkgs
+ cust_fields flattened_pkgs
);
for my $param ( @scalars ) {
@@ -54,7 +56,7 @@ for my $param ( @scalars ) {
}
#lists
-for my $param (qw( classnum payby )) {
+for my $param (qw( classnum payby tagnum )) {
$search_hash{$param} = [ $cgi->param($param) ];
}
@@ -84,7 +86,7 @@ $search_hash{'current_balance'} =
# etc
###
-my $sql_query = FS::cust_main->search(\%search_hash);
+my $sql_query = FS::cust_main::Search->search(\%search_hash);
my $count_query = delete($sql_query->{'count_query'});
my @extra_headers = @{ delete($sql_query->{'extra_headers'}) };
my @extra_fields = @{ delete($sql_query->{'extra_fields'}) };
@@ -104,7 +106,7 @@ if ( $FS::CurrentUser::CurrentUser->access_right('Bulk send customer notices') )
my $query = $uri->query;
push @$menubar, 'Email a notice to these customers' =>
- "${p}misc/email-customers.html?$query",
+ "${p}misc/email-customers.html?table=cust_main&$query",
}
diff --git a/httemplate/search/cust_pay.cgi b/httemplate/search/cust_pay.html
index 65bd39e..65bd39e 100755
--- a/httemplate/search/cust_pay.cgi
+++ b/httemplate/search/cust_pay.html
diff --git a/httemplate/search/cust_pay_pending.html b/httemplate/search/cust_pay_pending.html
index f46e08a..8b73508 100755
--- a/httemplate/search/cust_pay_pending.html
+++ b/httemplate/search/cust_pay_pending.html
@@ -19,7 +19,7 @@ my %statusaction = (
'new' => 'delete',
'pending' => 'complete',
#'authorized' => '',
- #'captured' => '',
+ 'captured' => 'capture',
#'declined' => '',
#wouldn't need to take action on a done state#'done'
);
diff --git a/httemplate/search/cust_pkg.cgi b/httemplate/search/cust_pkg.cgi
index 74a3a6d..207e4f6 100755
--- a/httemplate/search/cust_pkg.cgi
+++ b/httemplate/search/cust_pkg.cgi
@@ -19,6 +19,7 @@
'Adjourn',
'Susp.',
'Expire',
+ 'Contract end',
'Cancel',
'Reason',
FS::UI::Web::cust_header(
@@ -59,7 +60,7 @@
#sub { time2str('%b %d %Y', shift->expire); },
#sub { time2str('%b %d %Y', shift->get('cancel')); },
( map { time_or_blank($_) }
- qw( setup last_bill bill adjourn susp expire cancel ) ),
+ qw( setup last_bill bill adjourn susp expire contract_end cancel ) ),
sub { my $self = shift;
my $return = '';
@@ -175,8 +176,9 @@ my %search_hash = ();
#some false laziness w/misc/bulk_change_pkg.cgi
$search_hash{'query'} = $cgi->keywords;
-
-for (qw( agentnum custnum magic status classnum custom cust_fields )) {
+
+#scalars
+for (qw( agentnum custnum magic status classnum custom cust_fields pkgbatch )) {
$search_hash{$_} = $cgi->param($_) if $cgi->param($_);
}
@@ -205,7 +207,7 @@ my %disable = (
'' => {},
);
-foreach my $field (qw( setup last_bill bill adjourn susp expire cancel active )) {
+foreach my $field (qw( setup last_bill bill adjourn susp expire contract_end cancel active )) {
my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi, $field);
@@ -289,6 +291,10 @@ my $html_init = sub {
'height' => 210,
). '<BR>';
}
+ $text .= include( '/elements/email-link.html',
+ 'search_hash' => \%search_hash,
+ 'table' => 'cust_pkg',
+ );
}
return $text;
};
diff --git a/httemplate/search/cust_pkg_discount.html b/httemplate/search/cust_pkg_discount.html
index 233345e..d70c311 100644
--- a/httemplate/search/cust_pkg_discount.html
+++ b/httemplate/search/cust_pkg_discount.html
@@ -78,9 +78,9 @@ if ( $cgi->param('status') eq 'active' ) {
"; #XXX also end date
}
-#otaker
-if ( $cgi->param('otaker') && $cgi->param('otaker') =~ /^([\w\.\-]+)$/ ) {
- push @where, "cust_pkg_discount.otaker = '$1'";
+#usernum
+if ( $cgi->param('usernum') =~ /^(\d+)$/ ) {
+ push @where, "cust_pkg_discount.usernum = $1";
}
#agent
diff --git a/httemplate/search/cust_pkg_summary.cgi b/httemplate/search/cust_pkg_summary.cgi
new file mode 100644
index 0000000..fc71c81
--- /dev/null
+++ b/httemplate/search/cust_pkg_summary.cgi
@@ -0,0 +1,87 @@
+<% include('/elements/header.html', $title) %>
+<% include('/elements/table-grid.html') %>
+ <TR>
+% foreach (@head) {
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% $_ %></TH>
+% }
+ </TR>
+% my $r=0;
+% foreach my $row (@rows) {
+ <TR>
+% foreach (@$row) {
+ <TD CLASS="grid" ALIGN="right" BGCOLOR="<% $r % 2 ? '#ffffff' : '#eeeeee' %>"><% $_ %></TD>
+% }
+ </TR>
+% $r++;
+% }
+ <TR>
+% foreach (@totals) {
+ <TD CLASS="grid" ALIGN="right" BGCOLOR="<% $r % 2 ? '#ffffff' : '#eeeeee' %>"><B><% $_ %></B></TD>
+% }
+ </TR>
+</TABLE>
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List packages');
+
+my $title = 'Package Summary Report';
+my ($begin, $end) = FS::UI::Web::parse_beginning_ending($cgi);
+if($begin > 0) {
+ $title = "$title (".
+ $cgi->param('beginning').' - '.$cgi->param('ending').')';
+}
+
+my @h_sql = FS::h_cust_pkg->sql_h_search($end);
+
+my ($end_sql, $addl_from) = @h_sql[1,3];
+$end_sql =~ s/ORDER BY.*//; # breaks aggregate queries
+
+my $begin_sql = $end_sql;
+$begin_sql =~ s/$end/$begin/g;
+
+my $active_sql = FS::cust_pkg->active_sql;
+my $suspended_sql = FS::cust_pkg->suspended_sql;
+my @conds = (
+ # SQL WHERE clauses for each column of the table.
+ " $begin_sql AND ($active_sql OR $suspended_sql)",
+ '',
+ " $end_sql AND ($active_sql OR $suspended_sql)",
+ " $end_sql AND $active_sql",
+ " $end_sql AND $suspended_sql",
+ );
+
+$_ =~ s/\bcust_pkg/maintable/g foreach @conds;
+
+my @head = ('Package', 'Before Period', 'Sales', 'Total', 'Active', 'Suspended');
+my @rows = ();
+my @totals = ('Total', 0, 0, 0, 0, 0);
+
+if( !$begin ) {
+ splice @conds, 1, 1;
+ splice @head, 1, 1;
+}
+
+foreach my $part_pkg (qsearch('part_pkg', {} )) {
+ my @row = ();
+ next if !$part_pkg->freq; # exclude one-time packages
+ push @row, $part_pkg->pkg;
+ my $i=1;
+ foreach my $cond (@conds) {
+ if($cond) {
+ my $result = qsearchs({
+ 'table' => 'h_cust_pkg',
+ 'hashref' => {},
+ 'select' => 'count(*)',
+ 'addl_from' => $addl_from,
+ 'extra_sql' => 'WHERE pkgpart = '.$part_pkg->pkgpart.$cond,
+ });
+ $row[$i] = $result->getfield('count');
+ $totals[$i] += $row[$i];
+ }
+ $i++;
+ }
+ $row[2] = $row[3]-$row[1];
+ $totals[2] += $row[2];
+ push @rows, \@row;
+}
+</%init>
diff --git a/httemplate/search/cust_pkg_summary.html b/httemplate/search/cust_pkg_summary.html
new file mode 100644
index 0000000..a0ef472
--- /dev/null
+++ b/httemplate/search/cust_pkg_summary.html
@@ -0,0 +1,24 @@
+<% include( '/elements/header.html', 'Package Summary Report' ) %>
+
+<FORM ACTION="cust_pkg_summary.cgi" METHOD="GET">
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH CLASS="background" COLSPAN=2 ALIGN="left">
+ <FONT SIZE="+1">Search options</FONT>
+ </TH>
+ </TR>
+
+ <% include ('/elements/tr-input-beginning_ending.html') %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+</%init>
diff --git a/httemplate/search/cust_pkg_susp.cgi b/httemplate/search/cust_pkg_susp.cgi
new file mode 100644
index 0000000..53631a2
--- /dev/null
+++ b/httemplate/search/cust_pkg_susp.cgi
@@ -0,0 +1,107 @@
+<% include('/elements/header.html', $title) %>
+<% include('/elements/table-grid.html') %>
+ <TR>
+% foreach (@head) {
+ <TH CLASS="grid" BGCOLOR="#cccccc"><% $_ %></TH>
+% }
+ </TR>
+% my $r=0;
+% foreach my $row (@rows) {
+ <TR>
+% foreach (@$row) {
+ <TD CLASS="grid" STYLE="border: 1px solid #cccccc" ALIGN="right" BGCOLOR="<% $r % 2 ? '#ffffff' : '#eeeeee' %>"><% $_ %></TD>
+% }
+ </TR>
+% $r++;
+% }
+ <TR>
+% foreach (@totals) {
+ <TD CLASS="grid" STYLE="border: 1px solid #cccccc" ALIGN="right" BGCOLOR="<% $r % 2 ? '#ffffff' : '#eeeeee' %>"><B><% $_ %></B></TD>
+% }
+ </TR>
+</TABLE>
+<%init>
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List packages');
+
+my $money_char = FS::Conf->new()->config('money_char') || '$';
+
+$FS::Record::DEBUG=0;
+
+my $title = 'Suspension/Unsuspension Report';
+my ($begin, $end) = FS::UI::Web::parse_beginning_ending($cgi);
+if($begin > 0) {
+ $title = "$title (".
+ ($cgi->param('beginning') || 'beginning').' - '.
+ ($cgi->param('ending') || 'present').')';
+}
+
+
+my $begin_sql = $begin ? "AND h2.history_date > $begin" : '';
+my $end_sql = $end ? "AND h2.history_date < $end" : '';
+
+my $h_sql = # self-join FTW!
+"SELECT h1.pkgpart, count(h1.pkgnum) as pkgcount
+ FROM h_cust_pkg AS h1 INNER JOIN h_cust_pkg AS h2 ON (h1.pkgnum = h2.pkgnum)
+ WHERE h1.history_action = 'replace_old' AND h2.history_action = 'replace_new'
+ AND h2.historynum - h1.historynum = 1
+ $begin_sql $end_sql";
+# This assumes replace_old and replace_new records get consecutive
+# numbers. That's true in every case I've seen but is not actually
+# enforced anywhere. If this is a problem we can match them up
+# line by line but that's cumbersome.
+
+my @conds = (
+ '(h1.susp is null OR h1.susp = 0) AND (h2.susp is not null AND h2.susp != 0)',
+ '(h1.susp is not null AND h1.susp != 0) AND (h2.susp is null OR h2.susp = 0)',
+);
+
+my @results;
+foreach my $cond (@conds) {
+ my $sql = "$h_sql AND $cond GROUP BY h1.pkgpart";
+ my $sth = dbh->prepare($sql) or die dbh->errstr;
+ $sth->execute() or die $sth->errstr;
+ push @results, { map { @$_ } @{ $sth->fetchall_arrayref() } };
+}
+
+my @pay_cond;
+push @pay_cond, "cust_bill_pay._date < $end" if $end;
+push @pay_cond, "cust_bill_pay._date > $begin" if $begin;
+
+my $pay_cond = '';
+$pay_cond = 'WHERE '.join(' AND ', @pay_cond) if @pay_cond;
+
+my $pkg_payments = {
+ map { $_->pkgpart => $_->total_pay }
+ qsearch({
+ 'table' => 'cust_pkg',
+ 'select' => 'pkgpart, sum(cust_bill_pay_pkg.amount) AS total_pay',
+ 'addl_from' => 'INNER JOIN cust_bill_pkg USING (pkgnum)
+ INNER JOIN cust_bill_pay_pkg USING (billpkgnum)
+ INNER JOIN cust_bill_pay USING (billpaynum)',
+ 'extra_sql' => $pay_cond . ' GROUP BY pkgpart',
+}) };
+
+my @head = ('Package', 'Suspended', 'Unsuspended', 'Payments');
+my @rows = ();
+my @totals = map {0} @head;
+$totals[0] = 'Total';
+
+foreach my $part_pkg (qsearch('part_pkg', {} )) {
+ my @row = ();
+ next if !$part_pkg->freq; # exclude one-time packages
+ my $pkgpart = $part_pkg->pkgpart;
+ push @row,
+ $part_pkg->pkg,
+ $results[0]->{$pkgpart} || 0,
+ $results[1]->{$pkgpart} || 0,
+ sprintf("%.02f",$pkg_payments->{$pkgpart});
+
+ $totals[$_] += $row[$_] foreach (1..3);
+ $row[3] = $money_char.$row[3];
+
+ push @rows, \@row;
+}
+$totals[3] = $money_char.$totals[3];
+
+</%init>
diff --git a/httemplate/search/cust_pkg_susp.html b/httemplate/search/cust_pkg_susp.html
new file mode 100644
index 0000000..c59e6c1
--- /dev/null
+++ b/httemplate/search/cust_pkg_susp.html
@@ -0,0 +1,24 @@
+<% include( '/elements/header.html', 'Suspension/Reactivation Report' ) %>
+
+<FORM ACTION="cust_pkg_susp.cgi" METHOD="GET">
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH CLASS="background" COLSPAN=2 ALIGN="left">
+ <FONT SIZE="+1">Search options</FONT>
+ </TH>
+ </TR>
+
+ <% include ('/elements/tr-input-beginning_ending.html') %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+</%init>
diff --git a/httemplate/search/cust_pkg_svc.html b/httemplate/search/cust_pkg_svc.html
new file mode 100644
index 0000000..4f27d66
--- /dev/null
+++ b/httemplate/search/cust_pkg_svc.html
@@ -0,0 +1,117 @@
+<% include( 'elements/search.html',
+ 'title' => $part_svc->svc.' services in package #'.$pkgnum,
+ 'name' => 'services',
+ 'html_form' => $html_form,
+ 'query' => $sql_query,
+ 'count_query' => $count_query,
+ 'redirect' => $link,
+ 'header' => [ '#',
+ 'Service',
+ '', #checkboxes
+ ],
+ 'fields' => [ 'svcnum',
+ sub {
+ ($_[0]->label)[1]
+ },
+ sub {
+ $areboxes = 1;
+ '<INPUT TYPE="checkbox" NAME="svcnum" VALUE='.$_[0]->svcnum.'>'
+ },
+ ],
+ 'links' => [ $link,
+ $link,
+ '',
+ ],
+ 'align' => 'rrlc',
+ 'color' => [
+ ('')x4,
+ ],
+ 'style' => [
+ ('')x4,
+ ],
+ 'html_foot' => sub { $areboxes ? $html_foot : '' }
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List services');
+
+my $pkgnum = $cgi->param('pkgnum');
+$pkgnum =~ /^(\d+)$/ or die "invalid pkgnum: $pkgnum";
+my @extra_sql = ( "cust_svc.pkgnum = $pkgnum" );
+
+my $svcpart = $cgi->param('svcpart');
+$svcpart =~ /^(\d+)$/ or die "invalid svcpart: $svcpart";
+push @extra_sql, "cust_svc.svcpart = $svcpart";
+my $part_svc = qsearchs('part_svc', {svcpart => $svcpart});
+my $svcdb = $part_svc->svcdb;
+
+my $orderby = 'ORDER BY svcnum'; #others?
+
+my $addl_from = " LEFT JOIN part_svc USING (svcpart)
+LEFT JOIN cust_pkg USING (pkgnum)
+LEFT JOIN cust_main USING (custnum)
+INNER JOIN $svcdb USING (svcnum)";
+
+my $search_string;
+if ( length( $cgi->param('search_svc') ) ) {
+
+ $search_string = $cgi->param('search_svc');
+ $search_string =~ s/(^\s+|\s+$)//;
+ push @extra_sql, "FS::$svcdb"->search_sql($search_string);
+
+}
+
+#here is the agent virtualization
+push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
+ 'null_right' => 'View/link unlinked services'
+ );
+
+my $extra_sql = ' WHERE '. join(' AND ', @extra_sql );
+
+my $sql_query = {
+ 'select' => join(', ',
+ 'cust_svc.*',
+ 'part_svc.svc',
+ ),
+ 'table' => 'cust_svc',
+ 'addl_from' => $addl_from,
+ 'hashref' => {},
+ 'extra_sql' => "$extra_sql $orderby",
+};
+
+#warn Dumper($sql_query)."\n";
+
+my $count_query = "SELECT COUNT(*) FROM cust_svc $addl_from $extra_sql";
+
+my $link = sub {
+ my $cust_svc = shift;
+ my $url = svc_url(
+ 'm' => $m,
+ 'action' => 'view',
+ 'svcdb' => $svcdb,
+ 'query' => '',
+ );
+ [ $url, 'svcnum' ];
+};
+
+my $html_form = qq!
+<SCRIPT TYPE="text/javascript">
+function areyousure(obj) {
+ return confirm('Permanently delete the selected services?');
+}
+</SCRIPT>
+<FORM METHOD="POST" ACTION="${p}misc/unprovision.cgi" onsubmit="return areyousure()">!;
+
+my $areboxes = 0;
+
+my $html_foot = qq!
+<BR>
+<INPUT TYPE="submit" NAME="submit" VALUE="Unprovision selected">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE=$pkgnum>
+<INPUT TYPE="hidden" NAME="svcpart" VALUE=$svcpart>
+</FORM>!;
+
+
+</%init>
diff --git a/httemplate/search/elements/cust_main_dayranges.html b/httemplate/search/elements/cust_main_dayranges.html
index 9b8b08f..91e039d 100644
--- a/httemplate/search/elements/cust_main_dayranges.html
+++ b/httemplate/search/elements/cust_main_dayranges.html
@@ -145,8 +145,12 @@ unless ( $cgi->param('all_customers') ) {
$days = $1;
}
+ # If this is set, allow cust_main records with nonzero balances
+ my $negative = $cgi->param('negative') || 0;
+
push @where,
- call_range_sub($range_sub, $days, 0, 'offset' => $offset, 'no_as'=>1). ' > 0'; # != 0';
+ call_range_sub($range_sub, $days, 0, 'offset' => $offset, 'no_as'=>1).
+ ($negative ? ' != 0' : ' > 0');
}
if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html
index 4f83d0a..6f4aaf8 100755
--- a/httemplate/search/elements/cust_pay_or_refund.html
+++ b/httemplate/search/elements/cust_pay_or_refund.html
@@ -44,7 +44,7 @@ Examples:
'name_singular' => $name_singular,
'query' => $sql_query,
'count_query' => $count_query,
- 'count_addl' => [ '$%.2f total '.$opt{name_verb}, ],
+ 'count_addl' => \@count_addl,
'redirect_empty' => $opt{'redirect_empty'},
'header' => \@header,
'fields' => \@fields,
@@ -68,7 +68,10 @@ my $table = $opt{'table'} || 'cust_'.$opt{'thing'};
my $amount_field = $opt{'amount_field'};
my $name_singular = $opt{'name_singular'};
-my $title = "\u$name_singular Search Results";
+my $unapplied = $cgi->param('unapplied');
+my $title = '';
+$title = 'Unapplied ' if $unapplied;
+$title .= "\u$name_singular Search Results";
my $link = '';
if ( ( $curuser->access_right('View invoices') #XXX for now
@@ -100,6 +103,36 @@ my $cust_link = sub {
: '';
};
+# only valid for $table == 'cust_pay' atm
+my $tax_names = '';
+if ( $cgi->param('tax_names') ) {
+ if ( dbh->{Driver}->{Name} eq 'Pg' ) {
+
+ $tax_names = "
+ array_to_string(
+ array(
+ SELECT itemdesc
+ FROM cust_bill_pay
+ LEFT JOIN cust_bill_pay_pkg USING ( billpaynum )
+ LEFT JOIN cust_bill_pkg USING ( billpkgnum )
+ WHERE cust_bill_pkg.pkgnum = 0
+ AND cust_bill_pay.paynum = cust_pay.paynum
+ ), '|'
+ ) AS tax_names"
+ ;
+
+ } elsif ( dbh->{Driver}->{Name} =~ /^mysql/i ) {
+
+ $tax_names = "GROUP_CONCAT(itemdesc SEPARATOR '|') AS tax_names";
+
+ } else {
+
+ warn "warning: unknown database type ". dbh->{Driver}->{Name}.
+ "omitting tax name information from report.";
+
+ }
+}
+
my @header = ();
my @fields = ();
my $align = '';
@@ -113,15 +146,25 @@ if ( $opt{'pre_header'} ) {
push @header, "\u$name_singular",
'Amount',
- 'Date',
;
-$align .= 'rrr';
-push @links, '', '', '';
+$align .= 'rr';
+push @links, '', '';
push @fields, 'payby_payinfo_pretty',
sub { sprintf('$%.2f', shift->$amount_field() ) },
- sub { time2str('%b %d %Y', shift->_date ) },
;
+if ( $unapplied ) {
+ push @header, 'Unapplied';
+ $align .= 'r';
+ push @links, '';
+ push @fields, sub { sprintf('$%.2f', shift->unapplied_amount) };
+}
+
+push @header, 'Date';
+$align .= 'r';
+push @links, '';
+push @fields, sub { time2str('%b %d %Y', shift->_date ) };
+
unless ( $opt{'disable_by'} ) {
push @header, 'By';
$align .= 'c';
@@ -133,6 +176,22 @@ unless ( $opt{'disable_by'} ) {
};
}
+if ( $tax_names ) {
+ push @header, ('Tax names', 'Tax province');
+ $align .= 'cc';
+ push @links, ('','');
+ push @fields, sub { join (' + ', map { /^(.*?)(, \w\w)?$/; $1 }
+ split('\|', shift->tax_names)
+ );
+ };
+ push @fields, sub { join (' + ', map { if (/^(?:.*)(?:, )(\w\w)$/){ $1 }
+ else { () }
+ }
+ split('\|', shift->tax_names)
+ );
+ };
+}
+
push @header, FS::UI::Web::cust_header();
$align .= FS::UI::Web::cust_aligns();
push @links, map { $_ ne 'Cust. Status' ? $cust_link : '' }
@@ -146,10 +205,17 @@ push @header, @{ $opt{'addl_header'} }
push @fields, @{ $opt{'addl_fields'} }
if $opt{'addl_fields'};
-my( $count_query, $sql_query );
+my( $count_query, $sql_query, @count_addl );
if ( $cgi->param('magic') ) {
my @search = ();
+ my @select = (
+ "$table.*",
+ FS::UI::Web::cust_sql_fields(),
+ 'cust_main.custnum AS cust_main_custnum',
+ );
+ push @select, $tax_names if $tax_names;
+
my $orderby;
if ( $cgi->param('magic') eq '_date' ) {
@@ -245,8 +311,8 @@ if ( $cgi->param('magic') ) {
push @search, "$table.payinfo = '$1'";
}
- if ( $cgi->param('otaker') =~ /^(\w+)$/ ) {
- push @search, "$table.otaker = '$1'";
+ if ( $cgi->param('usernum') =~ /^(\d+)$/ ) {
+ push @search, "$table.usernum = $1";
}
#for cust_pay_pending... statusNOT=done
@@ -282,6 +348,13 @@ if ( $cgi->param('magic') ) {
die "unknown search magic: ". $cgi->param('magic');
}
+ #unapplied payment/refund
+ if ( $unapplied ) {
+ push @select, '(' . "FS::$table"->unapplied_sql . ') AS unapplied_amount';
+ push @search, "FS::$table"->unapplied_sql . ' > 0';
+
+ }
+
#for the history search
if ( $cgi->param('history_action') =~ /^([\w,]+)$/ ) {
my @history_action = split(/,/, $1);
@@ -300,22 +373,49 @@ if ( $cgi->param('magic') ) {
#here is the agent virtualization
push @search, $curuser->agentnums_sql;
+ my $addl_from = ' LEFT JOIN cust_main USING ( custnum ) ';
+ my $group_by = '';
+
+ if ( $cgi->param('tax_names') ) {
+ if ( dbh->{Driver}->{Name} eq 'Pg' ) {
+
+ 0;#twiddle thumbs
+
+ } elsif ( dbh->{Driver}->{Name} =~ /^mysql/i ) {
+
+ $addl_from .= "LEFT JOIN cust_bill_pay USING ( paynum )
+ LEFT JOIN cust_bill_pay_pkg USING ( billpaynum )
+ LEFT JOIN cust_bill_pkg USING ( billpkgnum ) AS tax_names";
+ $group_by .= "GROUP BY $table.*,cust_main_custnum,".
+ FS::UI::Web::cust_sql_fields();
+ push @search,
+ "( cust_bill_pkg.pkgnum = 0 OR cust_bill_pkg.pkgnum is NULL )";
+
+ } else {
+
+ warn "warning: unknown database type ". dbh->{Driver}->{Name}.
+ "omitting tax name information from report.";
+
+ }
+ }
+
my $search = ' WHERE '. join(' AND ', @search);
- $count_query = "SELECT COUNT(*), SUM($amount_field) ".
- "FROM $table LEFT JOIN cust_main USING ( custnum )".
- $search;
+ $count_query = "SELECT COUNT(*), SUM($amount_field) ";
+ $count_query .= ', SUM(' . "FS::$table"->unapplied_sql . ') '
+ if $unapplied;
+ $count_query .= "FROM $table $addl_from".
+ "$search $group_by";
+
+ @count_addl = ( '$%.2f total '.$opt{name_verb} );
+ push @count_addl, '$%.2f unapplied' if $unapplied;
$sql_query = {
'table' => $table,
- 'select' => join(', ',
- "$table.*",
- 'cust_main.custnum as cust_main_custnum',
- FS::UI::Web::cust_sql_fields(),
- ),
+ 'select' => join(', ', @select),
'hashref' => {},
- 'extra_sql' => "$search ORDER BY $orderby",
- 'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+ 'extra_sql' => "$search $group_by ORDER BY $orderby",
+ 'addl_from' => $addl_from,
};
} else {
@@ -331,6 +431,7 @@ if ( $cgi->param('magic') ) {
$count_query = "SELECT COUNT(*), SUM($amount_field) FROM $table".
" WHERE payinfo = '$payinfo' AND payby = '$payby'".
" AND ". $curuser->agentnums_sql;
+ @count_addl = ( '$%.2f total '.$opt{name_verb} );
$sql_query = {
'table' => $table,
@@ -342,4 +443,7 @@ if ( $cgi->param('magic') ) {
}
+# for consistency
+$title = join('',map {ucfirst} split(/\b/,$title));
+
</%init>
diff --git a/httemplate/search/elements/report_cust_pay_or_refund.html b/httemplate/search/elements/report_cust_pay_or_refund.html
new file mode 100644
index 0000000..9af4e33
--- /dev/null
+++ b/httemplate/search/elements/report_cust_pay_or_refund.html
@@ -0,0 +1,149 @@
+<%doc>
+
+Examples:
+
+ include( 'elements/report_cust_pay_or_refund.html',
+ 'thing' => 'pay',
+ 'name_singular' => 'payment',
+ )
+
+ include( 'elements/report_cust_pay_or_refund.html',
+ 'thing' => 'refund',
+ 'name_singular' => 'refund',
+ )
+
+</%doc>
+<% include('/elements/header.html', $title ) %>
+
+<FORM ACTION="<% $table %>.html" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+<INPUT TYPE="hidden" NAME="unapplied" VALUE="<% $unapplied %>">
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH CLASS="background" COLSPAN=2 ALIGN="left">
+ <FONT SIZE="+1">Search options</FONT>
+ </TH>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right"><% ucfirst(PL($name_singular)) %> of type: </TD>
+ <TD>
+ <SELECT NAME="payby" onChange="payby_changed(this)">
+ <OPTION VALUE="">all</OPTION>
+ <OPTION VALUE="CARD">credit card (all)</OPTION>
+ <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION>
+ <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION>
+ <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION>
+ <OPTION VALUE="CARD-Maestro">credit card (Maestro/Switch/Solo)</OPTION>
+ <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
+ <OPTION VALUE="BILL">check</OPTION>
+ <OPTION VALUE="PREP">prepaid card</OPTION>
+ <OPTION VALUE="CASH">cash</OPTION>
+ <OPTION VALUE="WEST">Western Union</OPTION>
+ <OPTION VALUE="MCRD">manual credit card</OPTION>
+ </SELECT>
+ </TD>
+ </TR>
+
+ <SCRIPT TYPE="text/javascript">
+
+ function payby_changed(what) {
+ if ( what.options[what.selectedIndex].value == 'BILL' ) {
+ document.getElementById('checkno_caption').style.color = '#000000';
+ what.form.payinfo.disabled = false;
+ what.form.payinfo.style.backgroundColor = '#ffffff';
+ } else {
+ document.getElementById('checkno_caption').style.color = '#bbbbbb';
+ what.form.payinfo.disabled = true;
+ what.form.payinfo.style.backgroundColor = '#dddddd';
+ }
+ }
+
+ </SCRIPT>
+
+ <TR>
+ <TD ALIGN="right"><FONT ID="checkno_caption" COLOR="#bbbbbb">Check #: </FONT></TD>
+ <TD>
+ <INPUT TYPE="text" NAME="payinfo" DISABLED STYLE="background-color: #dddddd">
+ </TD>
+ </TR>
+
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar($cgi->param('agentnum')),
+ 'label' => 'for agent: ',
+ 'disable_empty' => 0,
+ )
+ %>
+
+ <% include( '/elements/tr-select-user.html' ) %>
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center">Payment</TD>
+ <TD>
+ <TABLE>
+ <% include( '/elements/tr-input-beginning_ending.html',
+ layout => 'horiz',
+ )
+ %>
+ </TABLE>
+ </TD>
+ </TR>
+
+% if ( $void ) {
+ <TR>
+ <TD ALIGN="right" VALIGN="center">Voided</TD>
+ <TD>
+ <TABLE>
+ <% include( '/elements/tr-input-beginning_ending.html',
+ prefix => 'void',
+ layout => 'horiz',
+ )
+ %>
+ </TABLE>
+ </TD>
+ </TR>
+% }
+
+ <% include( '/elements/tr-input-lessthan_greaterthan.html',
+ 'label' => 'Amount',
+ 'field' => 'paid',
+ )
+ %>
+
+% if ( $table eq 'cust_pay' ) {
+ <% include( '/elements/tr-checkbox.html',
+ 'label' => 'Include tax names',
+ 'field' => 'tax_names',
+ 'value' => 1,
+ )
+ %>
+% }
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+my %opt = @_;
+my $table = 'cust_'.$opt{'thing'};
+my $name_singular = $opt{'name_singular'};
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $void = $cgi->param('void') ? 1 : 0;
+my $unapplied = $cgi->param('unapplied') ? 1 : 0;
+
+my $title = $void ? "Voided $name_singular report" :
+ $unapplied ? "Unapplied $name_singular report" :
+ "\u$name_singular report" ;
+$table .= '_void' if $void;
+
+</%init>
diff --git a/httemplate/search/elements/search-xml.html b/httemplate/search/elements/search-xml.html
index 9f5e9b6..50b1916 100644
--- a/httemplate/search/elements/search-xml.html
+++ b/httemplate/search/elements/search-xml.html
@@ -14,6 +14,7 @@
% } else {
% $value = $row->$field();
% }
+% next unless ($value || !$opt{xml_omit_empty});
%
<% &{$beginfield}($row, $i) %><% $value |h %><% &{$endfield}($row, $i) %>
%
diff --git a/httemplate/search/report_477.html b/httemplate/search/report_477.html
index bc2a958..7ac497a 100755
--- a/httemplate/search/report_477.html
+++ b/httemplate/search/report_477.html
@@ -181,7 +181,7 @@
<% include( '/elements/tr-checkbox.html',
'label' => 'Enable part VI?',
'field' => 'part',
- 'value' => 'VI',
+ 'value' => 'VI_census',
)
%>
diff --git a/httemplate/search/report_cdr.html b/httemplate/search/report_cdr.html
index a50e4db..866606c 100644
--- a/httemplate/search/report_cdr.html
+++ b/httemplate/search/report_cdr.html
@@ -65,7 +65,21 @@
<TR>
<TD ALIGN="right">Charged Party #: </TD>
<TD>
- <INPUT TYPE="text" NAME="charged_party">
+ <INPUT TYPE="text" NAME="charged_party" VALUE="<% join(',', @charged_party) |h %>">
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Charged Party or Source #: </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="charged_party_or_src" VALUE="<% join(',', @charged_party_or_src ) |h %>" >
+ </TD>
+ </TR>
+
+ <TR>
+ <TD ALIGN="right">Freeside service #: </TD>
+ <TD>
+ <INPUT TYPE="text" NAME="svcnum" VALUE="<% join(',', @svcnum ) %>" >
</TD>
</TR>
@@ -145,4 +159,72 @@ my $names_list = [ map {
@fields
];
+my @charged_party = ();
+my @charged_party_or_src = ();
+my @svcnum = ();
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ my $custnum = $1;
+
+ my $cust_main = qsearchs( {
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+ });
+ die "Customer not found!" unless $cust_main;
+
+ #historical?
+ foreach my $cust_pkg ( $cust_main->ncancelled_pkgs ) {
+
+ my @voip_pkgs =
+ grep { $_->plan eq 'voip_cdr' } $cust_pkg->part_pkg->self_and_bill_linked;
+ if ( scalar(@voip_pkgs) > 1 ) {
+ die "multiple voip_cdr packages bundled\n";
+ } elsif ( !@voip_pkgs ) {
+ next;
+ }
+ my $voip_pkg = @voip_pkgs[0];
+
+ my $cdr_svc_method = $voip_pkg->option('cdr_svc_method')
+ || 'svc_phone.phonenum';
+
+ my @cust_svc = $cust_pkg->cust_svc; #historical?
+
+ if ( $cdr_svc_method eq 'svc_phone.phonenum' ) {
+
+ my @svc_phone = map $_->svc_x,
+ grep { $_->part_svc->svcdb eq 'svc_phone' } @cust_svc;
+
+ my @numbers = map {
+ my $number = $_->phonenum;
+ $number = $_->countrycode. $number
+ unless $_->countrycode eq '1';
+ $number;
+ }
+ @svc_phone;
+
+ if ( $voip_pkg->option('disable_src') ) {
+ push @charged_party, @numbers;
+ } else {
+ push @charged_party_or_src, @numbers;
+ }
+
+ } elsif ( $cdr_svc_method eq 'svc_pbx.title' ) {
+ my @svc_pbx = map $_->svc_x,
+ grep { $_->part_svc->svcdb eq 'svc_pbx' } @cust_svc;
+ push @charged_party, map $_->title, @svc_pbx;
+ } elsif ( $cdr_svc_method eq 'svc_pbx.svcnum' ) {
+ my @cust_svc_pbx = grep { $_->part_svc->svcdb eq 'svc_pbx' } @cust_svc;
+ push @svcnum, map $_->svcnum, @cust_svc_pbx;
+ }
+
+ }
+
+ die "No CDR packages for customer $custnum\n"
+ unless @charged_party || @charged_party_or_src || @svcnum;
+
+ #die "Multiple matching metods for customer $custnum\n"
+ # if #there's more than one
+
+}
+
</%init>
diff --git a/httemplate/search/report_cust_bill.html b/httemplate/search/report_cust_bill.html
index 00d566a..b1a252e 100644
--- a/httemplate/search/report_cust_bill.html
+++ b/httemplate/search/report_cust_bill.html
@@ -25,6 +25,13 @@
field => 'owed',
)
%>
+ <% include( '/elements/tr-select-payby.html',
+ label => 'Payment method:',
+ payby_type => 'cust',
+ multiple => 1,
+ all_selected => 1,
+ )
+ %>
<TR>
<TD ALIGN="right"><INPUT TYPE="checkbox" NAME="open" VALUE="1" CHECKED></TD>
diff --git a/httemplate/search/report_cust_bill_pkg_discount.html b/httemplate/search/report_cust_bill_pkg_discount.html
index f1879d4..f9ab901 100644
--- a/httemplate/search/report_cust_bill_pkg_discount.html
+++ b/httemplate/search/report_cust_bill_pkg_discount.html
@@ -5,9 +5,9 @@
<TABLE>
- <% include( '/elements/tr-select-otaker.html',
- 'label' => 'Discounts by employee: ',
- 'otakers' => \@otakers,
+ <% include( '/elements/tr-select-user.html',
+ 'label' => 'Discounts by employee: ',
+ 'access_user' => \%access_user,
)
%>
@@ -39,9 +39,12 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_pkg_discount")
+my $sth = dbh->prepare("SELECT DISTINCT usernum FROM cust_pkg_discount")
or die dbh->errstr;
$sth->execute or die $sth->errstr;
-my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+my @usernum = map $_->[0], @{$sth->fetchall_arrayref};
+my %access_user =
+ map { $_ => qsearchs('access_user',{'usernum'=>$_})->username }
+ @usernum;
</%init>
diff --git a/httemplate/search/report_cust_credit.html b/httemplate/search/report_cust_credit.html
index 9c719b7..16a75eb 100644
--- a/httemplate/search/report_cust_credit.html
+++ b/httemplate/search/report_cust_credit.html
@@ -1,13 +1,14 @@
-<% include('/elements/header.html', 'Credit report' ) %>
+<% include('/elements/header.html', $title ) %>
<FORM ACTION="cust_credit.html" METHOD="GET">
<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
+<INPUT TYPE="hidden" NAME="unapplied" VALUE="<% $unapplied %>">
<TABLE>
- <% include( '/elements/tr-select-otaker.html',
- 'label' => 'Credits by employee: ',
- 'otakers' => \@otakers,
+ <% include( '/elements/tr-select-user.html',
+ 'label' => 'Credits by employee: ',
+ 'access_user' => \%access_user,
)
%>
@@ -40,9 +41,17 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_credit")
+my $sth = dbh->prepare("SELECT DISTINCT usernum FROM cust_credit")
or die dbh->errstr;
$sth->execute or die $sth->errstr;
-my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+my @usernum = map $_->[0], @{$sth->fetchall_arrayref};
+my %access_user =
+ map { $_ => qsearchs('access_user',{'usernum'=>$_})->username }
+ @usernum;
+
+my $unapplied = $cgi->param('unapplied') ? 1 : 0;
+
+my $title = $cgi->param('unapplied') ?
+ 'Unapplied credit report' : 'Credit report';
</%init>
diff --git a/httemplate/search/report_cust_main.html b/httemplate/search/report_cust_main.html
index eb1a662..d6be4fb 100755
--- a/httemplate/search/report_cust_main.html
+++ b/httemplate/search/report_cust_main.html
@@ -28,6 +28,11 @@
)
%>
+ <TR>
+ <TD ALIGN="right" VALIGN="center">Address</TD>
+ <TD><INPUT TYPE="text" NAME="address" SIZE=54></TD>
+ </TR>
+
% foreach my $field (qw( signupdate )) {
<TR>
@@ -45,6 +50,14 @@
% }
+ <% include( '/elements/tr-select-cust_tag.html',
+ 'cgi' => $cgi,
+ 'is_report' => 1,
+ 'multiple' => 1,
+ 'all_selected' => 1,
+ )
+ %>
+
<% include( '/elements/tr-select-payby.html',
'payby_type' => 'cust',
'multiple' => 1,
@@ -105,13 +118,18 @@
<TD><INPUT TYPE="checkbox" NAME="cancelled_pkgs"></TD>
</TR>
-% if ( $conf->exists('cust_main-require_censustract') ) {
-
<TR>
<TD ALIGN="right" VALIGN="center">Without census tract</TD>
<TD><INPUT TYPE="checkbox" NAME="no_censustract"></TD>
</TR>
+% if ( $conf->exists('enable_taxproducts') ) {
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center">With hardcoded tax location</TD>
+ <TD><INPUT TYPE="checkbox" NAME="with_geocode"></TD>
+ </TR>
+
% }
<TR>
diff --git a/httemplate/search/report_cust_pay.html b/httemplate/search/report_cust_pay.html
index 6c10a2e..ea7a215 100644
--- a/httemplate/search/report_cust_pay.html
+++ b/httemplate/search/report_cust_pay.html
@@ -1,116 +1,5 @@
-<% include('/elements/header.html', $title ) %>
-
-<FORM ACTION="<% $void ? 'cust_pay_void.html' : 'cust_pay.cgi' %>" METHOD="GET">
-<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
-
-<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
-
- <TR>
- <TH CLASS="background" COLSPAN=2 ALIGN="left">
- <FONT SIZE="+1">Search options</FONT>
- </TH>
- </TR>
-
- <TR>
- <TD ALIGN="right">Payments of type: </TD>
- <TD>
- <SELECT NAME="payby" onChange="payby_changed(this)">
- <OPTION VALUE="">all</OPTION>
- <OPTION VALUE="CARD">credit card (all)</OPTION>
- <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION>
- <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION>
- <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION>
- <OPTION VALUE="CARD-Maestro">credit card (Maestro/Switch/Solo)</OPTION>
- <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
- <OPTION VALUE="BILL">check</OPTION>
- <OPTION VALUE="PREP">prepaid card</OPTION>
- <OPTION VALUE="CASH">cash</OPTION>
- <OPTION VALUE="WEST">Western Union</OPTION>
- <OPTION VALUE="MCRD">manual credit card</OPTION>
- </SELECT>
- </TD>
- </TR>
-
- <SCRIPT TYPE="text/javascript">
-
- function payby_changed(what) {
- if ( what.options[what.selectedIndex].value == 'BILL' ) {
- document.getElementById('checkno_caption').style.color = '#000000';
- what.form.payinfo.disabled = false;
- what.form.payinfo.style.backgroundColor = '#ffffff';
- } else {
- document.getElementById('checkno_caption').style.color = '#bbbbbb';
- what.form.payinfo.disabled = true;
- what.form.payinfo.style.backgroundColor = '#dddddd';
- }
- }
-
- </SCRIPT>
-
- <TR>
- <TD ALIGN="right"><FONT ID="checkno_caption" COLOR="#bbbbbb">Check #: </FONT></TD>
- <TD>
- <INPUT TYPE="text" NAME="payinfo" DISABLED STYLE="background-color: #dddddd">
- </TD>
- </TR>
-
- <% include( '/elements/tr-select-agent.html',
- 'curr_value' => scalar($cgi->param('agentnum')),
- 'label' => 'for agent: ',
- 'disable_empty' => 0,
- )
- %>
-
- <% include( '/elements/tr-select-otaker.html' ) %>
-
- <TR>
- <TD ALIGN="right" VALIGN="center">Payment</TD>
- <TD>
- <TABLE>
- <% include( '/elements/tr-input-beginning_ending.html',
- layout => 'horiz',
- )
- %>
- </TABLE>
- </TD>
- </TR>
-
-% if ( $void ) {
- <TR>
- <TD ALIGN="right" VALIGN="center">Voided</TD>
- <TD>
- <TABLE>
- <% include( '/elements/tr-input-beginning_ending.html',
- prefix => 'void',
- layout => 'horiz',
- )
- %>
- </TABLE>
- </TD>
- </TR>
-% }
-
- <% include( '/elements/tr-input-lessthan_greaterthan.html',
- 'label' => 'Amount',
- 'field' => 'paid',
- )
- %>
-
-</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');
-
-my $void = $cgi->param('void') ? 1 : 0;
-
-my $title = $void ? 'Voided payment report' : 'Payment report';
-
-</%init>
+<% include( 'elements/report_cust_pay_or_refund.html',
+ 'thing' => 'pay',
+ 'name_singular' => 'payment',
+ )
+%>
diff --git a/httemplate/search/report_cust_pkg.html b/httemplate/search/report_cust_pkg.html
index 58fcf61..289fec4 100755
--- a/httemplate/search/report_cust_pkg.html
+++ b/httemplate/search/report_cust_pkg.html
@@ -89,7 +89,7 @@
% }
-% foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
+% foreach my $field (qw( setup last_bill bill adjourn susp expire contract_end cancel )) {
<TR>
<TD ALIGN="right" VALIGN="center"><% $label{$field} %></TD>
@@ -181,6 +181,7 @@ my %label = (
'adjourn' => 'Adjourns',
'susp' => 'Suspended',
'expire' => 'Expires',
+ 'contract_end' => 'Contract ends',
'cancel' => 'Cancelled',
);
diff --git a/httemplate/search/report_cust_pkg_discount.html b/httemplate/search/report_cust_pkg_discount.html
index 7ebd44f..31774c3 100644
--- a/httemplate/search/report_cust_pkg_discount.html
+++ b/httemplate/search/report_cust_pkg_discount.html
@@ -16,9 +16,9 @@
</TD>
</TR>
- <% include( '/elements/tr-select-otaker.html',
- 'label' => 'Discounts by employee: ',
- 'otakers' => \@otakers,
+ <% include( '/elements/tr-select-user.html',
+ 'label' => 'Discounts by employee: ',
+ 'access_user' => \%access_user,
)
%>
@@ -42,9 +42,12 @@
die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_pkg_discount")
+my $sth = dbh->prepare("SELECT DISTINCT usernum FROM cust_pkg_discount")
or die dbh->errstr;
$sth->execute or die $sth->errstr;
-my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+my @usernum = map $_->[0], @{$sth->fetchall_arrayref};
+my %access_user =
+ map { $_ => qsearchs('access_user',{'usernum'=>$_})->username }
+ @usernum;
</%init>
diff --git a/httemplate/search/report_cust_refund.html b/httemplate/search/report_cust_refund.html
index 4d31100..b886f2e 100644
--- a/httemplate/search/report_cust_refund.html
+++ b/httemplate/search/report_cust_refund.html
@@ -1,116 +1,5 @@
-<% include('/elements/header.html', $title ) %>
-
-<FORM ACTION="<% $void ? 'cust_refund_void.html' : 'cust_refund.html' %>" METHOD="GET">
-<INPUT TYPE="hidden" NAME="magic" VALUE="_date">
-
-<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
-
- <TR>
- <TH CLASS="background" COLSPAN=2 ALIGN="left">
- <FONT SIZE="+1">Search options</FONT>
- </TH>
- </TR>
-
- <TR>
- <TD ALIGN="right">Refunds of type: </TD>
- <TD>
- <SELECT NAME="payby" onChange="payby_changed(this)">
- <OPTION VALUE="">all</OPTION>
- <OPTION VALUE="CARD">credit card (all)</OPTION>
- <OPTION VALUE="CARD-VisaMC">credit card (Visa/MasterCard)</OPTION>
- <OPTION VALUE="CARD-Amex">credit card (American Express)</OPTION>
- <OPTION VALUE="CARD-Discover">credit card (Discover)</OPTION>
- <OPTION VALUE="CARD-Maestro">credit card (Maestro/Switch/Solo)</OPTION>
- <OPTION VALUE="CHEK">electronic check / ACH</OPTION>
- <OPTION VALUE="BILL">check</OPTION>
- <OPTION VALUE="PREP">prepaid card</OPTION>
- <OPTION VALUE="CASH">cash</OPTION>
- <OPTION VALUE="WEST">Western Union</OPTION>
- <OPTION VALUE="MCRD">manual credit card</OPTION>
- </SELECT>
- </TD>
- </TR>
-
- <SCRIPT TYPE="text/javascript">
-
- function payby_changed(what) {
- if ( what.options[what.selectedIndex].value == 'BILL' ) {
- document.getElementById('checkno_caption').style.color = '#000000';
- what.form.payinfo.disabled = false;
- what.form.payinfo.style.backgroundColor = '#ffffff';
- } else {
- document.getElementById('checkno_caption').style.color = '#bbbbbb';
- what.form.payinfo.disabled = true;
- what.form.payinfo.style.backgroundColor = '#dddddd';
- }
- }
-
- </SCRIPT>
-
- <TR>
- <TD ALIGN="right"><FONT ID="checkno_caption" COLOR="#bbbbbb">Check #: </FONT></TD>
- <TD>
- <INPUT TYPE="text" NAME="payinfo" DISABLED STYLE="background-color: #dddddd">
- </TD>
- </TR>
-
- <% include( '/elements/tr-select-agent.html',
- 'curr_value' => scalar($cgi->param('agentnum')),
- 'label' => 'for agent: ',
- 'disable_empty' => 0,
- )
- %>
-
- <% include( '/elements/tr-select-otaker.html' ) %>
-
- <TR>
- <TD ALIGN="right" VALIGN="center">Refund</TD>
- <TD>
- <TABLE>
- <% include( '/elements/tr-input-beginning_ending.html',
- layout => 'horiz',
- )
- %>
- </TABLE>
- </TD>
- </TR>
-
-% if ( $void ) {
- <TR>
- <TD ALIGN="right" VALIGN="center">Voided</TD>
- <TD>
- <TABLE>
- <% include( '/elements/tr-input-beginning_ending.html',
- prefix => 'void',
- layout => 'horiz',
- )
- %>
- </TABLE>
- </TD>
- </TR>
-% }
-
- <% include( '/elements/tr-input-lessthan_greaterthan.html',
- 'label' => 'Amount',
- 'field' => 'paid',
- )
- %>
-
-</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');
-
-my $void = $cgi->param('void') ? 1 : 0;
-
-my $title = $void ? 'Voided refund report' : 'Refund report';
-
-</%init>
+<% include( 'elements/report_cust_pay_or_refund.html',
+ 'thing' => 'refund',
+ 'name_singular' => 'refund',
+ )
+%>
diff --git a/httemplate/search/report_h_cust_pay.html b/httemplate/search/report_h_cust_pay.html
index fe7c4a9..5c7f27a 100644
--- a/httemplate/search/report_h_cust_pay.html
+++ b/httemplate/search/report_h_cust_pay.html
@@ -88,7 +88,7 @@
)
%>
- <% include( '/elements/tr-select-otaker.html' ) %>
+ <% include( '/elements/tr-select-user.html' ) %>
<TR>
<TD ALIGN="right" VALIGN="center">Payment</TD>
diff --git a/httemplate/search/report_receivables.html b/httemplate/search/report_receivables.html
index 912ef26..e85d786 100755
--- a/httemplate/search/report_receivables.html
+++ b/httemplate/search/report_receivables.html
@@ -20,8 +20,23 @@
<TR>
<TD ALIGN="right">Customers</TD>
<TD>
- <INPUT TYPE="radio" NAME="all_customers" VALUE="1" onClick="if (this.checked) { document.OneTrueForm.days.disabled=true; document.OneTrueForm.days.style.backgroundColor = '#dddddd'; } else { document.OneTrueForm.days.disabled=false; document.OneTrueForm.days.style.backgroundColor = '#ffffff'; }">All customers (even those without an outstanding balance)<BR>
- <INPUT TYPE="radio" NAME="all_customers" VALUE="0" CHECKED onClick="if ( ! this.checked ) { document.OneTrueForm.days.disabled=true; document.OneTrueForm.days.style.backgroundColor = '#dddddd'; } else { document.OneTrueForm.days.disabled=false; document.OneTrueForm.days.style.backgroundColor = '#ffffff'; }">Customers with a balance over <INPUT NAME="days" TYPE="text" SIZE=4 MAXLENGTH=3 VALUE="0"> days old
+ <SCRIPT TYPE="text/javascript">
+function toggle(obj) {
+ var f = document.OneTrueForm;
+ var val = (obj.value == obj.checked);
+ f.days.disabled = val;
+ f.negative.disabled = val;
+ f.days.style.backgroundColor = val ? '#dddddd' : '#ffffff';
+}
+ </SCRIPT>
+ <TABLE STYLE="padding: 0px">
+ <TR><TD><INPUT TYPE="radio" NAME="all_customers" VALUE="1" onClick="toggle(this)"></TD>
+ <TD>All customers (even those without an outstanding balance)</TD></TR>
+ <TR><TD><INPUT TYPE="radio" NAME="all_customers" VALUE="0" CHECKED onClick="toggle(this)"></TD>
+ <TD>Customers with a balance over <INPUT NAME="days" TYPE="text" SIZE=4 MAXLENGTH=3 VALUE="0"> days old</TD></TR>
+ <TR><TD></TD>
+ <TD><INPUT TYPE="checkbox" NAME="negative" VALUE="1">&nbsp;Including customers with credit balances</TD></TR>
+ </TABLE>
</TD>
</TR>
<% include( '/elements/tr-input-date-field.html', {
diff --git a/httemplate/search/report_svc_broadband.html b/httemplate/search/report_svc_broadband.html
new file mode 100755
index 0000000..8571ef1
--- /dev/null
+++ b/httemplate/search/report_svc_broadband.html
@@ -0,0 +1,100 @@
+<% include('/elements/header.html', $title ) %>
+
+<FORM ACTION="svc_broadband.cgi" METHOD="GET">
+<INPUT TYPE="hidden" NAME="magic" VALUE="advanced">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+%# extensive false laziness with svc_acct
+ <TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+ <TR>
+ <TH CLASS="background" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Search options</FONT></TH>
+ </TR>
+
+% unless ( $custnum ) {
+ <% include( '/elements/tr-select-agent.html',
+ 'curr_value' => scalar( $cgi->param('agentnum') ),
+ 'disable_empty' => 0,
+ )
+ %>
+
+ <% include( '/elements/tr-select-table.html',
+ 'label' => 'Routers',
+ 'table' => 'router',
+ 'name_col' => 'routername',
+ 'curr_value' => $routernum,
+ 'hashref' => {},
+ 'multiple' => 'multiple',
+ )
+ %>
+% }
+
+ <% include( '/elements/tr-selectmultiple-part_pkg.html',
+ %pkg_search,
+ )
+ %>
+
+ <TR>
+ <TH CLASS="background" COLSPAN=2>&nbsp;</TH>
+ </TR>
+
+ <TR>
+ <TH CLASS="background" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Display options</FONT></TH>
+ </TR>
+
+% #move to /elements/tr-select-cust_pkg-fields if anything else needs it...
+ <TR>
+ <TD ALIGN="right">Package fields</TD>
+ <TD>
+ <SELECT NAME="cust_pkg_fields">
+ <OPTION VALUE="">(none)
+ <OPTION VALUE="setup,last_bill,bill,cancel">Setup date | Last bill date | Next bill date | Cancel date
+ </SELECT>
+ </TD>
+ </TR>
+
+ <% include( '/elements/tr-select-cust-fields.html' ) %>
+
+ </TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('List packages'); #?
+
+my $title = 'Broadband Service Report';
+my $routernum = [ $cgi->param('routernum') || '' ];
+$routernum = join(',', @$routernum);
+
+#false laziness w/report_cust_pkg.html
+my $custnum = '';
+if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $custnum = $1;
+ my $cust_main = qsearchs({
+ 'table' => 'cust_main',
+ 'hashref' => { 'custnum' => $custnum },
+ 'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+ }) or die "unknown custnum $custnum";
+ $title .= ': '. $cust_main->name;
+}
+
+# exclude one-time charges, disabled packages, and packages with no
+# broadband services
+my %pkg_search = (
+ 'extra_sql' => "
+WHERE freq != '0' AND disabled IS NULL AND 0 < (
+ SELECT COUNT(*) FROM part_svc JOIN pkg_svc USING ( svcpart )
+ WHERE pkg_svc.pkgpart = part_pkg.pkgpart AND part_svc.svcdb = 'svc_broadband'
+ AND pkg_svc.quantity > 0
+)",
+);
+
+</%init>
+<%once>
+
+</%once>
diff --git a/httemplate/search/rt_transaction.html b/httemplate/search/rt_transaction.html
index 8dda4ba..fb828af 100644
--- a/httemplate/search/rt_transaction.html
+++ b/httemplate/search/rt_transaction.html
@@ -3,7 +3,7 @@
'name_singular' => 'transaction',
'query' => $query,
'count_query' => $count_query,
- 'count_addl' => [ $format_seconds_sub, $format_seconds_sub, ],
+ 'count_addl' => [ $format_seconds_sub ],#$format_seconds_sub, ],
'header' => [ 'Ticket #',
'Ticket',
'Date',
@@ -54,9 +54,9 @@ my $transactiontime = "
";
my $join = 'JOIN Tickets ON Transactions.ObjectId = Tickets.Id '.
- 'JOIN Users ON Transactions.Creator = Users.Id '.
- 'LEFT JOIN acct_rt_transaction '.
- ' ON Transactions.Id = acct_rt_transaction.transaction_id';
+ 'JOIN Users ON Transactions.Creator = Users.Id '; #.
+# 'LEFT JOIN acct_rt_transaction '.
+# ' ON Transactions.Id = acct_rt_transaction.transaction_id';
my $where = "
WHERE objecttype='RT::Ticket'
AND ( ( Transactions.Type = 'Set'
@@ -90,20 +90,28 @@ if ( $cgi->param('ticketid') =~ /^\s*(\d+)\s*$/ ) {
}
if ( $cgi->param('svcnum') =~ /^\s*(\d+)\s*$/ ) {
- $where .= " AND acct_rt_transaction.svcnum = $1";
+ $where .= " AND EXISTS( SELECT 1 FROM acct_rt_transaction WHERE acct_rt_transaction.transaction_id = Transactions.id AND svcnum = $1 )";
}
my $query = {
- 'select' => "Transactions.*, Tickets.Id AS ticketid, Tickets.Subject, Users.name as otaker, $transactiontime AS transaction_time, acct_rt_transaction.support",
+ 'select' => join(', ',
+ 'Transactions.*',
+ 'Tickets.Id AS ticketid',
+ 'Tickets.Subject',
+ 'Users.name AS otaker',
+ "$transactiontime AS transaction_time",
+ '( SELECT SUM(support) from acct_rt_transaction where transaction_id = Transactions.id ) AS support',
+ ),
+ 'table' => 'transactions', #Pg-ism
#'table' => 'Transactions',
- 'table' => 'transactions',
'addl_from' => $join,
'extra_sql' => $where,
'order by' => 'ORDER BY Created',
};
my $count_query =
- "SELECT COUNT(*), SUM($transactiontime), SUM(acct_rt_transaction.support) FROM Transactions $join $where";
+ #"SELECT COUNT(*), SUM($transactiontime), SUM(acct_rt_transaction.support) FROM Transactions $join $where";
+ "SELECT COUNT(*), SUM($transactiontime) FROM Transactions $join $where";
my $link = [ "${p}rt/Ticket/Display.html?id=", sub { shift->get('ticketid'); } ];
diff --git a/httemplate/search/svc_acct.cgi b/httemplate/search/svc_acct.cgi
index 1407d9e..c3ddd66 100755
--- a/httemplate/search/svc_acct.cgi
+++ b/httemplate/search/svc_acct.cgi
@@ -262,13 +262,13 @@ if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
}
$cgi->param('username') =~ /^([\w\-\.\&]+)$/; #untaint username_text
- my $username = $1;
+ my $username = lc($1);
- push @username_sql, "username ILIKE '$username'"
+ push @username_sql, "LOWER(username) LIKE '$username'"
if $username_type{'Exact'}
|| $username_type{'Fuzzy'};
- push @username_sql, "username ILIKE '\%$username\%'"
+ push @username_sql, "LOWER(username) LIKE '\%$username\%'"
if $username_type{'Substring'}
|| $username_type{'All'};
diff --git a/httemplate/search/svc_broadband.cgi b/httemplate/search/svc_broadband.cgi
index d0b1029..7026f52 100755
--- a/httemplate/search/svc_broadband.cgi
+++ b/httemplate/search/svc_broadband.cgi
@@ -1,8 +1,9 @@
<% include( 'elements/search.html',
'title' => 'Broadband Search Results',
'name' => 'broadband services',
+ 'html_init' => $html_init,
'query' => $sql_query,
- 'count_query' => $count_query,
+ 'count_query' => $sql_query->{'count_query'},
'redirect' => [ popurl(2). "view/svc_broadband.cgi?", 'svcnum' ],
'header' => [ '#',
'Service',
@@ -43,66 +44,29 @@
%>
<%init>
-die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('List services');
+die "access denied" unless
+ $FS::CurrentUser::CurrentUser->access_right('List services');
my $conf = new FS::Conf;
-my $orderby = 'ORDER BY svcnum';
-my %svc_broadband = ();
-my @extra_sql = ();
-if ( $cgi->param('magic') =~ /^(all|unlinked)$/ ) {
-
- push @extra_sql, 'pkgnum IS NULL'
- if $cgi->param('magic') eq 'unlinked';
-
- if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
- my $sortby = $1;
- $orderby = "ORDER BY $sortby";
+my %search_hash;
+if ( $cgi->param('magic') eq 'unlinked' ) {
+ %search_hash = ( 'unlinked' => 1 );
+}
+else {
+ foreach (qw(custnum agentnum svcpart)) {
+ $search_hash{$_} = $cgi->param($_) if $cgi->param($_);
+ }
+ foreach (qw(pkgpart routernum)) {
+ $search_hash{$_} = [ $cgi->param($_) ] if $cgi->param($_);
}
-
-} elsif ( $cgi->param('svcpart') =~ /^(\d+)$/ ) {
- push @extra_sql, "svcpart = $1";
-} elsif ( $cgi->param('ip_addr') =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ ) {
- push @extra_sql, "ip_addr = '$1'";
}
-my $addl_from = ' LEFT JOIN cust_svc USING ( svcnum ) '.
- ' LEFT JOIN part_svc USING ( svcpart ) '.
- ' LEFT JOIN cust_pkg USING ( pkgnum ) '.
- ' LEFT JOIN cust_main USING ( custnum ) ';
-
-push @extra_sql, $FS::CurrentUser::CurrentUser->agentnums_sql(
- 'null_right' => 'View/link unlinked services'
- );
-
-my $extra_sql = '';
-if ( @extra_sql ) {
- $extra_sql = ( keys(%svc_broadband) ? ' AND ' : ' WHERE ' ).
- join(' AND ', @extra_sql );
+if ( $cgi->param('sortby') =~ /^(\w+)$/ ) {
+ $search_hash{'order_by'} = $1;
}
-my $count_query = "SELECT COUNT(*) FROM svc_broadband $addl_from ";
-#if ( keys %svc_broadband ) {
-# $count_query .= ' WHERE '.
-# join(' AND ', map "$_ = ". dbh->quote($svc_broadband{$_}),
-# keys %svc_broadband
-# );
-#}
-$count_query .= $extra_sql;
-
-my $sql_query = {
- 'table' => 'svc_broadband',
- 'hashref' => {}, #\%svc_broadband,
- 'select' => join(', ',
- 'svc_broadband.*',
- 'part_svc.svc',
- 'cust_main.custnum',
- FS::UI::Web::cust_sql_fields(),
- ),
- 'extra_sql' => $extra_sql,
- 'addl_from' => $addl_from,
-};
+my $sql_query = FS::svc_broadband->search(\%search_hash);
my %routerbyblock = ();
foreach my $router (qsearch('router', {})) {
@@ -120,4 +84,9 @@ my $link_router = sub { my $routernum = $routerbyblock{shift->blocknum}->routern
my $link_cust = [ $p.'view/cust_main.cgi?', 'custnum' ];
+my $html_init = include('/elements/email-link.html',
+ 'search_hash' => \%search_hash,
+ 'table' => 'svc_broadband'
+ );
+
</%init>
diff --git a/httemplate/view/cust_bill.cgi b/httemplate/view/cust_bill.cgi
index ce8d96a..0928d04 100755
--- a/httemplate/view/cust_bill.cgi
+++ b/httemplate/view/cust_bill.cgi
@@ -26,7 +26,7 @@
% if ( $cust_bill->owed > 0
% && scalar( grep $payby{$_}, qw(BILL CASH WEST MCRD) )
-% && $curuser->access_right('Post payment')
+% && $curuser->access_right(['Post payment', 'Post check payment', 'Post cash payment'])
% && ! $conf->exists('pkg-balances')
% )
% {
@@ -34,22 +34,22 @@
Post
-% if ( $payby{'BILL'} ) {
+% if ( $payby{'BILL'} && $curuser->access_right(['Post payment', 'Post check payment']) ) {
<% $s++ ? ' | ' : '' %>
<A HREF="<% $p %>edit/cust_pay.cgi?payby=BILL;invnum=<% $invnum %>">check</A>
% }
-% if ( $payby{'CASH'} ) {
+% if ( $payby{'CASH'} && $curuser->access_right(['Post payment', 'Post cash payment']) ) {
<% $s++ ? ' | ' : '' %>
<A HREF="<% $p %>edit/cust_pay.cgi?payby=CASH;invnum=<% $invnum %>">cash</A>
% }
-% if ( $payby{'WEST'} ) {
+% if ( $payby{'WEST'} && $curuser->access_right(['Post payment']) ) {
<% $s++ ? ' | ' : '' %>
<A HREF="<% $p %>edit/cust_pay.cgi?payby=WEST;invnum=<% $invnum %>">Western Union</A>
% }
-% if ( $payby{'MCRD'} ) {
+% if ( $payby{'MCRD'} && $curuser->access_right(['Post payment']) ) {
<% $s++ ? ' | ' : '' %>
<A HREF="<% $p %>edit/cust_pay.cgi?payby=MCRD;invnum=<% $invnum %>">manual credit card</A>
% }
@@ -117,13 +117,14 @@ if ( $query =~ /^((.+)-)?(\d+)$/ ) {
$notice_name = $cgi->param('notice_name');
}
+my $conf = new FS::Conf;
+
my %opt = (
- 'template' => $template,
- 'notice_name' => $notice_name,
+ 'unsquelch_cdr' => $conf->exists('voip-cdr_email'),
+ 'template' => $template,
+ 'notice_name' => $notice_name,
);
-my $conf = new FS::Conf;
-
my @payby = grep /\w/, $conf->config('payby');
#@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH WEST COMP ))
@payby = (qw( CARD DCRD CHEK DCHK LECB BILL CASH COMP ))
diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi
index f6bef43..0f9c1e2 100755
--- a/httemplate/view/cust_main.cgi
+++ b/httemplate/view/cust_main.cgi
@@ -1,5 +1,5 @@
<% include('/elements/header.html', {
- 'title' => "Customer: ". $cust_main->name,
+ 'title' => $title,
'nobr' => 1,
})
%>
@@ -57,12 +57,29 @@ function areyousure(href, message) {
'color' => '#ff0000',
'cust_main' => $cust_main,
'width' => 616, #make room for reasons
+ 'height' => 366,
}
)
%> |
% }
+% if ( $curuser->access_right('Merge customer') ) {
+
+ <% include( '/elements/popup_link-cust_main.html',
+ { 'action' => $p. 'misc/merge_cust.html',
+ 'label' => 'Merge&nbsp;this&nbsp;customer',
+ 'actionlabel' => 'Merge customer',
+ #'color' => '#ff0000',
+ 'cust_main' => $cust_main,
+ 'width' => 480,
+ 'height' => 192,
+ }
+ )
+ %> |
+
+% }
+
% if ( $conf->exists('deletecustomers')
% && $curuser->access_right('Delete customer')
% ) {
@@ -233,6 +250,10 @@ Comments
<% include('cust_main/change_history.html', $cust_main ) %>
% }
+% if ( $view eq 'custom' ) {
+<% include('cust_main/custom.html', $cust_main ) %>
+% }
+
</DIV>
<% include('/elements/footer.html') %>
<%init>
@@ -261,6 +282,11 @@ my $cust_main = qsearchs( {
});
die "Customer not found!" unless $cust_main;
+my $title = $cust_main->name;
+$title = '('. $cust_main->display_custnum. ") $title"
+ if $conf->exists('cust_main-title-display_custnum');
+$title = "Customer: $title";
+
#false laziness w/pref/pref.html and Conf.pm (cust_main-default_view)
tie my %views, 'Tie::IxHash',
'Basics' => 'basics',
@@ -273,6 +299,8 @@ $views{'Payment History'} = 'payment_history'
unless $conf->config('payby-default' eq 'HIDE');
$views{'Change History'} = 'change_history'
if $curuser->access_right('View customer history');
+$views{$conf->config('cust_main-custom_title') || 'Custom'} = 'custom'
+ if $conf->config('cust_main-custom_link');
$views{'Jumbo'} = 'jumbo';
my %viewname = reverse %views;
diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html
index 54c180b..014ddab 100644
--- a/httemplate/view/cust_main/billing.html
+++ b/httemplate/view/cust_main/billing.html
@@ -132,7 +132,7 @@ Billing information
<TR>
<TD ALIGN="right">Attention</TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->payname %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->payname |h %></TD>
</TR>
% } elsif ( $cust_main->payby eq 'COMP' ) {
@@ -206,6 +206,14 @@ Billing information
<% $cust_main->invoice_terms || 'Default ('. ( $conf->config('invoice_default_terms') || 'Payable upon receipt' ). ')' %>
</TD>
</TR>
+<TR>
+ <TD ALIGN="right">Credit&nbsp;limit</TD>
+ <TD BGCOLOR="#ffffff">
+ <% length($cust_main->credit_limit) ?
+ $money_char.sprintf("%.2f", $cust_main->credit_limit) :
+ 'Unlimited' %>
+ </TD>
+</TR>
% if ( $conf->exists('voip-cust_cdr_spools') ) {
<TR>
diff --git a/httemplate/view/cust_main/contacts.html b/httemplate/view/cust_main/contacts.html
index e88c02e..e91af54 100644
--- a/httemplate/view/cust_main/contacts.html
+++ b/httemplate/view/cust_main/contacts.html
@@ -10,7 +10,7 @@
<TR>
<TD ALIGN="right">Contact name</TD>
<TD COLSPAN=5 BGCOLOR="#ffffff">
- <% $cust_main->get("${pre}last"). ', '. $cust_main->get("${pre}first") %>
+ <% $cust_main->get("${pre}last"). ', '. $cust_main->get("${pre}first") |h %>
</TD>
% if ( $which eq '' && $conf->exists('show_ss') ) {
<TD ALIGN="right">SS#</TD>
@@ -19,11 +19,11 @@
</TR>
<TR>
<TD ALIGN="right">Company</TD>
- <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}company") %></TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}company") |h %></TD>
</TR>
<TR>
<TD ALIGN="right">Address</TD>
- <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address1") %></TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address1") |h %></TD>
</TR>
% if ( $cust_main->get("${pre}address2") ) {
@@ -36,20 +36,20 @@
<TR>
<TD ALIGN="right"><% $address2_label %></TD>
- <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address2") %></TD>
+ <TD COLSPAN=7 BGCOLOR="#ffffff"><% $cust_main->get("${pre}address2") |h %></TD>
</TR>
% }
<TR>
<TD ALIGN="right">City</TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}city") %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}city") |h %></TD>
% if ( $cust_main->get("${pre}county") ) {
<TD ALIGN="right">County</TD>
- <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}county") %></TD>
+ <TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}county") |h %></TD>
% }
<TD ALIGN="right">State</TD>
- <TD BGCOLOR="#ffffff"><% state_label( $cust_main->get("${pre}state"), $cust_main->get("${pre}country") ) %></TD>
+ <TD BGCOLOR="#ffffff"><% state_label( $cust_main->get("${pre}state"), $cust_main->get("${pre}country") ) |h %></TD>
<TD ALIGN="right">Zip</TD>
<TD BGCOLOR="#ffffff"><% $cust_main->get("${pre}zip") %></TD>
</TR>
diff --git a/httemplate/view/cust_main/custom.html b/httemplate/view/cust_main/custom.html
new file mode 100644
index 0000000..8e2e07b
--- /dev/null
+++ b/httemplate/view/cust_main/custom.html
@@ -0,0 +1,21 @@
+<IFRAME id="customframe"
+ src="<% $proxyurl %>"
+ onload="resizeFrame(this)"
+ frameborder=0
+ marginheight="0px"
+ marginwidth="0px"
+ width="100%"
+ scrolling="no"
+>
+</IFRAME>
+<SCRIPT TYPE="text/javascript">
+function resizeFrame(f) {
+ f.style.height = f.contentDocument.body.scrollHeight + 'px';
+}
+</SCRIPT>
+<%init>
+
+my( $cust_main ) = @_;
+
+my $proxyurl = $p.'/misc/custom_link_proxy.cgi?custnum='.$cust_main->custnum;
+</%init>
diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html
index 811ac3c..660d0ef 100755
--- a/httemplate/view/cust_main/packages.html
+++ b/httemplate/view/cust_main/packages.html
@@ -57,7 +57,9 @@ Current packages
<TD ALIGN="right">
<A HREF="<%$p%>search/report_cust_pkg.html?custnum=<% $cust_main->custnum %>">Package reports</A><BR>
Service reports:
- <A HREF="<%$p%>search/report_svc_acct.html?custnum=<% $cust_main->custnum %>">accounts</A>
+ <A HREF="<%$p%>search/report_svc_acct.html?custnum=<% $cust_main->custnum %>">accounts</A><BR>
+ Usage reports:
+ <A HREF="<%$p%>search/report_cdr.html?custnum=<% $cust_main->custnum %>">CDRs</A>
</TD>
</TR>
@@ -161,6 +163,7 @@ my %conf_opt = (
'legacy_link' => $conf->exists('legacy_link'),
'svc_broadband-manage_link' => scalar($conf->config('svc_broadband-manage_link')),
'maestro-status_test' => $conf->exists('maestro-status_test'),
+ 'cust_pkg-large_pkg_size' => $conf->config('cust_pkg-large_pkg_size'),
);
#subroutines
diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html
index 3c486dd..3b58f9e 100644
--- a/httemplate/view/cust_main/packages/package.html
+++ b/httemplate/view/cust_main/packages/package.html
@@ -39,6 +39,7 @@
% if ( $curuser->access_right('Discount customer package')
% && $part_pkg->can_discount
% && ! scalar($cust_pkg->cust_pkg_discount_active)
+% && ! scalar($cust_pkg->part_pkg->part_pkg_discount)
% )
% {
% $br=1;
diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html
index 6e30922..512efcc 100644
--- a/httemplate/view/cust_main/packages/services.html
+++ b/httemplate/view/cust_main/packages/services.html
@@ -4,12 +4,40 @@
<TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
<TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
+ <SCRIPT TYPE="text/javascript">
+function clearhint_search_cust_svc(obj, str) {
+ if (obj.value == str) obj.value = '';
+}
+ </SCRIPT>
% #foreach my $svcpart (sort {$a->{svcpart} <=> $b->{svcpart}} @{$pkg->{svcparts}}) {
% foreach my $part_svc ( $cust_pkg->part_svc ) {
-% #foreach my $service (@{$svcpart->{services}}) {
-% foreach my $cust_svc ( @{ $part_svc->cust_pkg_svc } ) {
+% if ( $opt{'cust_pkg-large_pkg_size'} > 0 and
+% $opt{'cust_pkg-large_pkg_size'} <= $cust_pkg->num_svcs ) {
+% # summarize
+ <TR>
+ <TD ALIGN="center" VALIGN="top">
+% my $href="${p}search/cust_pkg_svc.html?svcpart=".$part_svc->svcpart.
+% ";pkgnum=".$cust_pkg->pkgnum;
+ <A HREF="<% $href %>"><% $part_svc->svc %></A>&nbsp;
+ <A HREF="<% $href %>"><B>(view all <% $cust_pkg->num_svcs %>)</B></A>
+% my $hint = $hints{$part_svc->svcdb};
+% if ( $hint ) {
+ <BR>
+ <FORM name="svcpart<%$part_svc->svcpart%>_search" STYLE="display:inline"
+ ACTION="<%$p%>search/cust_pkg_svc.html" METHOD="GET">
+ <INPUT TYPE="hidden" NAME="svcpart" VALUE="<%$part_svc->svcpart%>">
+ <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<%$cust_pkg->pkgnum%>">
+ <INPUT TYPE="text" NAME="search_svc"
+ onfocus="clearhint_search_cust_svc(this, '<%$hint%>')" VALUE="<%$hint%>">
+ <INPUT TYPE="submit" VALUE="Search"></FORM>
+% } #$hint
+ </TD>
+ </TR>
+% }
+% else {
+% foreach my $cust_svc ( @{ $part_svc->cust_pkg_svc } ) {
<TR>
<TD ALIGN="right" VALIGN="top"><% FS::UI::Web::svc_link($m, $part_svc, $cust_svc) %></TD>
@@ -65,7 +93,8 @@
</TD>
</TR>
-% }
+% } #foreach $cust_svc
+% }
% if ( ! $cust_pkg->get('cancel')
% && $curuser->access_right('Provision customer service')
@@ -137,4 +166,13 @@ sub svc_unprovision_link {
qq!', 'Permanently unprovision and delete this service?')">Unprovision</A>!;
}
+my %hints = (
+svc_acct => '(user or email)',
+svc_domain => '(domain)',
+svc_broadband => '(ip or mac)',
+svc_forward => '(email)',
+svc_phone => '(phone)',
+svc_pbx => '(phone)',
+);
+
</%init>
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index a686843..c05cd5a 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -54,8 +54,11 @@
<% pkg_status_row_changed( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
<% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
-% # pkg_status_row($cust_pkg, 'Next bill', 'bill', %opt)
+% if ( $part_pkg->option('suspend_bill') ) {
+ <% pkg_status_row_if( $cust_pkg, 'Next&nbsp;bill', 'bill', %opt, curuser=>$curuser ) %>
+% }
<% pkg_status_row_if( $cust_pkg, 'Expires', 'expire', %opt, curuser=>$curuser ) %>
+ <% pkg_status_row_if( $cust_pkg, 'Contract ends', 'contract_end', %opt ) %>
<TR>
<TD COLSPAN=<%$colspan%>>
@@ -167,6 +170,7 @@
<% pkg_status_row_if($cust_pkg, 'Will automatically suspend by', 'autosuspend', %opt) %>
<% pkg_status_row_if( $cust_pkg, 'Will suspend on', 'adjourn', %opt, curuser=>$curuser ) %>
<% pkg_status_row_if( $cust_pkg, 'Expires', 'expire', %opt, curuser=>$curuser ) %>
+ <% pkg_status_row_if( $cust_pkg, 'Contract ends', 'contract_end', %opt ) %>
% if ( $part_pkg->freq ) {
diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html
index 0dc4c41..046899e 100644
--- a/httemplate/view/cust_main/payment_history.html
+++ b/httemplate/view/cust_main/payment_history.html
@@ -1,7 +1,7 @@
%# payment links
% my $s = 0;
-% if ( $payby{'BILL'} && $curuser->access_right('Post payment') ) {
+% if ( $payby{'BILL'} && $curuser->access_right(['Post payment', 'Post check payment' ]) ) {
<% $s++ ? ' | ' : '' %>
<% include('/elements/popup_link-cust_main.html',
'label' => 'Enter check payment',
@@ -14,7 +14,7 @@
%>
% }
-% if ( $payby{'CASH'} && $curuser->access_right('Post payment') ) {
+% if ( $payby{'CASH'} && $curuser->access_right(['Post payment', 'Post cash payment']) ) {
<% $s++ ? ' | ' : '' %>
<% include('/elements/popup_link-cust_main.html',
'label' => 'Enter cash payment',
@@ -33,7 +33,7 @@
% }
% if ( ( $payby{'CARD'} || $payby{'DCRD'} )
-% && $curuser->access_right('Process payment')
+% && $curuser->access_right(['Process payment', 'Process credit card payment'])
% && ! $cust_main->is_encrypted($cust_main->payinfo)
% ) {
<% $s++ ? ' | ' : '' %>
@@ -41,7 +41,7 @@
% }
% if ( ( $payby{'CHEK'} || $payby{'DCHK'} )
-% && $curuser->access_right('Process payment')
+% && $curuser->access_right(['Process payment', 'Process Echeck payment'])
% && ! $cust_main->is_encrypted($cust_main->payinfo)
% ) {
<% $s++ ? ' | ' : '' %>
@@ -73,7 +73,7 @@
%# refund links
% $s = 0;
-% if ( $payby{'BILL'} && $curuser->access_right('Post refund') ) {
+% if ( $payby{'BILL'} && $curuser->access_right(['Post refund', 'Post check refund']) ) {
<% $s++ ? ' | ' : '' %>
<% include('/elements/popup_link-cust_main.html',
'label' => 'Enter check refund',
@@ -86,7 +86,7 @@
%>
% }
-% if ( $payby{'CASH'} && $curuser->access_right('Post refund') ) {
+% if ( $payby{'CASH'} && $curuser->access_right(['Post refund', 'Post cash refund']) ) {
<% $s++ ? ' | ' : '' %>
<% include('/elements/popup_link-cust_main.html',
'label' => 'Enter cash refund',
@@ -291,7 +291,7 @@
<TR <% $display ? $display.' ID="old_history'.$old_history++.'"' : ''%>>
- <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <TD VALIGN="top" CLASS="grid" BGCOLOR="<% $bgcolor %>">
% unless ( !$target || $target{$target}++ ) {
<A NAME="<% $target %>">
@@ -308,19 +308,19 @@
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
<% $item->{'desc'} %>
</TD>
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
<% $charge %>
</TD>
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
<% $payment %>
</TD>
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
<% $credit %>
</TD>
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
<% $refund %>
</TD>
- <TD ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
+ <TD VALIGN="top" ALIGN="right" CLASS="grid" BGCOLOR="<% $bgcolor %>">
<% $showbalance %>
</TD>
</TR>
@@ -412,6 +412,16 @@ foreach my $cust_pay ($cust_main->cust_pay) {
};
}
+#pending payments
+foreach my $cust_pay_pending ($cust_main->cust_pay_pending) {
+ push @history, {
+ 'date' => $cust_pay_pending->_date,
+ 'desc' => include('payment_history/pending_payment.html', $cust_pay_pending, %opt ),
+ 'void_payment' => $cust_pay_pending->paid,
+ };
+}
+
+
#voided payments
foreach my $cust_pay_void ($cust_main->cust_pay_void) {
push @history, {
@@ -422,6 +432,16 @@ foreach my $cust_pay_void ($cust_main->cust_pay_void) {
}
+#declined payments
+foreach my $cust_pay_pending ($cust_main->cust_pay_pending_attempt) {
+ push @history, {
+ 'date' => $cust_pay_pending->_date,
+ 'desc' => include('payment_history/attempted_payment.html', $cust_pay_pending, %opt ),
+ 'void_payment' => $cust_pay_pending->paid, #??
+ #'target' => $target, #XXX
+ };
+}
+
#credits (some false laziness w/payments)
foreach my $cust_credit ($cust_main->cust_credit) {
push @history, {
diff --git a/httemplate/view/cust_main/payment_history/attempted_payment.html b/httemplate/view/cust_main/payment_history/attempted_payment.html
new file mode 100644
index 0000000..554aa73
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history/attempted_payment.html
@@ -0,0 +1,41 @@
+<I>Payment attempt <% $info |h %></I>
+<%init>
+
+my( $cust_pay_pending, %opt ) = @_;
+
+my $date_format = $opt{'date_format'} || '%m/%d/%Y';
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $payby = $cust_pay_pending->payby;
+
+my $payinfo;
+if ( $payby eq 'CARD' ) {
+ $payinfo = $cust_pay_pending->paymask;
+} elsif ( $payby eq 'CHEK' ) {
+ my( $account, $aba ) = split('@', $cust_pay_pending->paymask );
+ $payinfo = "ABA $aba, Acct #$account";
+} else {
+ $payinfo = $cust_pay_pending->payinfo;
+}
+
+$payby =~ s/^BILL$/Check #/ if $payinfo;
+$payby =~ s/^CHEK$/Electronic check /;
+$payby =~ s/^PREP$/Prepaid card /;
+$payby =~ s/^CARD$/Credit card #/;
+$payby =~ s/^COMP$/Complimentary by /;
+$payby =~ s/^CASH$/Cash/;
+$payby =~ s/^WEST$/Western Union/;
+$payby =~ s/^MCRD$/Manual credit card/;
+$payby =~ s/^BILL$//;
+my $info = $payby ? "($payby$payinfo)" : '';
+
+if ( $opt{'pkg-balances'} && $cust_pay_pending->pkgnum ) {
+ my $cust_pkg = qsearchs('cust_pkg', { 'pkgnum'=>$cust_pay_pending->pkgnum } );
+ $info .= ' for '. $cust_pkg->pkg_label_long;
+}
+
+$info .= ': '. $cust_pay_pending->statustext
+ if length($cust_pay_pending->statustext);
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history/payment.html b/httemplate/view/cust_main/payment_history/payment.html
index 6ec9fdb..e745864 100644
--- a/httemplate/view/cust_main/payment_history/payment.html
+++ b/httemplate/view/cust_main/payment_history/payment.html
@@ -155,11 +155,14 @@ my $view =
my $refund = '';
my $refund_days = $opt{'card_refund-days'} || 120;
+my @rights = ('Refund payment');
+push @rights, 'Refund credit card payment' if $payby eq 'CARD';
+push @rights, 'Refund Echeck payment' if $payby eq 'CHEK';
if ( $cust_pay->closed !~ /^Y/i
&& $cust_pay->payby =~ /^(CARD|CHEK)$/
&& time-$cust_pay->_date < $refund_days*86400
&& $cust_pay->unrefunded > 0
- && $curuser->access_right('Refund payment')
+ && $curuser->access_right(\@rights)
) {
$refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!.
qq!paynum=!. $cust_pay->paynum. '"'.
diff --git a/httemplate/view/cust_main/payment_history/pending_payment.html b/httemplate/view/cust_main/payment_history/pending_payment.html
new file mode 100644
index 0000000..40805b1
--- /dev/null
+++ b/httemplate/view/cust_main/payment_history/pending_payment.html
@@ -0,0 +1,61 @@
+<b><font size="+1" color="#FF0000">Pending payment </font></b> <% "$info $status ($link)" %>
+<%init>
+
+my( $cust_pay_pending, %opt ) = @_;
+
+my $conf = new FS::Conf;
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+my $payby = $cust_pay_pending->payby;
+
+my $payinfo;
+if ( $payby eq 'CARD' ) {
+ $payinfo = $cust_pay_pending->paymask;
+} elsif ( $payby eq 'CHEK' ) {
+ my( $account, $aba ) = split('@', $cust_pay_pending->paymask );
+ $payinfo = "ABA $aba, Acct #$account";
+} else {
+ $payinfo = $cust_pay_pending->payinfo;
+}
+
+my $target = "$payby$payinfo";
+$payby =~ s/^BILL$/Check #/ if $payinfo;
+$payby =~ s/^CHEK$/Electronic check /;
+$payby =~ s/^PREP$/Prepaid card /;
+$payby =~ s/^CARD$/Credit card #/;
+$payby =~ s/^COMP$/Complimentary by /;
+$payby =~ s/^CASH$/Cash/;
+$payby =~ s/^WEST$/Western Union/;
+$payby =~ s/^MCRD$/Manual credit card/;
+$payby =~ s/^BILL$//;
+my $info = $payby ? "($payby$payinfo)" : '';
+
+my %statusaction = (
+ 'new' => 'delete',
+ 'pending' => 'complete',
+ 'captured' => 'capture',
+);
+
+my $edit_pending =
+ $FS::CurrentUser::CurrentUser->access_right('Edit customer pending payments');
+
+my $status = "Status: ".$cust_pay_pending->status;
+
+my $action = $statusaction{$cust_pay_pending->status};
+
+my $link = "";
+if ( $action && $edit_pending ) {
+ $link = include('/elements/popup_link.html',
+ 'action' => $p. 'edit/cust_pay_pending.html'.
+ '?paypendingnum='. $cust_pay_pending->paypendingnum.
+ ";action=$action",
+ 'label' => $action,
+ 'color' => '#ff0000',
+ 'width' => 655,
+ 'height' => ( $action eq 'delete' ? 480 : 575 ),
+ 'actionlabel' => ucfirst($action). ' pending payment',
+ );
+}
+
+</%init>
diff --git a/httemplate/view/cust_main/payment_history/voided_payment.html b/httemplate/view/cust_main/payment_history/voided_payment.html
index be68ff0..5d7f60c 100644
--- a/httemplate/view/cust_main/payment_history/voided_payment.html
+++ b/httemplate/view/cust_main/payment_history/voided_payment.html
@@ -1,6 +1,10 @@
-<DEL>Payment <% $info %></DEL>
+<DEL>Payment <% $info %> by <% $cust_pay_void->otaker %></DEL>
<I>voided <% time2str($date_format, $cust_pay_void->void_date) %>
-by <% $cust_pay_void->otaker %></I><% $unvoid %>
+% my $void_user = $cust_pay_void->void_access_user;
+% if ($void_user) {
+ by <% $void_user->username %></I>
+% }
+<% $unvoid %>
<%init>
my( $cust_pay_void, %opt ) = @_;
diff --git a/httemplate/view/cust_pay.html b/httemplate/view/cust_pay.html
index 2f23d9e..1408b3d 100644
--- a/httemplate/view/cust_pay.html
+++ b/httemplate/view/cust_pay.html
@@ -2,7 +2,10 @@
<% include('/elements/header-popup.html', "$thing Receipt" ) %>
- <CENTER><A HREF="javascript:self.parent.location = '<% $pr_link %>'">Print</A></CENTER><BR>
+ <div align="center">
+ <A HREF="javascript:self.parent.location = '<% $pr_link %>'">Print</A> |
+ <A HREF="javascript:self.location = '<% $email_link %>'">Re-email</A>
+ </div><BR>
% } elsif ( $link eq 'print' ) {
@@ -15,7 +18,12 @@
)
%>
<BR><BR>
-
+% } elsif ( $link eq 'email' ) {
+% if ( $email_error ) {
+ <% include('/elements/header-popup.html', "Error re-emailing receipt: $email_error" ) %>
+% } else {
+ <% include('/elements/header-popup.html', "Re-emailed receipt" ) %>
+% }
% } else {
<% include('/elements/header.html', "$thing Receipt", menubar(
@@ -26,7 +34,7 @@
% }
-% unless ($link eq 'popup' ) {
+% unless ($link =~ /^(popup|email)$/ ) {
<% include('/elements/small_custview.html',
$custnum,
scalar($conf->config('countrydefault')),
@@ -110,9 +118,14 @@
window.print();
</SCRIPT>
-% }
+% } elsif ( $link eq 'email' ) {
-% if ( $link =~ /^(popup|print)$/ ) {
+ <SCRIPT TYPE="text/javascript">
+ window.top.location.reload();
+ </SCRIPT>
+
+% }
+% if ( $link =~ /^(popup|print|email)$/ ) {
</BODY>
</HTML>
% } else {
@@ -149,6 +162,7 @@ my $cust_pay = qsearchs({
die "$thing #$paynum not found!" unless $cust_pay;
my $pr_link = "${p}view/cust_pay.html?link=print;paynum=$paynum;void=$void";
+my $email_link = "${p}view/cust_pay.html?link=email;paynum=$paynum;void=$void";
my $custnum = $cust_pay->custnum;
my $display_custnum = $cust_pay->cust_main->display_custnum;
@@ -159,4 +173,14 @@ my $money_char = $conf->config('money_char') || '$';
tie my %payby, 'Tie::IxHash', FS::payby->payby2longname;
+my $email_error;
+
+if ( $link eq 'email' ) {
+ my $email_error = $cust_pay->send_receipt(
+ 'manual' => 1,
+ );
+
+ warn "can't send payment receipt/statement: $email_error" if $email_error;
+}
+
</%init>
diff --git a/httemplate/view/elements/svc_Common.html b/httemplate/view/elements/svc_Common.html
index 852640e..8a352f3 100644
--- a/httemplate/view/elements/svc_Common.html
+++ b/httemplate/view/elements/svc_Common.html
@@ -21,6 +21,13 @@
)
</%doc>
+<SCRIPT>
+function areyousure(href) {
+ if (confirm("Permanently delete this <% $label %>?") == true)
+ window.location.href = href;
+}
+</SCRIPT>
+
% if ( $custnum ) {
<% include("/elements/header.html","View $label: $value") %>
@@ -36,18 +43,13 @@
"javascript:areyousure(\'${p}misc/cancel-unaudited.cgi?$svcnum\')"
)) %>
- <SCRIPT>
- function areyousure(href) {
- if (confirm("Permanently delete this <% $label %>?") == true)
- window.location.href = href;
- }
- </SCRIPT>
-
% }
Service #<B><% $svcnum %></B>
% my $url = $opt{'edit_url'} || $p. 'edit/'. $opt{'table'}. '.cgi?';
| <A HREF="<%$url%><%$svcnum%>">Edit this <% $label %></A>
+| <A HREF="javascript:areyousure('<%$p.'misc/unprovision.cgi?'.$svcnum%>')">
+Unprovision this Service</A>
<BR>
<% ntable("#cccccc") %><TR><TD><% ntable("#cccccc",2) %>
diff --git a/httemplate/view/image.cgi b/httemplate/view/image.cgi
new file mode 100644
index 0000000..153ec85
--- /dev/null
+++ b/httemplate/view/image.cgi
@@ -0,0 +1,31 @@
+<% $data %>\
+<%init>
+
+#die "access denied"
+# unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $conf = new FS::Conf;
+
+my $type;
+if ( $cgi->param('type') eq 'png' ) {
+ $type = 'png';
+} elsif ( $cgi->param('type') eq 'eps' ) {
+ $type = 'eps';
+} else {
+ die "unknown image type ". $cgi->param('type');
+}
+
+my $data;
+if ( $cgi->param('prefname') =~ /^(\w+)$/ ) {
+
+ my $prefname = $1;
+ my $curuser = $FS::CurrentUser::CurrentUser;
+ $data = decode_base64( $curuser->option("$prefname") );
+
+} else {
+ die "no preview_session specified";
+}
+
+http_header('Content-Type' => 'image/png' );
+
+</%init>
diff --git a/httemplate/view/svc_acct/communigate.html b/httemplate/view/svc_acct/communigate.html
index 0f090fd..179facf 100644
--- a/httemplate/view/svc_acct/communigate.html
+++ b/httemplate/view/svc_acct/communigate.html
@@ -32,6 +32,14 @@
<% include('/view/elements/tr.html', label=>'Add trailer to sent mail',
value=>$svc_acct->cgp_addmailtrailer ? 'YES' : 'NO' ) %>
+% my $archive_after = $svc_acct->cgp_archiveafter;
+% $archive_after =
+% $archive_after
+% ? ( $archive_after / 86400 ). ' days'
+% : ( $archive_after eq '0' ? 'Never' : 'default (730 days)' );
+ <% include('/view/elements/tr.html', label=>'Archive messages after',
+ value=>$archive_after, ) %>
+
%# preferences
<% include('/view/elements/tr.html', label=>'Message delete method',
@@ -54,17 +62,16 @@
value=>$svc_acct->cgp_sendmdnmode ) %>
%# vacation message
-%#XXX finish me... do we need to search for specific rules
-%# (and hide them?) need to see what CGP gives back after we've added a rule
<% include('/elements/init_overlib.html') %>
<TR>
<TD ALIGN="right">Vacation message</TD>
<TD BGCOLOR="#FFFFFF">
+ <% $vacation_rule ? 'Active' : '' %>
<% include('/elements/popup_link.html',
'action' => $p.'edit/cgp_rule-vacation.html?'.
'svcnum='. $svc_acct->svcnum,
- 'label' => '(add)', #XXX (edit)
+ 'label' => $vacation_rule ? '(edit)' : '(add)',
'actionlabel' => 'Vacation message',
'width' => 600,
'height' => 300,
@@ -75,15 +82,15 @@
</TR>
%# redirect all mail
-%#XXX finish me...
<TR>
<TD ALIGN="right">Redirect all mail</TD>
<TD BGCOLOR="#FFFFFF">
+ <% $redirect_rule ? 'Active' : '' %>
<% include('/elements/popup_link.html',
'action' => $p.'edit/cgp_rule-redirect_all.html?'.
'svcnum='. $svc_acct->svcnum,
- 'label' => '(add)', #XXX (edit)
+ 'label' => $redirect_rule ? '(edit)' : '(add)',
'actionlabel' => 'Redirect all mail',
'width' => 763,
#'height'
@@ -100,6 +107,13 @@
)
%>
+%# RPOP
+
+ <% include('/view/elements/tr.html', label=>'Remote POP accounts',
+ value=>$rpop_link,
+ )
+ %>
+
<%init>
my %opt = @_;
@@ -109,7 +123,20 @@ my %opt = @_;
my $svc_acct = $opt{'svc_acct'};
#my $part_svc = $opt{'part_svc'};
-my $rule_link = qq(<A HREF="${p}browse/cgp_rule.html?svcnum=).
+my $rule_link = qq(<A HREF="${p}browse/cgp_rule.html?svcnum=). #"dum vim
$svc_acct->svcnum. '">View/edit mail rules</A>';
+my $rpop_link = qq(<A HREF="${p}browse/acct_snarf.html?svcnum=). #"dee vim
+ $svc_acct->svcnum. '">View/edit remote POP accounts</A>';
+
+my $vacation_rule = qsearchs('cgp_rule', { 'svcnum' => $svc_acct->svcnum,
+ 'name' => '#Vacation'
+ }
+ );
+
+my $redirect_rule = qsearchs('cgp_rule', { 'svcnum' => $svc_acct->svcnum,
+ 'name' => '#Redirect'
+ }
+ );
+
</%init>
diff --git a/httemplate/view/svc_domain/acct_defaults.html b/httemplate/view/svc_domain/acct_defaults.html
index 3a4e187..b561282 100644
--- a/httemplate/view/svc_domain/acct_defaults.html
+++ b/httemplate/view/svc_domain/acct_defaults.html
@@ -71,6 +71,14 @@
)
%>
+% my $archive_after = $svc_domain->acct_def_cgp_archiveafter;
+% $archive_after =
+% $archive_after
+% ? ( $archive_after / 86400 ). ' days'
+% : ( $archive_after eq '0' ? 'Never' : 'default (730 days)' );
+ <% include('/view/elements/tr.html', label=>'Archive messages after',
+ value=>$archive_after, ) %>
+
%# preferences
<% include('/view/elements/tr.html',
diff --git a/httemplate/view/svc_domain/dns.html b/httemplate/view/svc_domain/dns.html
index 88a9bda..184286c 100644
--- a/httemplate/view/svc_domain/dns.html
+++ b/httemplate/view/svc_domain/dns.html
@@ -7,22 +7,30 @@
return confirm("Remove all records and slave from " + document.SlaveForm.recdata.value + "?");
}
</SCRIPT>
+<% include('/elements/init_overlib.html') %>
-DNS records
-% my @records; if ( @records = $svc_domain->domain_record ) {
+<A NAME="dns"></A>
+<div class="fscontainer">
+<div class="fsbox">
+<div class="fsbox-title">
+ <span class="left">DNS Records</span>
+</div>
- <% include('/elements/table-grid.html') %>
+<% include('/elements/table-grid.html') %>
% my $bgcolor1 = '#eeeeee';
-% my $bgcolor2 = '#ffffff';
-% my $bgcolor = $bgcolor2;
+% my $bgcolor2 = '#ffffff';
+% my $bgcolor = $bgcolor2;
<tr>
<th CLASS="grid" BGCOLOR="#cccccc">Zone</th>
<th CLASS="grid" BGCOLOR="#cccccc">Type</th>
<th CLASS="grid" BGCOLOR="#cccccc">Data</th>
+ <th CLASS="grid" BGCOLOR="#cccccc">TTL</th>
+ <th CLASS="grid" BGCOLOR="#cccccc"></th>
</tr>
+% my @records = $svc_domain->domain_record;
% foreach my $domain_record ( @records ) {
% my $type = $domain_record->rectype eq '_mstr'
% ? "(slave)"
@@ -32,13 +40,27 @@ DNS records
<tr>
<td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $domain_record->reczone %></td>
<td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $type %></td>
- <td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $domain_record->recdata %>
+ <td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $domain_record->recdata %></td>
+ <td CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $domain_record->ttl %></td>
+ <td CLASS="grid" BGCOLOR="<% $bgcolor %>">
% unless ( $domain_record->rectype eq 'SOA'
% || ! $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice')
% ) {
+% my $edit_link = include('/elements/popup_link.html',
+% 'label' => 'edit',
+% 'action' => $p.'edit/domain_record.html?recnum='.
+% $domain_record->recnum,
+% 'actionlabel' => 'Edit nameservice record',
+% 'width' => 655,
+% 'height' => 176,
+% #'color' => '#ff0000',
+% );
% ( my $recdata = $domain_record->recdata ) =~ s/"/\\'\\'/g;
- (<A HREF="javascript:areyousure('<%$p%>misc/delete-domain_record.cgi?<%$domain_record->recnum%>', 'Delete \'<% $domain_record->reczone %> <% $type %> <% $recdata %>\' ?' )">delete</A>)
+% my $delete_url= "javascript:areyousure('${p}misc/delete-domain_record.cgi?".
+% $domain_record->recnum. "', 'Delete ".
+% $domain_record->reczone. " $type $recdata ?' )";
+ <%$edit_link%>&nbsp;|&nbsp;<A HREF="<%$delete_url%>">delete</A>
% }
</td>
</tr>
@@ -52,23 +74,51 @@ DNS records
% }
- </table>
-% }
+% if ( ! @records ) {
+
+ <FORM METHOD="POST" NAME="DefaultForm" ACTION="<%$p%>edit/process/svc_domain-defaultrecords.cgi">
+ <tr>
+ <td class="grid" BGCOLOR="#ffffff" COLSPAN=5>
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
+ <INPUT TYPE="submit" VALUE="Add default records">
+ </td>
+ </tr>
+ </FORM>
+
+% }
% if ( $FS::CurrentUser::CurrentUser->access_right('Edit domain nameservice') ) {
<FORM METHOD="POST" ACTION="<%$p%>edit/process/domain_record.cgi">
- <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
- <INPUT TYPE="text" NAME="reczone">
- <INPUT TYPE="hidden" NAME="recaf" VALUE="IN"> IN
- <SELECT NAME="rectype">
-% foreach (qw( A NS CNAME MX PTR TXT) ) {
- <OPTION VALUE="<%$_%>"><%$_%></OPTION>
-% }
- </SELECT>
- <INPUT TYPE="text" NAME="recdata">
- <INPUT TYPE="submit" VALUE="Add record">
+ <INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
+ <tr>
+ <td class="grid" bgcolor="<%$bgcolor%>">
+ <INPUT TYPE="text" NAME="reczone"><BR>
+ <FONT SIZE="-1"><I>Zone</I></FONT>
+ </TD>
+ <TD class="grid" bgcolor="<%$bgcolor%>">
+ <INPUT TYPE="hidden" NAME="recaf" VALUE="IN">
+ <SELECT NAME="rectype">
+% foreach ( @{ FS::domain_record->rectypes } ) {
+ <OPTION VALUE="<%$_%>">IN <%$_%></OPTION>
+% }
+ </SELECT><BR>
+ <FONT SIZE="-1"><I>Type</I></FONT>
+ </TD>
+ <TD class="grid" bgcolor="<%$bgcolor%>">
+ <INPUT TYPE="text" NAME="recdata"><BR>
+ <FONT SIZE="-1"><I>Data</I></FONT>
+ </TD>
+ <TD class="grid" bgcolor="<%$bgcolor%>">
+ <INPUT TYPE="text" NAME="ttl" size="6"><BR>
+ <FONT SIZE="-1"><I>TTL</I></FONT>
+ </TD>
+ <TD class="grid" bgcolor="<%$bgcolor%>" VALIGN="top">
+ <INPUT TYPE="submit" VALUE="Add record">
+ </TD>
+ </TR>
</FORM>
+ <BR>
<FORM NAME="SlaveForm" METHOD="POST" ACTION="<%$p%>edit/process/domain_record.cgi">
<INPUT TYPE="hidden" NAME="svcnum" VALUE="<%$svcnum%>">
Or
@@ -83,9 +133,14 @@ DNS records
<INPUT TYPE="text" NAME="recdata">
<INPUT TYPE="submit" VALUE="Slave domain" onClick="return slave_areyousure()">
</FORM>
+ <BR><BR>
% }
+</table>
+
+</div>
+</div>
<%init>
my($svc_domain, %opt) = @_;
diff --git a/httemplate/view/svc_pbx.cgi b/httemplate/view/svc_pbx.cgi
new file mode 100644
index 0000000..79cafed
--- /dev/null
+++ b/httemplate/view/svc_pbx.cgi
@@ -0,0 +1,72 @@
+<% include('elements/svc_Common.html',
+ 'table' => 'svc_pbx',
+ 'edit_url' => $p."edit/svc_Common.html?svcdb=svc_pbx;svcnum=",
+ 'labels' => \%labels,
+ 'html_foot' => $html_foot,
+ )
+%>
+<%init>
+
+my $fields = FS::svc_pbx->table_info->{'fields'};
+my %labels = map { $_ => ( ref($fields->{$_})
+ ? $fields->{$_}{'label'}
+ : $fields->{$_}
+ );
+ }
+ keys %$fields;
+
+my $html_foot = sub {
+ my $svc_pbx = shift;
+
+ ##
+ # CDR links
+ ##
+
+ tie my %what, 'Tie::IxHash',
+ 'pending' => 'NULL',
+ 'billed' => 'done',
+ ;
+
+ #matching as per package def cdr_svc_method
+ my $cust_pkg = $svc_pbx->cust_svc->cust_pkg;
+ return '' unless $cust_pkg;
+
+ my @voip_pkgs =
+ grep { $_->plan eq 'voip_cdr' } $cust_pkg->part_pkg->self_and_bill_linked;
+ if ( scalar(@voip_pkgs) > 1 ) {
+ warn "multiple voip_cdr packages bundled\n";
+ return '';
+ } elsif ( !@voip_pkgs ) {
+ warn "no voip_cdr packages\n";
+ }
+ my $voip_pkg = @voip_pkgs[0];
+
+ my $cdr_svc_method = $voip_pkg->option('cdr_svc_method')
+ || 'svc_phone.phonenum';
+ return '' unless $cdr_svc_method =~ /^svc_pbx\.(\w+)$/;
+ my $field = $1;
+
+ my $search;
+ if ( $field eq 'title' ) {
+ $search = 'charged_party='. uri_escape($svc_pbx->title);
+ } elsif ( $field eq 'svcnum' ) {
+ $search = 'svcnum='. $svc_pbx->svcnum;
+ } else {
+ warn "unknown cdr_svc_method svc_pbx.$field";
+ return '';
+ }
+
+ my @links = map {
+ qq(<A HREF="${p}search/cdr.html?cdrbatchnum=__ALL__;$search;freesidestatus=$what{$_}">).
+ "View $_ CDRs</A>";
+ } keys(%what);
+
+ ###
+ # concatenate & return
+ ###
+
+ join(' | ', @links ). '<BR>';
+
+};
+
+</%init>
diff --git a/httemplate/view/svc_phone.cgi b/httemplate/view/svc_phone.cgi
index 75591c7..27d270c 100644
--- a/httemplate/view/svc_phone.cgi
+++ b/httemplate/view/svc_phone.cgi
@@ -120,19 +120,28 @@ my $html_foot = sub {
'billed' => 'done',
;
- #XXX src & charged party (& default prefix) as per voip_cdr.pm
- #XXX handle toll free too
-
my $number = $svc_phone->phonenum;
$number = $svc_phone->countrycode. $number
unless $svc_phone->countrycode eq '1';
+ #src & charged party as per voip_cdr.pm
+ my $search;
+ my $cust_pkg = $svc_phone->cust_svc->cust_pkg;
+ if ( $cust_pkg && $cust_pkg->part_pkg->option('disable_src') ) {
+ $search = "charged_party=$number";
+ } else {
+ $search = "charged_party_or_src=$number";
+ }
+
+ #XXX default prefix as per voip_cdr.pm
+ #XXX handle toll free too
+
#my @links = map {
# qq(<A HREF="${p}search/cdr.html?src=$number;freesidestatus=$what{$_}">).
# "View $_ CDRs</A>";
#} keys(%what);
my @links = map {
- qq(<A HREF="${p}search/cdr.html?cdrbatchnum=__ALL__;charged_party=$number;freesidestatus=$what{$_}">).
+ qq(<A HREF="${p}search/cdr.html?cdrbatchnum=__ALL__;$search;freesidestatus=$what{$_}">).
"View $_ CDRs</A>";
} keys(%what);