diff options
Diffstat (limited to 'httemplate')
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/ / /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> </TD></TR>'. % '<TR><TD> </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> </TD><TD> </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> </TD><TD> </TD></TR>'; - - } - - if ( ship_changed ) { - - confirm_change = confirm_change + - '<TR><TH>Entered service address</TH>' + - '<TH>Standardized service address</TH></TR>'; - // + '<TR><TD> </TD><TD> </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> </TD><TD> </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 name<BR>(last, 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> </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> </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> </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> </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' => ' ', }, '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> </TD><TD> </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> </TD><TD> </TD></TR>'; + + } + + if ( ship_changed ) { + + confirm_change = confirm_change + + '<TR><TH>Entered service address</TH>' + + '<TH>Standardized service address</TH></TR>'; + // + '<TR><TD> </TD><TD> </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> </TD><TD> </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: + <% 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->$_. ' ' 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 = ' '; +$header .= $_. ' ' 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 -> Provisioning, services and packages -> View/Edit package definitions + <li><i>pkgpart</i>: Package definition. Configuration -> Packages -> 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 -> Packages -> Package definitions + + <li><i>discountnum</i>: Optional discount. Configuration -> Packages -> 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"> <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"> 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> </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 this 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 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> + <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 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%> | <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); |