summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
Diffstat (limited to 'httemplate')
-rwxr-xr-xhttemplate/browse/part_svc.cgi13
-rw-r--r--httemplate/edit/elements/edit.html1
-rw-r--r--httemplate/edit/elements/part_svc_column.html18
-rw-r--r--httemplate/edit/elements/svc_Common.html3
-rw-r--r--httemplate/edit/elements/svc_forward.html83
-rwxr-xr-xhttemplate/edit/part_svc.cgi20
-rwxr-xr-xhttemplate/edit/svc_acct.cgi63
-rw-r--r--httemplate/edit/svc_acct/communigate.html5
-rw-r--r--httemplate/edit/svc_broadband.cgi11
-rwxr-xr-xhttemplate/edit/svc_forward.cgi122
-rw-r--r--httemplate/elements/make_appointment.html39
-rw-r--r--httemplate/elements/menu.html5
-rw-r--r--httemplate/elements/popup_link-make_appointment.html31
-rw-r--r--httemplate/elements/schedule-appointment.html20
-rw-r--r--httemplate/elements/tr-input-date-field.html9
-rw-r--r--httemplate/elements/tr-select-hardware_type.html5
-rw-r--r--httemplate/elements/tr-select-router_block_ip.html6
-rw-r--r--httemplate/misc/payment.cgi6
-rw-r--r--httemplate/search/cdr.html22
-rw-r--r--httemplate/search/cust_bill_pay_pkg.html572
-rw-r--r--httemplate/search/report_cdr.html2
-rw-r--r--httemplate/search/report_cust_bill_pay_pkg.html88
-rw-r--r--httemplate/view/Status.html2
-rw-r--r--httemplate/view/cust_main/appointments.html11
-rw-r--r--httemplate/view/cust_main/packages/services.html3
-rw-r--r--httemplate/view/cust_main/packages/status.html2
26 files changed, 1002 insertions, 160 deletions
diff --git a/httemplate/browse/part_svc.cgi b/httemplate/browse/part_svc.cgi
index 0d3685355..ec5f321dd 100755
--- a/httemplate/browse/part_svc.cgi
+++ b/httemplate/browse/part_svc.cgi
@@ -61,6 +61,8 @@ function part_export_areyousure(href) {
<TH COLSPAN=2 CLASS="grid" BGCOLOR="#cccccc">Modifier</TH>
+ <TH CLASS="grid" BGCOLOR="#cccccc" STYLE="font-size: smaller;">Required</TH>
+
</TR>
% my $conf = FS::Conf->new;
% foreach my $part_svc ( @part_svc ) {
@@ -78,6 +80,9 @@ function part_export_areyousure(href) {
% $col->columnflag || ( $col->columnlabel !~ /^\S*$/
% && $col->columnlabel ne $def->{'label'}
% )
+% || ( $col->required
+% && !$def->{'required'}
+% )
% )
% }
% @dfields ;
@@ -150,7 +155,7 @@ function part_export_areyousure(href) {
</TD>
% unless ( @fields ) {
-% for ( 1..4 ) {
+% for ( 1..5 ) {
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>"</TD>
% }
% }
@@ -170,7 +175,6 @@ function part_export_areyousure(href) {
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $field %></TD>
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $label %></TD>
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $flag{$flag} %></TD>
-
<TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
% my $value = &$formatter($part_svc->part_svc_column($field)->columnvalue);
% if ( $flag =~ /^[MAH]$/ ) {
@@ -189,6 +193,11 @@ function part_export_areyousure(href) {
% }
</TD>
+ <TD CLASS="grid" BGCOLOR="<% $bgcolor %>">
+% if ($part_svc_column->required) {
+ Yes
+% }
+ </TD>
% $n1="</TR><TR>";
% } #foreach $field
% if ( $part_svc->restrict_edit_password ) {
diff --git a/httemplate/edit/elements/edit.html b/httemplate/edit/elements/edit.html
index c80cdae19..c9eceb459 100644
--- a/httemplate/edit/elements/edit.html
+++ b/httemplate/edit/elements/edit.html
@@ -395,6 +395,7 @@ Example:
% 'value' => $opt{curr_value},
% 'label' => $label,
% 'noinit' => $f->{noinit},
+% 'required' => $f->{'required'},
% }
% );
% }
diff --git a/httemplate/edit/elements/part_svc_column.html b/httemplate/edit/elements/part_svc_column.html
index 2bb4f5e41..d9500320a 100644
--- a/httemplate/edit/elements/part_svc_column.html
+++ b/httemplate/edit/elements/part_svc_column.html
@@ -77,6 +77,7 @@ that field.
<TH BGCOLOR="#cccccc">Field</TH>
<TH BGCOLOR="#cccccc">Label</TH>
<TH BGCOLOR="#cccccc" COLSPAN=2>Modifier</TH>
+ <TH BGCOLOR="#cccccc"><% $manual_require ? 'Required?' : '' %></TH>
</TR>
% $part_svc->set('svcpart' => $opt{'clone'}) if $opt{'clone'}; # for now
% my $i = 0;
@@ -210,9 +211,19 @@ that field.
&>
% }
</TD>
+ <TD>
+% if ($manual_require &&
+% (!$def->{'type'} || !(grep {$_ eq $def->{'type'}} ('checkbox','disabled')))
+% ) {
+ <INPUT ID="<% $name.'_required' %>" TYPE="checkbox" NAME="<% $svcdb %>__<% $field %>_required" VALUE="Y"
+ <% ($part_svc_column->required || $def->{'required'}) ? 'CHECKED' : '' %>
+ <% $def->{'required'} ? 'DISABLED' : '' %>
+ >
+% }
+ </TD>
</TR>
<TR CLASS="row<%$i%>">
- <TD COLSPAN=2 CLASS="def_info">
+ <TD COLSPAN=3 CLASS="def_info">
% if ( $def->{def_info} ) {
(<% $def->{def_info} %>)
</TD>
@@ -228,7 +239,7 @@ that field.
<TD COLSPAN=3 ALIGN="right">
<% emt('Require "Provision" access right to edit password') %>
</TD>
- <TD>
+ <TD COLSPAN=2>
<INPUT TYPE="checkbox" NAME="restrict_edit_password" VALUE="Y" \
<% $part_svc->restrict_edit_password ? 'CHECKED' : '' %>>
</TD>
@@ -244,7 +255,7 @@ that field.
<TD COLSPAN=3 ALIGN="right">
<% emt('This service has an attached router') %>
</TD>
- <TD>
+ <TD COLSPAN=2>
<INPUT TYPE="checkbox" NAME="has_router" VALUE="Y" \
<% $part_svc->has_router ? 'CHECKED' : '' %>>
</TD>
@@ -301,4 +312,5 @@ if ( $svcdb eq 'svc_acct'
}
my @defs = map { FS::part_svc->svc_table_fields($svcdb)->{$_} } @fields;
+my $manual_require = FS::part_svc->svc_table_info($svcdb)->{'manual_require'};
</%init>
diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html
index 97b630f76..a4e345e40 100644
--- a/httemplate/edit/elements/svc_Common.html
+++ b/httemplate/edit/elements/svc_Common.html
@@ -102,6 +102,9 @@
my $columndef = $part_svc->part_svc_column($f->{'field'});
my $flag = $columndef->columnflag;
+ $f->{'required'} = 1
+ if $columndef->required;
+
if ( $flag eq 'F' ) { #fixed
$f->{'value'} = $columndef->columnvalue;
if (length($columndef->columnvalue)) {
diff --git a/httemplate/edit/elements/svc_forward.html b/httemplate/edit/elements/svc_forward.html
new file mode 100644
index 000000000..e40ebe807
--- /dev/null
+++ b/httemplate/edit/elements/svc_forward.html
@@ -0,0 +1,83 @@
+<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $opt{'svcnum'} %>">
+<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $opt{'pkgnum'} %>">
+<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $opt{'svcpart'} %>">
+
+<SCRIPT TYPE="text/javascript">
+function srcchanged(what) {
+ if ( what.options[what.selectedIndex].value == 0 ) {
+ what.form.src.disabled = false;
+ what.form.src.style.backgroundColor = "white";
+ } else {
+ what.form.src.disabled = true;
+ what.form.src.style.backgroundColor = "lightgrey";
+ }
+}
+function dstchanged(what) {
+ if ( what.options[what.selectedIndex].value == 0 ) {
+ what.form.dst.disabled = false;
+ what.form.dst.style.backgroundColor = "white";
+ } else {
+ what.form.dst.disabled = true;
+ what.form.dst.style.backgroundColor = "lightgrey";
+ }
+}
+</SCRIPT>
+
+<% ntable("#cccccc",2) %>
+
+<TR>
+ <TD ALIGN="right">Email to</TD>
+ <TD>
+% if ( $conf->exists('svc_forward-no_srcsvc') ) {
+ <INPUT NAME="srcsrc" TYPE="hidden" VALUE="0">
+% } else {
+ <SELECT NAME="srcsvc" SIZE=1 onChange="srcchanged(this)">
+% foreach my $somesvc (keys %email) {
+ <OPTION VALUE="<% $somesvc %>"
+ <% $somesvc eq $opt{'srcsvc'} ? 'SELECTED' : '' %>
+ ><% $email{$somesvc} %></OPTION>
+% }
+ <OPTION VALUE="0" <% $opt{'src'} ? 'SELECTED' : '' %>
+ >(other email address)</OPTION>
+ </SELECT>
+% }
+
+% my $src_disabled = $opt{'src'}
+% || $conf->exists('svc_forward-no_srcsvc')
+% || !scalar(%email);
+ <INPUT NAME = "src"
+ TYPE = "text"
+ VALUE = "<% $opt{'src'} %>"
+ <% $src_disabled ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>
+ >
+
+ </TD>
+</TR>
+
+<TR><TD ALIGN="right">Forwards to</TD>
+<TD><SELECT NAME="dstsvc" SIZE=1 onChange="dstchanged(this)">
+% foreach my $somesvc (keys %email) {
+
+ <OPTION<% $somesvc eq $opt{'dstsvc'} ? " SELECTED" : "" %> VALUE="<% $somesvc %>"><% $email{$somesvc} %></OPTION>
+% }
+
+<OPTION <% $opt{'dst'} ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION>
+</SELECT>
+<INPUT TYPE="text" NAME="dst" VALUE="<% $opt{'dst'} %>" <% ( $opt{'dst'} || !scalar(%email) ) ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>>
+</TD></TR>
+</TABLE>
+
+<%init>
+
+# This is used directly by selfservice, only use what's passed
+# Do not look up sensitive information here
+
+my %opt = @_;
+
+my $conf = $opt{'conf'};
+$conf ||= new FS::Conf;
+
+my %email;
+%email = %{$opt{'email'}} if ref($opt{'email'}) eq 'HASH';
+
+</%init>
diff --git a/httemplate/edit/part_svc.cgi b/httemplate/edit/part_svc.cgi
index 47b020c5a..7a47f1550 100755
--- a/httemplate/edit/part_svc.cgi
+++ b/httemplate/edit/part_svc.cgi
@@ -101,6 +101,15 @@ function flag_changed(obj) {
}
}
}
+ var required = document.getElementById(layer + '__' + field + '_required');
+ if (required && !required.disabledinit) {
+ if (newflag == "F") {
+ required.checked = false;
+ required.disabled = true;
+ } else {
+ required.disabled = false;
+ }
+ }
}
window.onload = function() {
@@ -111,6 +120,17 @@ window.onload = function() {
obj.setAttribute('should_be_multiple', true);
}
}
+ var inputs = document.getElementsByTagName('INPUT');
+ for(i = 0; i < inputs.length; i++) {
+ var obj = inputs[i];
+ if (obj.type == 'checkbox') {
+ if ( obj.name.match(/_required$/) ) {
+ if ( obj.disabled ) {
+ obj.disabledinit = 1;
+ }
+ }
+ }
+ }
for(i = 0; i < selects.length; i++) {
var obj = selects[i];
if ( obj.name.match(/_flag$/) ) {
diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi
index 90f48237f..a5e778981 100755
--- a/httemplate/edit/svc_acct.cgi
+++ b/httemplate/edit/svc_acct.cgi
@@ -30,8 +30,9 @@
<TD BGCOLOR="#eeeeee"><% $part_svc->svc %></TD>
</TR>
-<TR>
- <TD ALIGN="right"><% mt('Username') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Username'),
+ 'required' => $part_svc->part_svc_column('username')->required ) %>
% if ( $svcnum && $conf->exists('svc_acct-no_edit_username') ) {
<TD BGCOLOR="#eeeeee"><% $svc_acct->username() %></TD>
<INPUT TYPE="hidden" NAME="username" VALUE="<% $username %>">
@@ -43,9 +44,10 @@
</TR>
%if ( $part_svc->part_svc_column('_password')->columnflag ne 'F' ) {
-<TR>
% #XXX eventually should require "Edit Password" ACL
- <TD ALIGN="right"><% mt('Password') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Password'),
+ 'required' => $part_svc->part_svc_column('_password')->required ) %>
<TD>
<INPUT TYPE="text" ID="clear_password" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>>
<& /elements/random_pass.html, 'clear_password' &>
@@ -60,8 +62,9 @@
%if ( $conf->exists('security_phrase')
% && $part_svc->part_svc_column('sec_phrase')->columnflag ne 'F' ) {
- <TR>
- <TD ALIGN="right"><% mt('Security phrase') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Security phrase'),
+ 'required' => $part_svc->part_svc_column('sec_phrase')->required ) %>
<TD>
<INPUT TYPE="text" NAME="sec_phrase" VALUE="<% $sec_phrase %>" SIZE=32>
(<% mt('for forgotten passwords') |h %>)
@@ -97,8 +100,9 @@
% )
% );
- <TR>
- <TD ALIGN="right"><% mt('Domain') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Domain'),
+ 'required' => $part_svc->part_svc_column('domsvc')->required ) %>
<TD>
<SELECT NAME="domsvc" SIZE=1>
% foreach my $svcnum (
@@ -143,8 +147,9 @@
<INPUT TYPE="hidden" NAME="popnum" VALUE="<% $popnum %>">
% } else {
- <TR>
- <TD ALIGN="right"><% mt('Access number') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Access number'),
+ 'required' => $part_svc->part_svc_column('popnum')->required ) %>
<TD><% FS::svc_acct_pop::popselector($popnum) %></TD>
</TR>
% }
@@ -156,6 +161,7 @@
'curr_value' => $svc_acct->sectornum,
#'part_svc' => $part_svc,
#'cust_pkg' => $cust_pkg,
+ 'required' => $part_svc->part_svc_column('sectornum')->required,
&>
%} else {
<INPUT TYPE="hidden" NAME="sectornum" VALUE="<% $svc_acct->sectornum %>">
@@ -176,6 +182,9 @@
%
% if ( length($svc_acct->$xid()) ) {
+<% include('/elements/tr-td-label.html',
+ 'label' => uc($xid),
+ 'required' => $part_svc->part_svc_column($xid)->required ) %>
<TR>
<TD ALIGN="right"><% uc($xid) %></TD>
<TD BGCOLOR="#eeeeee"><% $svc_acct->$xid() %></TD>
@@ -187,8 +196,9 @@
<INPUT TYPE="hidden" NAME="<% $xid %>" VALUE="<% $svc_acct->$xid() %>">
% } else {
- <TR>
- <TD ALIGN="right"><% uc($xid) %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => uc($xid),
+ 'required' => $part_svc->part_svc_column($xid)->required ) %>
<TD>
<INPUT TYPE="text" NAME="<% $xid %>" SIZE=8 MAXLENGTH=6 VALUE="<% $svc_acct->$xid() %>">
</TD>
@@ -205,8 +215,9 @@
% } else {
- <TR>
- <TD ALIGN="right"><% mt('Real Name') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Real Name'),
+ 'required' => $part_svc->part_svc_column('finger')->required ) %>
<TD>
<INPUT TYPE="text" NAME="finger" VALUE="<% $svc_acct->finger %>">
</TD>
@@ -223,8 +234,9 @@
% } else {
- <TR>
- <TD ALIGN="right"><% mt('Home directory') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Home directory'),
+ 'required' => $part_svc->part_svc_column('dir')->required ) %>
<TD><INPUT TYPE="text" NAME="dir" VALUE="<% $svc_acct->dir %>"></TD>
</TR>
% }
@@ -240,8 +252,9 @@
% } else {
- <TR>
- <TD ALIGN="right"><% mt('Shell') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('Shell'),
+ 'required' => $part_svc->part_svc_column('shell')->required ) %>
<TD>
<SELECT NAME="shell" SIZE=1>
%
@@ -269,7 +282,9 @@
% # (should we show this if slipip is fixed?)
<& /elements/tr-select-router_block_ip.html,
'object' => $svc_acct,
- 'ip_field' => 'slipip'
+ 'ip_field' => 'slipip',
+ 'required' => $part_svc->part_svc_column('routernum')->required,
+ 'ip_addr_required' => $part_svc->part_svc_column('slipip')->required,
&>
% } else {
% # don't expose these to the user--they're only useful in the other case
@@ -278,8 +293,9 @@
% if ( $part_svc->part_svc_column('slipip')->columnflag =~ /^[FA]$/ ) {
<INPUT TYPE="hidden" NAME="slipip" VALUE="<% $svc_acct->slipip %>">
% } else {
- <TR>
- <TD ALIGN="right"><% mt('IP') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('IP'),
+ 'required' => $part_svc->part_svc_column('slipip')->required ) %>
<TD><INPUT TYPE="text" NAME="slipip" VALUE="<% $svc_acct->slipip %>"></TD>
</TR>
% }
@@ -324,8 +340,9 @@
% }
-<TR>
- <TD ALIGN="right"><% mt('RADIUS groups') |h %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => mt('RADIUS groups'),
+ 'required' => $part_svc->part_svc_column('usergroup')->required ) %>
% if ( $part_svc_usergroup->columnflag eq 'F' ) {
<TD BGCOLOR="#eeeeee"><% join('<BR>', @groupnames) %></TD>
% } else {
diff --git a/httemplate/edit/svc_acct/communigate.html b/httemplate/edit/svc_acct/communigate.html
index 6370a54dc..370bfb0e7 100644
--- a/httemplate/edit/svc_acct/communigate.html
+++ b/httemplate/edit/svc_acct/communigate.html
@@ -47,8 +47,9 @@
<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>
+<% include('/elements/tr-td-label.html',
+ 'label' => $quota_label,
+ 'required' => $part_svc->part_svc_column('quota')->required ) %>
<TD><INPUT TYPE="text" NAME="quota" VALUE="<% $svc_acct->quota %>"></TD>
</TR>
% }
diff --git a/httemplate/edit/svc_broadband.cgi b/httemplate/edit/svc_broadband.cgi
index 1b85460e6..81c694aa5 100644
--- a/httemplate/edit/svc_broadband.cgi
+++ b/httemplate/edit/svc_broadband.cgi
@@ -102,7 +102,16 @@ END
my @fields = (
qw( description speed_down speed_up ),
{ field=>'sectornum', type=>'select-tower_sector', },
- { field=>'routernum', type=>'select-router_block_ip' },
+ { field=>'routernum', type=>'select-router_block_ip',
+ include_opt_callback => sub {
+ my $svc_broadband = shift;
+ my $part_svc = $svc_broadband->part_svc;
+ return () unless $part_svc; #sanity check
+ my $col = $part_svc->part_svc_column('ip_addr');
+ return () unless $col; #sanity check
+ return ('ip_addr_required' => $col->required);
+ },
+ },
{ field=>'mac_addr' , type=>'input-mac_addr' },
qw(
latitude longitude altitude
diff --git a/httemplate/edit/svc_forward.cgi b/httemplate/edit/svc_forward.cgi
index 73f6465b9..de56070ec 100755
--- a/httemplate/edit/svc_forward.cgi
+++ b/httemplate/edit/svc_forward.cgi
@@ -6,74 +6,19 @@ Service #<% $svcnum ? "<B>$svcnum</B>" : " (NEW)" %><BR>
Service: <B><% $part_svc->svc %></B><BR><BR>
<FORM ACTION="process/svc_forward.cgi" METHOD="POST">
-<INPUT TYPE="hidden" NAME="svcnum" VALUE="<% $svcnum %>">
-<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
-<INPUT TYPE="hidden" NAME="svcpart" VALUE="<% $svcpart %>">
-
-<SCRIPT TYPE="text/javascript">
-function srcchanged(what) {
- if ( what.options[what.selectedIndex].value == 0 ) {
- what.form.src.disabled = false;
- what.form.src.style.backgroundColor = "white";
- } else {
- what.form.src.disabled = true;
- what.form.src.style.backgroundColor = "lightgrey";
- }
-}
-function dstchanged(what) {
- if ( what.options[what.selectedIndex].value == 0 ) {
- what.form.dst.disabled = false;
- what.form.dst.style.backgroundColor = "white";
- } else {
- what.form.dst.disabled = true;
- what.form.dst.style.backgroundColor = "lightgrey";
- }
-}
-</SCRIPT>
-
-<% ntable("#cccccc",2) %>
-
-<TR>
- <TD ALIGN="right">Email to</TD>
- <TD>
-% if ( $conf->exists('svc_forward-no_srcsvc') ) {
- <INPUT NAME="srcsrc" TYPE="hidden" VALUE="0">
-% } else {
- <SELECT NAME="srcsvc" SIZE=1 onChange="srcchanged(this)">
-% foreach $_ (keys %email) {
- <OPTION VALUE="<% $_ %>"
- <% $_ eq $srcsvc ? 'SELECTED' : '' %>
- ><% $email{$_} %></OPTION>
-% }
- <OPTION VALUE="0" <% $src ? 'SELECTED' : '' %>
- >(other email address)</OPTION>
- </SELECT>
-% }
-
-% my $src_disabled = $src
-% || $conf->exists('svc_forward-no_srcsvc')
-% || !scalar(%email);
- <INPUT NAME = "src"
- TYPE = "text"
- VALUE = "<% $src %>"
- <% $src_disabled ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>
- >
-
- </TD>
-</TR>
-
-<TR><TD ALIGN="right">Forwards to</TD>
-<TD><SELECT NAME="dstsvc" SIZE=1 onChange="dstchanged(this)">
-% foreach $_ (keys %email) {
-
- <OPTION<% $_ eq $dstsvc ? " SELECTED" : "" %> VALUE="<% $_ %>"><% $email{$_} %></OPTION>
-% }
-
-<OPTION <% $dst ? 'SELECTED' : '' %> VALUE="0">(other email address)</OPTION>
-</SELECT>
-<INPUT TYPE="text" NAME="dst" VALUE="<% $dst %>" <% ( $dst || !scalar(%email) ) ? '' : 'DISABLED STYLE="background-color: lightgrey"' %>>
-</TD></TR>
- </TABLE>
+
+<% include('elements/svc_forward.html',
+ 'conf' => $conf,
+ 'svcnum' => $svcnum,
+ 'pkgnum' => $pkgnum,
+ 'svcpart' => $svcpart,
+ 'srcsvc' => $srcsvc,
+ 'dstsvc' => $dstsvc,
+ 'src' => $src,
+ 'dst' => $dst,
+ 'email' => \%email,
+ ) %>
+
<BR><INPUT TYPE="submit" VALUE="Submit">
</FORM>
@@ -136,44 +81,23 @@ my $action = $svc_forward->svcnum ? 'Edit' : 'Add';
my %email;
-#starting with those currently attached
-foreach my $method (qw( srcsvc_acct dstsvc_acct )) {
- my $svc_acct = $svc_forward->$method();
- $email{$svc_acct->svcnum} = $svc_acct->email if $svc_acct;
-}
-
if ($pkgnum) {
#find all possible user svcnums (and emails)
- #and including the rest for this customer
- my($u_part_svc,@u_acct_svcparts);
- foreach $u_part_svc ( qsearch('part_svc',{'svcdb'=>'svc_acct'}) ) {
- push @u_acct_svcparts,$u_part_svc->getfield('svcpart');
- }
-
- my($cust_pkg)=qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
- my($custnum)=$cust_pkg->getfield('custnum');
- my($i_cust_pkg);
- foreach $i_cust_pkg ( qsearch('cust_pkg',{'custnum'=>$custnum}) ) {
- my($cust_pkgnum)=$i_cust_pkg->getfield('pkgnum');
- my($acct_svcpart);
- foreach $acct_svcpart (@u_acct_svcparts) { #now find the corresponding
- #record(s) in cust_svc ( for this
- #pkgnum ! )
- foreach my $i_cust_svc (
- qsearch( 'cust_svc', { 'pkgnum' => $cust_pkgnum,
- 'svcpart' => $acct_svcpart } )
- ) {
- my $svc_acct =
- qsearchs( 'svc_acct', { 'svcnum' => $i_cust_svc->svcnum } );
- $email{$svc_acct->svcnum} = $svc_acct->email;
- }
- }
- }
+ my $cust_pkg = qsearchs('cust_pkg',{'pkgnum'=>$pkgnum});
+ die "Specified package not found" unless $cust_pkg;
+ %email = $cust_pkg->forward_emails('svc_forward' => $svc_forward);
} elsif ( $action eq 'Add' ) {
+
die "\$action eq Add, but \$pkgnum is null!\n";
+
+} else {
+
+ use FS::cust_pkg;
+ %email = FS::cust_pkg->forward_emails('svc_forward' => $svc_forward);
+
}
my($srcsvc,$dstsvc,$dst)=(
diff --git a/httemplate/elements/make_appointment.html b/httemplate/elements/make_appointment.html
new file mode 100644
index 000000000..ddf377f54
--- /dev/null
+++ b/httemplate/elements/make_appointment.html
@@ -0,0 +1,39 @@
+<& /elements/header-popup.html &>
+
+% #XXX actually, the action needs to repace the top-level window with this
+% #$params->{'action'} = $p. 'rt/Search/Schedule.html?'. $query;
+<FORM NAME="appointment_form" METHOD="POST" ACTION="<% $p %>elements/schedule-appointment.html">
+
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $cgi->param('custnum') |h %>">
+
+% my @sched_item = qsearch('sched_item', { 'disabled' => '', });
+% my @username = map $_->access_user->username, @sched_item;
+% foreach my $username (@username) {
+ <INPUT TYPE="hidden" NAME="username" VALUE="<% $username |h %>">
+% }
+
+Length:
+<SELECT NAME="LengthMin">
+% for ( my $hours = .5; $hours < 10.5; $hours += .5 ) {
+% my $min = $hours * 60;
+ <OPTION VALUE="<% $min %>"
+ <% $min == $default_length ? 'SELECTED' : '' %>
+ ><% $hours %> hour<% $hours > 1 ? 's' : '' %>
+% }
+</SELECT>
+<BR>
+<BR>
+
+<INPUT TYPE="submit" VALUE="Schedule appointment">
+
+</FORM>
+
+<& /elements/footer-popup.html &>
+<%init>
+
+my $default_length = FS::Conf->new->config('default_appointment_length') || 180;
+
+#XXX '&pkgnum=$pkgnum";need to be for specific package/location, not just for a customer... default to active(/ordered) package in a pkg_class w/ticketing_queueid, otherwise, a popup? (well, we're already a popup now) we're getting complicated like form-creat_ticket.html
+
+
+</%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 1bab4ef46..34bba7e8c 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -328,13 +328,14 @@ tie my %report_bill_event, 'Tie::IxHash',
;
tie my %report_payments, 'Tie::IxHash',
- 'Payments' => [ $fsurl.'search/report_cust_pay.html', 'Payment report (by type and/or date range)' ],
+ 'Payments' => [ $fsurl.'search/report_cust_pay.html', 'Payment report (by type and/or date range)' ],
+ 'Payment application detail' => [ $fsurl.'search/report_cust_bill_pay_pkg.html', 'Line item application detail' ],
;
$report_payments{'Pending Payments'} = [ $fsurl.'search/cust_pay_pending.html?magic=_date;statusNOT=done', 'Pending real-time payments' ]
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{'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' ];
diff --git a/httemplate/elements/popup_link-make_appointment.html b/httemplate/elements/popup_link-make_appointment.html
new file mode 100644
index 000000000..60a337c04
--- /dev/null
+++ b/httemplate/elements/popup_link-make_appointment.html
@@ -0,0 +1,31 @@
+<%doc>
+
+Example:
+
+ include('/elements/init_overlib.html')
+
+ include( '/elements/popup_link-make-appointment.html', { #hashref or a list, either way
+ 'custnum' => 54,
+ })
+
+</%doc>
+<& /elements/popup_link.html, $params &>\
+<%init>
+
+my $params = { 'closetext' => 'Close' };
+
+if (ref($_[0]) eq 'HASH') {
+ $params = { %$params, %{ $_[0] } };
+} else {
+ $params = { %$params, @_ };
+}
+
+#$params->{'label'} ||= 'schedule new appointment';
+$params->{'label'} ||= 'Schedule new appointment';
+$params->{'actionlabel'} ||= 'Schedule appointment';
+#$params->{'width'} ||= 350;
+#$params->{'height'} ||= 220;
+
+$params->{'action'} = $p. '/elements/make_appointment.html?custnum='. $params->{'custnum'};
+
+</%init>
diff --git a/httemplate/elements/schedule-appointment.html b/httemplate/elements/schedule-appointment.html
new file mode 100644
index 000000000..45a8a5bab
--- /dev/null
+++ b/httemplate/elements/schedule-appointment.html
@@ -0,0 +1,20 @@
+<& /elements/header-popup.html &>
+<h1>Scheduling appointment...</h1>
+<SCRIPT TYPE="text/javascript">
+ window.top.location.href = '<% $url %>';
+</SCRIPT>
+<& /elements/footer-popup.html &>
+<%init>
+
+my @username = map encode_entities($_), $cgi->param('username');
+my $LengthMin = encode_entities( $cgi->param('LengthMin') );
+my $custnum = encode_entities( $cgi->param('custnum') );
+
+my $query = join('&', map "username=$_", @username).
+ "&LengthMin=$LengthMin".
+ "&custnum=$custnum";
+ #XXX '&pkgnum=$pkgnum";need to be for specific package/location, not just for a customer... default to active(/ordered) package in a pkg_class w/ticketing_queueid, otherwise, a popup? we're getting complicated like form-creat_ticket.html
+
+my $url = $p. 'rt/Search/Schedule.html?'. $query;
+
+</%init>
diff --git a/httemplate/elements/tr-input-date-field.html b/httemplate/elements/tr-input-date-field.html
index ff9855184..f2a570b99 100644
--- a/httemplate/elements/tr-input-date-field.html
+++ b/httemplate/elements/tr-input-date-field.html
@@ -13,6 +13,7 @@ Example:
'usedatetime' => 1, #use DateTime->strftime to format the date
# instead of Date::Format->time2str
'noinit' => 1, #first one on the page is enough
+ 'required' => 1,
},
&>
@@ -24,8 +25,9 @@ Example:
<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT>
% }
-<TR>
- <TD ALIGN="right"><% $label %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => $label,
+ 'required' => $required ) %>
<TD COLSPAN=<% $colspan %>>
<INPUT TYPE="text" NAME="<% $name %>" ID="<% $name %>_text" VALUE="<% $value %>">
<IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $name %>_button" STYLE="cursor: pointer" TITLE="<% mt('Select date') |h %>">
@@ -48,7 +50,7 @@ Example:
<%init>
-my($name, $value, $label, $format, $usedatetime, $noinit, $colspan);
+my($name, $value, $label, $format, $usedatetime, $noinit, $colspan, $required);
if ( ref($_[0]) ) {
my $opt = shift;
$name = $opt->{'name'};
@@ -58,6 +60,7 @@ if ( ref($_[0]) ) {
$usedatetime = $opt->{'usedatetime'};
$noinit = $opt->{'noinit'};
$colspan = $opt->{'colspan'} || 1;
+ $required = $opt->{'required'};
} else {
($name, $value, $label, $format, $usedatetime) = @_;
$colspan = 1;
diff --git a/httemplate/elements/tr-select-hardware_type.html b/httemplate/elements/tr-select-hardware_type.html
index c3066417b..b51afc0a5 100644
--- a/httemplate/elements/tr-select-hardware_type.html
+++ b/httemplate/elements/tr-select-hardware_type.html
@@ -1,5 +1,6 @@
-<TR>
- <TD ALIGN="right"><% $opt{'label'} || 'Device type: ' %></TD>
+<% include('/elements/tr-td-label.html',
+ 'label' => $opt{'label'} || 'Device type: ',
+ 'required' => $opt{'required'} ) %>
<TD><% include('select-hardware_type.html', %opt) %></TD>
</TR>
diff --git a/httemplate/elements/tr-select-router_block_ip.html b/httemplate/elements/tr-select-router_block_ip.html
index 11f7c4831..ee135686c 100644
--- a/httemplate/elements/tr-select-router_block_ip.html
+++ b/httemplate/elements/tr-select-router_block_ip.html
@@ -30,7 +30,7 @@ function clearhint_ip_addr (what) {
what.value = '';
}
</script>
-<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router') &>
+<& /elements/tr-td-label.html, label => ($opt{'label'} || 'Router'), required => $opt{'required'} &>
<td>
<& /elements/select-tiered.html, prefix => 'router_', tiers => [
{
@@ -56,9 +56,9 @@ function clearhint_ip_addr (what) {
]
&>
</td></tr>
-<& /elements/tr-td-label.html, label => 'IP address' &>
+<& /elements/tr-td-label.html, label => 'IP address', required => $opt{'ip_addr_required'} &>
<td>
-% warn Dumper \%fixed;
+% #warn Dumper \%fixed;
% if ( exists $fixed{$ip_field} ) {
<input type="hidden" id="input_ip_addr" name="<% $ip_field %>"
value="<% $opt{'ip_addr'} |h%>"><% $opt{'ip_addr'} || '' %>
diff --git a/httemplate/misc/payment.cgi b/httemplate/misc/payment.cgi
index 12fca2086..a48aa0120 100644
--- a/httemplate/misc/payment.cgi
+++ b/httemplate/misc/payment.cgi
@@ -17,7 +17,11 @@
'process-display' => scalar($conf->config('manual_process-display')),
'process-skip_first' => $conf->exists('manual_process-skip_first'),
'num_payments' => scalar($cust_main->cust_pay),
- 'surcharge_percentage' => scalar($conf->config('credit-card-surcharge-percentage')),
+ 'surcharge_percentage' =>
+ ( $payby eq 'CARD'
+ ? scalar($conf->config('credit-card-surcharge-percentage'))
+ : 0
+ ),
&>
% if ( $conf->exists('part_pkg-term_discounts') ) {
diff --git a/httemplate/search/cdr.html b/httemplate/search/cdr.html
index 778799b1f..e57913e5f 100644
--- a/httemplate/search/cdr.html
+++ b/httemplate/search/cdr.html
@@ -289,11 +289,11 @@ my $qsearch = join(' AND ', @qsearch);
$qsearch = ( scalar(keys %$hashref) ? ' AND ' : ' WHERE ' ) . $qsearch
if $qsearch;
-my $query = { 'table' => 'cdr',
- 'hashref' => $hashref,
- 'extra_sql' => $qsearch,
- 'order_by' => 'ORDER BY calldate',
- };
+my $query = { 'table' => 'cdr',
+ 'hashref' => $hashref,
+ 'extra_sql' => $qsearch,
+ 'order_by' => 'ORDER BY calldate',
+ };
if ( $cgi->param('longest') =~ /^(\d+)$/ && $cgi->param('longest') > 0 ) {
$cgi->param('maxrecords',$1);
@@ -308,16 +308,26 @@ if ( $cgi->param('longest') =~ /^(\d+)$/ && $cgi->param('longest') > 0 ) {
###
my %header = %{ FS::cdr->table_info->{'fields'} };
+$header{ratename} = 'Rate plan';
my @first = qw( acctid calldate clid charged_party src dst dcontext );
my %first = map { $_=>1 } @first;
-my @fields = ( @first, grep !$first{$_}, fields('cdr') );
+my @fields = ( @first,
+ grep !$first{$_}, ( fields('cdr'), 'ratename' )
+ );
if ( $cgi->param('show') ) {
@fields = grep $cgi->param("show_$_"), @fields;
}
+if ( grep { $_ eq 'ratename' } @fields ) {
+ $query->{addl_from} .= '
+ LEFT JOIN rate_detail ON (cdr.rated_ratedetailnum = rate_detail.ratedetailnum)
+ LEFT JOIN rate ON (rate_detail.ratenum = rate.ratenum)
+ ';
+}
+
my @header = map {
if ( exists($header{$_}) ) {
$header{$_};
diff --git a/httemplate/search/cust_bill_pay_pkg.html b/httemplate/search/cust_bill_pay_pkg.html
new file mode 100644
index 000000000..a6738f32c
--- /dev/null
+++ b/httemplate/search/cust_bill_pay_pkg.html
@@ -0,0 +1,572 @@
+<& elements/search.html,
+ 'title' => 'Payment application detail', #to line item
+ 'name_singular' => 'payment application',
+ 'query' => $query,
+ 'count_query' => $count_query,
+ 'count_addl' => [ $money_char. '%.2f total', ],
+ 'header' => [
+ #'#',
+
+ 'Amount',
+ 'By',
+ '#',
+
+ #payment
+ 'Date',
+ 'By',
+
+ #application
+ 'Applied',
+
+ # line item
+ 'Description',
+ 'Location',
+ @post_desc_header,
+
+ #invoice
+ 'Invoice',
+ 'Date',
+ FS::UI::Web::cust_header(),
+ ],
+ 'fields' => [
+ #'billpaypkgnum',
+ sub { sprintf($money_char.'%.2f', shift->paid ) },
+ sub { $payby{shift->cust_bill_pay->cust_pay->payby} },
+ sub { my $cust_pay = shift->cust_bill_pay->cust_pay;
+ $cust_pay->payby =~ /^(CARD|CHEK)$/
+ ? $cust_pay->paymask : $cust_pay->payinfo;
+ },
+
+ sub { time2str('%b %d %Y', shift->get('cust_pay_date') ) },
+ sub { shift->cust_bill_pay->cust_pay->otaker },
+
+ sub { sprintf($money_char.'%.2f', shift->amount ) },
+
+ sub { $_[0]->pkgnum > 0
+ ? $_[0]->get('pkg') # possibly use override.pkg
+ : $_[0]->get('itemdesc') # but i think this correct
+ },
+ $location_sub,
+ @post_desc,
+ 'invnum',
+ sub { time2str('%b %d %Y', shift->_date ) },
+ \&FS::UI::Web::cust_fields,
+ ],
+ 'sort_fields' => [
+ 'paid',
+ '', #payby
+ 'cust_pay_date',
+ '', #'otaker',
+ '', #amount
+ '', #line item description
+ '', #location
+ @post_desc_null,
+ 'invnum',
+ '_date',
+ #cust fields
+ ],
+ 'links' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ @post_desc_null,
+ $ilink,
+ $ilink,
+ ( map { $_ ne 'Cust. Status' ? $clink : '' }
+ FS::UI::Web::cust_header()
+ ),
+ ],
+ 'align' => 'rcrrlrlllrrcl'.
+ $post_desc_align.
+ 'rr'.
+ FS::UI::Web::cust_aligns(),
+ 'color' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ @post_desc_null,
+ '',
+ '',
+ FS::UI::Web::cust_colors(),
+ ],
+ 'style' => [
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ @post_desc_null,
+ '',
+ '',
+ FS::UI::Web::cust_styles(),
+ ],
+
+&>
+<%init>
+
+#(and now we're cust_bill_pay_pkg.html, false laziness w/cust_credit_bill_pkg)
+#LOTS of false laziness below w/cust_bill_pkg.cgi
+# and a little w/cust_credit.html
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+my %payby = FS::payby->payby2shortname;
+
+#here is the agent virtualization
+my $agentnums_sql =
+ $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my @where = ( $agentnums_sql );
+
+if ( $cgi->param('usernum') =~ /^(\d+)$/ ) {
+ push @where, "cust_pay.usernum = $1";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @where, "cust_bill._date >= $beginning",
+ "cust_bill._date <= $ending";
+
+my($cr_begin, $cr_end) = FS::UI::Web::parse_beginning_ending($cgi, 'payment');
+push @where, "cust_pay._date >= $cr_begin",
+ "cust_pay._date <= $cr_end";
+
+#payment amount? seems more what is expected than the applied amount
+my @lt_gt = FS::UI::Web::parse_lt_gt($cgi, 'paid' );
+s/paid/cust_pay.paid/g foreach (@lt_gt);
+push @where, @lt_gt;
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+ push @where, "cust_main.agentnum = $1";
+}
+
+if ( $cgi->param('billpkgnum') =~ /^(\d+)$/ ) {
+ push @where, "billpkgnum = $1";
+}
+
+#classnum
+# not specified: all classes
+# 0: empty class
+# N: classnum
+my $use_override = $cgi->param('use_override');
+if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
+ my $comparison = '';
+ if ( $1 == 0 ) {
+ $comparison = "IS NULL";
+ } else {
+ $comparison = "= $1";
+ }
+
+ if ( $use_override ) {
+ push @where, "(
+ part_pkg.classnum $comparison AND pkgpart_override IS NULL OR
+ override.classnum $comparison AND pkgpart_override IS NOT NULL
+ )";
+ } else {
+ push @where, "part_pkg.classnum $comparison";
+ }
+}
+
+if ( $cgi->param('taxclass')
+ && ! $cgi->param('istax') #no part_pkg.taxclass in this case
+ #(should we save a taxclass or a link to taxnum
+ # in cust_bill_pkg or something like
+ # cust_bill_pkg_tax_location?)
+ )
+{
+
+ #override taxclass when use_override is specified? probably
+ #if ( $use_override ) {
+ #
+ # push @where,
+ # ' ( '. join(' OR ',
+ # map {
+ # ' ( part_pkg.taxclass = '. dbh->quote($_).
+ # ' AND pkgpart_override IS NULL '.
+ # ' OR '.
+ # ' override.taxclass = '. dbh->quote($_).
+ # ' AND pkgpart_override IS NOT NULL '.
+ # ' ) '
+ # }
+ # $cgi->param('taxclass')
+ # ).
+ # ' ) ';
+ #
+ #} else {
+
+ push @where,
+ ' ( '. join(' OR ',
+ map ' part_pkg.taxclass = '.dbh->quote($_),
+ $cgi->param('taxclass')
+ ).
+ ' ) ';
+
+ #}
+
+}
+
+my @loc_param = qw( district city county state country );
+
+if ( $cgi->param('out') ) {
+
+ my ( $loc_sql, @param ) = FS::cust_pkg->location_sql( 'ornull' => 1 );
+ while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution
+ $loc_sql =~ s/\?/'cust_main_county.'.shift(@param)/e;
+ }
+
+ $loc_sql =~ s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g
+ if $cgi->param('istax');
+
+ push @where, "
+ 0 = (
+ SELECT COUNT(*) FROM cust_main_county
+ WHERE cust_main_county.tax > 0
+ AND $loc_sql
+ )
+ ";
+
+ #not linked to by anything, but useful for debugging "out of taxable region"
+ if ( grep $cgi->param($_), @loc_param ) {
+
+ my %ph = map { $_ => dbh->quote( scalar($cgi->param($_)) ) } @loc_param;
+
+ my ( $loc_sql, @param ) = FS::cust_pkg->location_sql;
+ while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution
+ $loc_sql =~ s/\?/$ph{shift(@param)}/e;
+ }
+
+ push @where, $loc_sql;
+
+ }
+
+} elsif ( $cgi->param('country') ) {
+
+ my @counties = $cgi->param('county');
+
+ if ( scalar(@counties) > 1 ) {
+
+ #hacky, could be more efficient. care if it is ever used for more than the
+ # tax-report_groups filtering kludge
+
+ my $locs_sql =
+ ' ( '. join(' OR ', map {
+
+ my %ph = ( 'county' => dbh->quote($_),
+ map { $_ => dbh->quote( $cgi->param($_) ) }
+ qw( district city state country )
+ );
+
+ my ( $loc_sql, @param ) = FS::cust_pkg->location_sql;
+ while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution
+ $loc_sql =~ s/\?/$ph{shift(@param)}/e;
+ }
+
+ $loc_sql;
+
+ } @counties
+
+ ). ' ) ';
+
+ push @where, $locs_sql;
+
+ } else {
+
+ my %ph = map { $_ => dbh->quote( scalar($cgi->param($_)) ) } @loc_param;
+
+ my ( $loc_sql, @param ) = FS::cust_pkg->location_sql;
+ while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution
+ $loc_sql =~ s/\?/$ph{shift(@param)}/e;
+ }
+
+ push @where, $loc_sql;
+
+ }
+
+ my($title, $name);
+ if ( $cgi->param('istax') ) {
+ $title = 'Tax credits';
+ $name = 'tax credits';
+ if ( $cgi->param('taxname') ) {
+ push @where, 'itemdesc = '. dbh->quote( $cgi->param('taxname') );
+ #} elsif ( $cgi->param('taxnameNULL') {
+ } else {
+ push @where, "( itemdesc IS NULL OR itemdesc = '' OR itemdesc = 'Tax' )";
+ }
+ } elsif ( $cgi->param('nottax') ) {
+ $title = 'Payment applications to line items';
+ $name = 'applications';
+ #what can we usefully do with "taxname" ???? look up a class???
+ } else {
+ $title = 'Payment applications to line items';
+ $name = 'applications';
+ #warn "neither nottax nor istax parameters specified";
+ }
+
+ if ( $cgi->param('taxclassNULL')
+ && ! $cgi->param('istax') #no taxclass in this case
+ )
+ {
+
+ my %hash = ( 'country' => scalar($cgi->param('country')) );
+ foreach (qw( state county )) {
+ $hash{$_} = scalar($cgi->param($_)) if $cgi->param($_);
+ }
+ my $cust_main_county = qsearchs('cust_main_county', \%hash);
+ die "unknown base region for empty taxclass" unless $cust_main_county;
+
+ my $same_sql = $cust_main_county->sql_taxclass_sameregion;
+ push @where, $same_sql if $same_sql;
+
+ }
+
+} elsif ( scalar( grep( /locationtaxid/, $cgi->param ) ) ) {
+
+ # this should really be shoved out to FS::cust_pkg->location_sql or something
+ # along with the code in report_newtax.cgi
+
+ my %pn = (
+ 'county' => 'tax_rate_location.county',
+ 'state' => 'tax_rate_location.state',
+ 'city' => 'tax_rate_location.city',
+ 'locationtaxid' => 'cust_bill_pkg_tax_rate_location.locationtaxid',
+ );
+
+ my %ph = map { ( $pn{$_} => dbh->quote( $cgi->param($_) || '' ) ) }
+ qw( city county state locationtaxid );
+
+ push @where,
+ join( ' AND ', map { "( $_ = $ph{$_} OR $ph{$_} = '' AND $_ IS NULL)" }
+ keys %ph
+ );
+
+}
+
+if ( $cgi->param('itemdesc') ) {
+ if ( $cgi->param('itemdesc') eq 'Tax' ) {
+ push @where, "(itemdesc='Tax' OR itemdesc is null)";
+ } else {
+ push @where, 'itemdesc='. dbh->quote($cgi->param('itemdesc'));
+ }
+}
+
+if ( $cgi->param('report_group') =~ /^(=|!=) (.*)$/ && $cgi->param('istax') ) {
+ my ( $group_op, $group_value ) = ( $1, $2 );
+ if ( $group_op eq '=' ) {
+ #push @where, 'itemdesc LIKE '. dbh->quote($group_value.'%');
+ push @where, 'itemdesc = '. dbh->quote($group_value);
+ } elsif ( $group_op eq '!=' ) {
+ push @where, '( itemdesc != '. dbh->quote($group_value) .' OR itemdesc IS NULL )';
+ } else {
+ die "guru meditation #00de: group_op $group_op\n";
+ }
+
+}
+
+push @where, 'cust_bill_pkg.pkgnum != 0' if $cgi->param('nottax');
+push @where, 'cust_bill_pkg.pkgnum = 0' if $cgi->param('istax');
+
+if ( $cgi->param('cust_tax') ) {
+ #false laziness -ish w/report_tax.cgi
+ my $cust_exempt;
+ if ( $cgi->param('taxname') ) {
+ my $q_taxname = dbh->quote($cgi->param('taxname'));
+ $cust_exempt =
+ "( tax = 'Y'
+ OR EXISTS ( SELECT 1 FROM cust_main_exemption
+ WHERE cust_main_exemption.custnum = cust_main.custnum
+ AND cust_main_exemption.taxname = $q_taxname )
+ )
+ ";
+ } else {
+ $cust_exempt = " tax = 'Y' ";
+ }
+
+ push @where, $cust_exempt;
+}
+
+my $count_query = "SELECT COUNT(DISTINCT billpaypkgnum),
+ SUM(cust_bill_pay_pkg.amount)";
+
+my $join_cust =
+ ' JOIN cust_bill ON ( cust_bill_pkg.invnum = cust_bill.invnum )'.
+ FS::UI::Web::join_cust_main('cust_bill', 'cust_pkg');
+
+
+my $join_pkg;
+
+my $join_cust_bill_pkg = 'LEFT JOIN cust_bill_pkg USING ( billpkgnum )';
+
+if ( $cgi->param('nottax') ) {
+
+ $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart )
+ LEFT JOIN part_pkg AS override
+ ON pkgpart_override = override.pkgpart ';
+ $join_pkg .= ' LEFT JOIN cust_location USING ( locationnum ) '
+ if $conf->exists('tax-pkg_address');
+
+} elsif ( $cgi->param('istax') ) {
+
+ #false laziness w/report_tax.cgi $taxfromwhere
+ if ( scalar( grep( /locationtaxid/, $cgi->param ) ) ||
+ $cgi->param('iscredit') eq 'rate') {
+
+ $join_pkg .=
+ ' LEFT JOIN cust_bill_pkg_tax_rate_location USING ( billpkgnum ';
+ if ( $cgi->param('iscredit') eq 'rate' ) {
+ $join_pkg .= ', billpkgtaxratelocationnum )';
+ } elsif ( $conf->exists('tax-pkg_address') ) {
+ $join_pkg .= ', billpkgtaxlocationnum )';
+ push @where, "billpkgtaxratelocationnum IS NULL";
+ } else {
+ $join_pkg .= ' )';
+ push @where, "billpkgtaxratelocationnum IS NULL";
+ }
+
+ $join_pkg .= ' 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;
+ }
+
+} else {
+
+ #warn "neither nottax nor istax parameters specified";
+ #same as before?
+ $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum )
+ LEFT JOIN part_pkg USING ( pkgpart ) ';
+
+}
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+my $join_pay = ' LEFT JOIN cust_bill_pay USING ( billpaynum )
+ LEFT JOIN cust_pay USING ( paynum ) ';
+
+$count_query .= " FROM cust_bill_pay_pkg
+ $join_cust_bill_pkg
+ $join_pkg
+ $join_pay
+ $join_cust
+ $where";
+
+my @select = ( 'cust_bill_pay_pkg.*',
+ 'cust_bill_pkg.*',
+ 'cust_pay.paid',
+ 'cust_pay.otaker',
+ 'cust_pay._date AS cust_pay_date',
+ 'cust_bill._date',
+ );
+push @select, 'part_pkg.pkg' unless $cgi->param('istax');
+push @select, 'cust_main.custnum',
+ FS::UI::Web::cust_sql_fields();
+
+my @post_desc_header = ();
+my @post_desc = ();
+my @post_desc_null = ();
+my $post_desc_align = '';
+if ( $conf->exists('enable_taxclasses') && ! $cgi->param('istax') ) {
+ push @post_desc_header, 'Tax class';
+ push @post_desc, 'taxclass';
+ push @post_desc_null, '';
+ $post_desc_align .= 'l';
+ push @select, 'part_pkg.taxclass'; # or should this use override?
+}
+
+warn "$join_cust_bill_pkg
+ $join_pkg
+ $join_pay
+ $join_cust";
+
+my $query = {
+ 'table' => 'cust_bill_pay_pkg',
+ 'addl_from' => "$join_cust_bill_pkg
+ $join_pkg
+ $join_pay
+ $join_cust",
+ 'hashref' => {},
+ 'select' => join(', ', @select ),
+ 'extra_sql' => $where,
+ 'order_by' => 'ORDER BY billpaypkgnum', #cust_bill. or cust_pay._date?
+};
+
+my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $tax_pkg_address = $conf->exists('tax-pkg_address');
+my $tax_ship_address = $conf->exists('tax-ship_address');
+
+my $location_sub = sub {
+ #my $cust_bill_pay_pkg = shift;
+ my $self = shift;
+ my $tax_Xlocation = $self->cust_bill_pkg_tax_Xlocation;
+ if ( defined($tax_Xlocation) && $tax_Xlocation ) {
+
+ if ( ref($tax_Xlocation) eq 'FS::cust_bill_pkg_tax_location' ) {
+
+ if ( $tax_Xlocation->taxtype eq 'FS::cust_main_county' ) {
+ my $cust_main_county = $tax_Xlocation->cust_main_county;
+ if ( $cust_main_county ) {
+ $cust_main_county->label;
+ } else {
+ ''; #cust_main_county record is gone... history? yuck.
+ }
+ } else {
+ '(CCH tax_rate)'; #XXX FS::tax_rate.. vendor taxes not yet handled here
+ }
+
+ } elsif ( ref($tax_Xlocation) eq 'FS::cust_bill_pkg_tax_rate_location' ) {
+ '(CCH)'; #XXX vendor taxes not yet handled here
+ } else {
+ 'unknown tax_Xlocation '. ref($tax_Xlocation);
+ }
+
+ } else {
+
+ my $cust_bill_pkg = $self->cust_bill_pkg;
+ if ( $cust_bill_pkg->pkgnum > 0 ) {
+ my $cust_pkg = $cust_bill_pkg->cust_pkg;
+ if ( $tax_pkg_address && (my $cust_location = $cust_pkg->cust_location) ){
+ $cust_location->county_state_country;
+ } else {
+ my $cust_main = $cust_pkg->cust_main;
+ if ( $tax_ship_address && $cust_main->has_ship_address ) {
+ $cust_main->county_state_country('ship_');
+ } else {
+ $cust_main->county_state_country;
+ }
+ }
+
+ } else {
+ #tax? we shouldn't have wound up here then...
+ ''; #return customer ship or bill address? (depending on tax-ship_address)
+ }
+
+ }
+
+};
+
+</%init>
diff --git a/httemplate/search/report_cdr.html b/httemplate/search/report_cdr.html
index 0eefd4ce7..ef5447838 100644
--- a/httemplate/search/report_cdr.html
+++ b/httemplate/search/report_cdr.html
@@ -188,7 +188,9 @@ die "access denied"
unless $FS::CurrentUser::CurrentUser->access_right('List rating data');
my @fields = fields('cdr');
+push @fields, 'ratename';
my $labels = FS::cdr->table_info->{'fields'};
+$labels->{ratename} = 'Rate plan';
my $conf = new FS::Conf;
my $default_phone_countrycode =
diff --git a/httemplate/search/report_cust_bill_pay_pkg.html b/httemplate/search/report_cust_bill_pay_pkg.html
new file mode 100644
index 000000000..2347bab6d
--- /dev/null
+++ b/httemplate/search/report_cust_bill_pay_pkg.html
@@ -0,0 +1,88 @@
+<& /elements/header.html, mt('Payment application report') &>
+
+<FORM ACTION="cust_bill_pay_pkg.html" METHOD="GET">
+<!--<INPUT TYPE="hidden" NAME="magic" VALUE="_date">-->
+
+<TABLE BGCOLOR="#cccccc" CELLSPACING=0>
+
+<& /elements/tr-select-user.html,
+ 'label' => emt('Employee: '),
+ 'access_user' => $access_user,
+&>
+
+<& /elements/tr-select-agent.html,
+ curr_value => scalar( $cgi->param('agentnum') ),
+ #label => emt('Line items for agent: '),
+ disable_empty => 0,
+&>
+
+<!--
+<& /elements/tr-select-cust_main-status.html,
+ label => emt('Customer status'),
+&>
+-->
+
+<!-- customer
+<& /elements/tr-select-cust_class.html,
+ 'label' => emt('Class'),
+ 'multiple' => 1,
+ 'pre_options' => [ '' => emt('(none)') ],
+ 'all_selected' => 1,
+&>
+-->
+
+<!-- some sort of label saying this is the payment date... -->
+<& /elements/tr-input-beginning_ending.html,
+ 'prefix' => 'payment',
+&>
+
+<& /elements/tr-input-lessthan_greaterthan.html,
+ label => emt('Amount'),
+ field => 'paid',
+&>
+
+<!--
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="nottax" VALUE="Y" onClick="nottax_changed(this)" onChange="nottax_change(thid)"></TD>
+ <TD><% mt('Omit taxes') |h %></TD>
+</TD>
+
+<TR>
+ <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="istax" VALUE="Y" onClick="istax_changed(this)" onChange="istax_change(thid)"></TD>
+ <TD><% mt('Taxes only') |h %></TD>
+</TD>
+
+<SCRIPT TYPE="text/javascript">
+ function nottax_changed (what) {
+ if (what.checked && what.form.istax.checked) {
+ what.form.istax.checked = false;
+ }
+ }
+ function istax_changed (what) {
+ if (what.checked && what.form.nottax.checked) {
+ what.form.nottax.checked = false;
+ }
+ }
+</SCRIPT>
+-->
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="<% mt('Get Report') |h %>">
+
+</FORM>
+
+<& /elements/footer.html &>
+<%init>
+
+#Financial reports?
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $access_user = $FS::CurrentUser::CurrentUser->access_users_hashref('table' => 'cust_pay');
+
+my $conf = new FS::Conf;
+
+</%init>
+
diff --git a/httemplate/view/Status.html b/httemplate/view/Status.html
index f1060874c..eb7df6846 100644
--- a/httemplate/view/Status.html
+++ b/httemplate/view/Status.html
@@ -93,7 +93,7 @@ if ( $db eq 'PostgreSQL' ) {
&& FS::Record->scalar_sql('SHOW archive_mode') eq 'on';
my $slave =
- FS::Record->scalar_sql('SHOW archive_command') =~ / postgres\@([\w\.\-]): /
+ FS::Record->scalar_sql('SHOW archive_command') =~ / postgres\@([\w\.\-]+): /
? $1 : '';
$status{'Replication'} = [
diff --git a/httemplate/view/cust_main/appointments.html b/httemplate/view/cust_main/appointments.html
index 9bec7b9ae..335bd208b 100644
--- a/httemplate/view/cust_main/appointments.html
+++ b/httemplate/view/cust_main/appointments.html
@@ -2,16 +2,7 @@
<A NAME="appointments"><FONT CLASS="fsinnerbox-title">Appointments</FONT></A>
% if ( $FS::CurrentUser::CurrentUser->access_right('Make appointment') ) {
-%# just a link for now, should probably be FS::TicketSystem->href_schedule
-%# maybe need to add a dropdown for type (install etc.) from a custom field
-%# or maybe change to a popup and get that info there
-% my @sched_item = qsearch('sched_item', { 'disabled' => '', });
-% my @username = map $_->access_user->username, @sched_item;
-% my $query = join('&', map "username=$_", @username).
-% '&LengthMin=180'. #XXX appointment length
-% '&custnum='. $cust_main->custnum; #.
-% #XXX '&pkgnum=$pkgnum";need to be for specific package/location, not just for a customer... default to active(/ordered) package in a pkg_class w/ticketing_queueid, otherwise, a popup? we're getting complicated like form-creat_ticket.html
-<A HREF="<%$p%>rt/Search/Schedule.html?<% $query %>">Make appointment</A>
+ <& /elements/popup_link-make_appointment.html, custnum=>$cust_main->custnum &>
% }
%# |
diff --git a/httemplate/view/cust_main/packages/services.html b/httemplate/view/cust_main/packages/services.html
index d7644b7de..39055f49a 100644
--- a/httemplate/view/cust_main/packages/services.html
+++ b/httemplate/view/cust_main/packages/services.html
@@ -10,6 +10,7 @@ function clearhint_search_cust_svc(obj, str) {
}
</SCRIPT>
+% my $showdidlink = browse_received_did_inventory($cust_pkg->custnum);
% foreach my $part_svc (
% $cust_pkg->part_svc(
% 'summarize_size' => $opt{'cust_pkg-large_pkg_size'},
@@ -97,7 +98,7 @@ function clearhint_search_cust_svc(obj, str) {
% local $opt{'num_avail'} = $part_svc->num_avail;
<BR><% svc_provision_link($cust_pkg, $part_svc, \%opt, $curuser) %>
% }
-% if (browse_received_did_inventory($cust_pkg->custnum)) {
+% if ($showdidlink) {
<BR><A HREF="<%$p%>browse/did_order.html?custnum=<%$cust_pkg->custnum%>"><% mt('Browse Received DID Inventory') |h %></A>
% }
% }
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index 690f858ad..047abda0b 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -112,7 +112,7 @@
% }
% if ( $curuser->access_right('Unsuspend customer package') ) {
% if ( $cust_pkg->order_date eq $cust_pkg->get('susp') ) { #on hold
- (&nbsp;<% pkg_link('misc/unsusp_pkg', emt('Start bililng now'), $cust_pkg) %>&nbsp;)
+ (&nbsp;<% pkg_link('misc/unsusp_pkg', emt('Start billing now'), $cust_pkg) %>&nbsp;)
% } else {
(&nbsp;<% pkg_unsuspend_link($cust_pkg) %>&nbsp;)
(&nbsp;<% pkg_resume_link($cust_pkg) %>&nbsp;)