diff options
Diffstat (limited to 'httemplate')
36 files changed, 1045 insertions, 171 deletions
diff --git a/httemplate/browse/deploy_zone.html b/httemplate/browse/deploy_zone.html index 02ebb8b8c..a1bd57f15 100644 --- a/httemplate/browse/deploy_zone.html +++ b/httemplate/browse/deploy_zone.html @@ -57,7 +57,7 @@ '(cir_speed_down, cir_speed_up)', ], links => [ $link_fixed, $link_fixed, ], - align => 'cllllrr', + align => 'cllllrrr', nohtmlheader => 1, disable_maxselect => 1, disable_total => 1, diff --git a/httemplate/browse/log_email.html b/httemplate/browse/log_email.html new file mode 100644 index 000000000..0f64dd454 --- /dev/null +++ b/httemplate/browse/log_email.html @@ -0,0 +1,92 @@ +<% include('/elements/init_overlib.html') %> +<% include('/browse/elements/browse.html', + 'title' => 'Log email condition configuration', + 'name_singular' => 'condition', + 'html_init' => '<P STYLE="margin-top: 0">' + . $add_condition_link + . ' | ' + . $system_log_link + . '</P>' + . '<SCRIPT>' + . $areyousure + . '</SCRIPT>', + 'query' => $query, + 'count_query' => $count_query, + 'header' => [ '#', + 'Context', + 'Min. Level', + 'Template', + 'To', + '', + ], + 'fields' => [ 'logemailnum', + sub { $_[0]->context || '(all)' }, + sub { $FS::Log::LEVELS[$_[0]->min_level] }, + 'msgname', + 'to_addr', + $actions, + ], + 'sort_fields' => [ 'logemailnum', + 'context', + 'min_level', + 'msgname', + 'to_addr', + '', + ], + 'links' => [ $editlink, + $editlink, + $editlink, + $editlink, + $editlink, + '', + ], + + ) %> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right([ 'View system logs', 'Configuration' ]); + +my $add_condition_link = include('/elements/popup_link.html', + 'action' => $p.'edit/log_email.html?popup=1', + 'label' => 'Add log email condition', + 'actionlabel' => 'Add log email condition', +); + +my $system_log_link = qq(<A HREF="${p}search/log.html">System Log</A>); + +my $query = { + 'table' => 'log_email', + 'select' => '*', + 'addl_from' => 'LEFT JOIN msg_template USING (msgnum)', + 'hashref' => { }, +}; + +my $count_query = "SELECT COUNT(*) FROM log_email"; + +my $actions = sub { + my $log_email = shift; + my $logemailnum = $log_email->logemailnum; + qq!<A HREF="javascript:areyousure_delete_log_email($logemailnum)">(delete)</A>!; +}; + +my $areyousure_onclick = include('/elements/popup_link_onclick.html', + 'js_action' => q(') . $p . q(misc/delete-log_email.html?logemailnum=' + logemailnum), + 'actionlabel' => 'Delete log email condition', +); + +my $areyousure = <<EOF; +function areyousure_delete_log_email(logemailnum) { + if (confirm('Are you sure you want to delete log email condition #'+logemailnum+'?')) { +${areyousure_onclick} + } +} +EOF + +my $editlink = [ $p.'edit/log_email.html?logemailnum=', 'logemailnum' ]; + +</%init> + diff --git a/httemplate/browse/part_pkg.cgi b/httemplate/browse/part_pkg.cgi index 07f104e55..1e8b51030 100755 --- a/httemplate/browse/part_pkg.cgi +++ b/httemplate/browse/part_pkg.cgi @@ -88,6 +88,14 @@ if ( $cgi->param('missing_recur_fee') ) { )"; } +if ( $cgi->param('ratenum') =~ /^(\d+)$/ ) { + push @where, "EXISTS( SELECT 1 FROM part_pkg_option + WHERE optionname LIKE '%ratenum' + AND optionvalue = '$1' + AND part_pkg_option.pkgpart = part_pkg.pkgpart + )"; +} + if ( $cgi->param('family') =~ /^(\d+)$/ ) { $family_pkgpart = $1; push @where, "family_pkgpart = $1"; diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/cust_pkg_detail.html index 5e107066d..b1e60dad5 100644 --- a/httemplate/edit/cust_pkg_detail.html +++ b/httemplate/edit/cust_pkg_detail.html @@ -46,7 +46,7 @@ <TR> <TD></TD> <TD> - <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup = "possiblyAddRow;" > + <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup="possiblyAddRow" onchange="possiblyAddRow" > </TD> </TR> @@ -88,6 +88,7 @@ detail_input.setAttribute('maxLength', 65); detail_input.setAttribute('rownum', rownum); detail_input.onkeyup = possiblyAddRow; + detail_input.onchange = possiblyAddRow; detail_cell.appendChild(detail_input); row.appendChild(detail_cell); diff --git a/httemplate/edit/cust_pkg_discount.html b/httemplate/edit/cust_pkg_discount.html index 0bb84b8f2..e1e3daede 100755 --- a/httemplate/edit/cust_pkg_discount.html +++ b/httemplate/edit/cust_pkg_discount.html @@ -1,18 +1,5 @@ -<% include('/elements/header-popup.html', "Discount Package") %> - -<SCRIPT TYPE="text/javascript"> - - function enable_discount_pkg () { - if ( document.DiscountPkgForm.discountnum.selectedIndex > 0 ) { - document.DiscountPkgForm.submit.disabled = false; - } else { - document.DiscountPkgForm.submit.disabled = true; - } - } - -</SCRIPT> - -<% include('/elements/error.html') %> +<& /elements/header-popup.html, "Discount Package" &> +<& /elements/error.html &> <FORM NAME="DiscountPkgForm" ACTION="<% $p %>edit/process/cust_pkg_discount.html" METHOD=POST> <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> @@ -26,17 +13,17 @@ </TD> </TR> -<% include('/elements/tr-select-discount.html', - 'empty_label' => ( $pkgdiscountnum ? '' : 'Select discount' ), - 'onchange' => 'enable_discount_pkg()', - 'cgi' => $cgi, - ) -%> - +<& /elements/tr-select-pkg-discount.html, + curr_value_setup => $setup_discountnum, + curr_value_recur => $recur_discountnum, + disable_setup => $disable_setup, + disable_recur => $disable_recur, +&> + </TABLE> <BR> -<INPUT NAME="submit" TYPE="submit" VALUE="Discount package" <% $pkgdiscountnum ? '' : 'DISABLED' %>> +<INPUT NAME="submit" TYPE="submit" VALUE="Discount package"> </FORM> </BODY> @@ -44,14 +31,13 @@ <%init> -#some false laziness w/misc/change_pkg.cgi - my $conf = new FS::Conf; my $curuser = $FS::CurrentUser::CurrentUser; die "access denied" - unless $curuser->access_right('Discount customer package'); + unless $curuser->access_right([ 'Discount customer package', + 'Waive setup fee']); my $pkgnum = scalar($cgi->param('pkgnum')); $pkgnum =~ /^(\d+)$/ or die "illegal pkgnum $pkgnum"; @@ -67,10 +53,30 @@ my $cust_pkg = 'extra_sql' => ' AND '. $curuser->agentnums_sql, }) or die "unknown pkgnum $pkgnum"; -#my $cust_main = $cust_pkg->cust_main -# or die "can't get cust_main record for custnum ". $cust_pkg->custnum. -# " ( pkgnum ". cust_pkg->pkgnum. ")"; - my $part_pkg = $cust_pkg->part_pkg; +my @discounts = $cust_pkg->cust_pkg_discount_active; +my ($setup_discountnum, $recur_discountnum); +foreach (@discounts) { + if ( $_->setuprecur eq 'setup') { + die "multiple setup discounts on pkg#$pkgnum" if $setup_discountnum; + $setup_discountnum = $_->discountnum; + } elsif ( $_->setuprecur eq 'recur' ) { + die "multiple setup discounts on pkg#$pkgnum" if $recur_discountnum; + $recur_discountnum = $_->discountnum; + } +} +if ( $cust_pkg->waive_setup ) { + $setup_discountnum = -2; +} + +my $disable_setup = 1; +if ( !$cust_pkg->get('setup') and $cust_pkg->base_setup > 0 ) { + $disable_setup = 0; +} +my $disable_recur = 1; +if ( $cust_pkg->base_recur > 0 ) { + $disable_recur = 0; +} + </%init> diff --git a/httemplate/edit/elements/rate_detail.html b/httemplate/edit/elements/rate_detail.html index 7b5ec314a..32dd502ce 100644 --- a/httemplate/edit/elements/rate_detail.html +++ b/httemplate/edit/elements/rate_detail.html @@ -61,18 +61,18 @@ with row headers showing the region name and prefixes. % $row++; % }# foreach @rate_region % if ( !$opt{regionnum} ) { -%# global default +% # global default for this cdrtypenum <TR> <TD COLSPAN=2 STYLE="padding-top: 10px"> <B>Global default</B> (for calls not matching any prefix) </TD> <TD STYLE="padding-top: 10px"> -% # default rate: set a null region +% # default rate: set a null region for this cdr type <B> <& .detail_box, - detail => $rate->default_detail, + detail => $rate->default_detail($cdrtypenum), ratetimenum => '', - cdrtypenum => '', + cdrtypenum => $cdrtypenum, regionnum => '', ratenum => $rate->ratenum &> diff --git a/httemplate/edit/log_email.html b/httemplate/edit/log_email.html new file mode 100644 index 000000000..bbce7c708 --- /dev/null +++ b/httemplate/edit/log_email.html @@ -0,0 +1,45 @@ +<% include( 'elements/edit.html', + 'name_singular' => 'log email condition', + 'table' => 'log_email', + 'fields' => [ + { 'field' => 'context', + 'type' => 'select', + 'options' => [ '', @contexts ], + 'labels' => { '' => '(all)', map { $_ => $_ } @contexts }, + 'curr_value' => scalar($cgi->param('context')), + }, + { 'field' => 'min_level', + 'type' => 'select', + 'options' => [ 0..7 ], + 'labels' => { map {$_ => $FS::Log::LEVELS[$_]} 0..7 }, + 'curr_value' => scalar($cgi->param('min_level')), + }, + 'to_addr', + { 'field' => 'msgnum', + 'type' => 'select-msg_template', + 'empty_label' => 'Select template', + 'required' => 1, + }, + ], + 'labels' => { + 'context' => 'Context', + 'min_level' => 'Min. Level', + 'to_addr' => 'To', + 'msgnum' => 'Message', + }, + 'viewall_dir' => 'browse', + 'popup' => $opts{'popup'}, + 'form_init' => $opts{'popup'} ? q(<INPUT TYPE="hidden" NAME="popup" VALUE="1">) : '', + ) +%> +<%once> +my @contexts = sort FS::log_context->contexts; +</%once> +<%init> + +my %opts = @_; + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]); + +</%init> diff --git a/httemplate/edit/msg_template/email.html b/httemplate/edit/msg_template/email.html index dc70ef6ec..b0c1aa31a 100644 --- a/httemplate/edit/msg_template/email.html +++ b/httemplate/edit/msg_template/email.html @@ -302,6 +302,11 @@ my %substitutions = ( '$payinfo' => 'Card/account# (masked)', '$error' => 'Decline reason', ], + 'system_log' => [ + '$logmessage' => 'Log entry message', + '$loglevel' => 'Log entry level', + '$logcontext' => 'Log entry context', + ], ); tie my %sections, 'Tie::IxHash', ( @@ -315,6 +320,7 @@ tie my %sections, 'Tie::IxHash', ( 'svc_domain'=> 'Domain service fields', 'svc_phone' => 'Phone service fields', 'svc_broadband' => 'Broadband service fields', +'system_log' => 'System log fields', ); my $widget = new HTML::Widgets::SelectLayers( diff --git a/httemplate/edit/process/cust_pkg_discount.html b/httemplate/edit/process/cust_pkg_discount.html index 4a71f6975..143611ef9 100644 --- a/httemplate/edit/process/cust_pkg_discount.html +++ b/httemplate/edit/process/cust_pkg_discount.html @@ -14,9 +14,8 @@ <%init> my $curuser = $FS::CurrentUser::CurrentUser; - -die "access denied" - unless $curuser->access_right('Discount customer package'); +my $can_discount = $curuser->access_right('Discount customer package'); +my $can_waive_setup = $curuser->access_right('Waive setup fee'); #this search is really for security wrt agent virt... #maybe move it to the cust_pkg_discount->insert call? @@ -29,20 +28,81 @@ my $cust_pkg = qsearchs({ }); die 'unknown pkgnum' unless $cust_pkg; -my $cust_pkg_discount = new FS::cust_pkg_discount { - 'pkgnum' => $cust_pkg->pkgnum, - 'discountnum' => scalar($cgi->param('discountnum')), - 'months_used' => 0, - 'end_date' => '', #XXX - #for the create a new discount case - '_type' => scalar($cgi->param('discountnum__type')), - 'amount' => scalar($cgi->param('discountnum_amount')), - 'percent' => scalar($cgi->param('discountnum_percent')), - 'months' => scalar($cgi->param('discountnum_months')), - 'setup' => scalar($cgi->param('discountnum_setup')), - #'linked' => scalar($cgi->param('discountnum_linked')), - #'disabled' => $self->discountnum_disabled, -}; -my $error = $cust_pkg_discount->insert; +my $error; +my %discountnum = (setup => '', recur => ''); +if ( $cgi->param('setup_discountnum') == -2 ) { + + die "access denied" unless $can_waive_setup; # UI protects against this + # waive setup fee (not really a discount but treated as one in the UI) + if ( !$cust_pkg->get('setup') and !$cust_pkg->waive_setup ) { + $cust_pkg->set('waive_setup' => 'Y'); + $error = $cust_pkg->replace; + } + +} else { + if ( $cgi->param('setup_discountnum') =~ /^(-?\d+)$/ ) { + $discountnum{setup} = $1; + } + if ( $cust_pkg->waive_setup ) { + $cust_pkg->set('waive_setup', ''); + $error = $cust_pkg->replace; + } +} + +if ( $cgi->param('recur_discountnum') =~ /^(-?\d+)$/ ) { + + $discountnum{recur} = $1; + +} + +my @active_discounts = $cust_pkg->cust_pkg_discount_active; + +foreach my $setuprecur (qw(setup recur)) { + + if ( $cust_pkg->get('setup') and $setuprecur eq 'setup' ) { + # no point allowing setup discounts to be edited for a previously setup + # package + next; + } + + my ($active) = grep { $_->setuprecur eq $setuprecur } @active_discounts; + + if ( $active ) { + if ( $active->discount ne $discountnum{$setuprecur} ) { + $active->set('disabled' => 'Y'); + $error ||= $active->replace; + undef $active; + } else { + # it's the same discountnum; don't touch it + next; + } + } + + if ( $discountnum{$setuprecur} ) { + die "access_denied" unless $can_discount; + my $cust_pkg_discount = FS::cust_pkg_discount->new({ + 'pkgnum' => $cust_pkg->pkgnum, + 'discountnum' => $discountnum{$setuprecur}, + 'setuprecur' => $setuprecur, + 'months_used' => 0, + 'end_date' => '', #XXX + #for the create a new discount case + '_type' => scalar($cgi->param($setuprecur.'_discountnum__type')), + 'amount' => scalar($cgi->param($setuprecur.'_discountnum_amount')), + 'percent' => scalar($cgi->param($setuprecur.'_discountnum_percent')), + }); + if ( $setuprecur eq 'setup' ) { + $cust_pkg_discount->set('setup' => 'Y'); + $cust_pkg_discount->set('months' => 1); + } else { + if ( $cgi->param($setuprecur.'_discountnum_months') =~ /^(\w+)$/ ) { + $cust_pkg_discount->set('months' => $1); + } + } + + $error ||= $cust_pkg_discount->insert; + + } +} # foreach $setuprecur </%init> diff --git a/httemplate/edit/process/elements/process.html b/httemplate/edit/process/elements/process.html index a76f4befb..fd12c61d9 100644 --- a/httemplate/edit/process/elements/process.html +++ b/httemplate/edit/process/elements/process.html @@ -164,7 +164,9 @@ process(); % # some false laziness with the above % my ($form_name, $job_fields) = @{ $opt{'progress_init'} }; <form name="<% $form_name %>"> + <input type="hidden" name="<% $pkey %>" value="<% $new->get($pkey) %>"> % foreach my $field (@$job_fields) { +% next if $field eq $pkey; <input type="hidden" name="<% $field %>" value="<% $cgi->param($field) |h %>"> % } <& /elements/progress-init.html, diff --git a/httemplate/edit/process/log_email.html b/httemplate/edit/process/log_email.html new file mode 100644 index 000000000..769e180a8 --- /dev/null +++ b/httemplate/edit/process/log_email.html @@ -0,0 +1,18 @@ +<% include('elements/process.html', + 'table' => 'log_email', + %processopts + ) %> +<%init> + +my %opts = @_; + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right([ 'View system logs', 'Configuration' ]); + +my %processopts = $opts{'popup'} + ? ( 'popup_reload' => 'Logging email added' ) + : ( 'redirect' => $fsurl.'browse/log_email.html?' ); # id will be needlessly appended, should be harmless + +</%init> diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi index f1d8c2696..60352154a 100644 --- a/httemplate/edit/process/quick-cust_pkg.cgi +++ b/httemplate/edit/process/quick-cust_pkg.cgi @@ -79,9 +79,6 @@ my $contactnum = $1; $cgi->param('locationnum') =~ /^(\-?\d*)$/ or die 'illegal locationnum '. $cgi->param('locationnum'); my $locationnum = $1; -$cgi->param('discountnum') =~ /^(\-?\d*)$/ - or die 'illegal discountnum '. $cgi->param('discountnum'); -my $discountnum = $1; # for going right to a provision service after ordering a package my( $svcpart, $part_svc ) = ( '', '' ); @@ -114,19 +111,29 @@ my %hash = ( 'refnum' => $refnum, 'contactnum' => $contactnum, 'locationnum' => $locationnum, - 'discountnum' => $discountnum, - #for the create a new discount case - 'discountnum__type' => scalar($cgi->param('discountnum__type')), - 'discountnum_amount' => scalar($cgi->param('discountnum_amount')), - 'discountnum_percent' => scalar($cgi->param('discountnum_percent')), - 'discountnum_months' => scalar($cgi->param('discountnum_months')), - 'discountnum_setup' => scalar($cgi->param('discountnum_setup')), 'contract_end' => ( scalar($cgi->param('contract_end')) ? parse_datetime($cgi->param('contract_end')) : '' ), - 'waive_setup' => ( $cgi->param('waive_setup') eq 'Y' ? 'Y' : '' ), ); + +if ( $cgi->param('setup_discountnum') =~ /^(-?\d+)$/ ) { + if ( $1 == -2 ) { + $hash{waive_setup} = 'Y'; + } else { + $hash{setup_discountnum} = $1; + $hash{setup_discountnum_amount} = $cgi->param('setup_discountnum_amount'); + $hash{setup_discountnum_percent} = $cgi->param('setup_discountnum_percent'); + } +} + +if ( $cgi->param('recur_discountnum') =~ /^(-?\d+)$/ ) { + $hash{recur_discountnum} = $1; + $hash{recur_discountnum_amount} = $cgi->param('recur_discountnum_amount'); + $hash{recur_discountnum_percent} = $cgi->param('recur_discountnum_percent'); + $hash{recur_discountnum_months} = $cgi->param('recur_discountnum_months'); +} + $hash{'custnum'} = $cust_main->custnum if $cust_main; if ( $cgi->param('start') eq 'on_hold' ) { diff --git a/httemplate/edit/process/quotation_pkg_detail.html b/httemplate/edit/process/quotation_pkg_detail.html new file mode 100644 index 000000000..2fc420280 --- /dev/null +++ b/httemplate/edit/process/quotation_pkg_detail.html @@ -0,0 +1,45 @@ +% if ( $error ) { +<% header('Error') %> +<FONT COLOR="#ff0000"><B><% $error |h %></B></FONT><BR><BR> +<CENTER><INPUT TYPE="BUTTON" VALUE="OK" onClick="parent.cClick()"></CENTER> +</BODY></HTML> +% } else { +<% header($action) %> + <SCRIPT TYPE="text/javascript"> + window.top.location.reload(); + </SCRIPT> + </BODY></HTML> +% } +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Generate quotation'); + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum'; +my $pkgnum = $1; + +my $quotation_pkg = qsearchs({ + 'table' => 'quotation_pkg', + 'addl_from' => 'LEFT JOIN quotation USING ( quotationnum )'. + 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'quotationpkgnum' => $pkgnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); + +my @orig_details = $quotation_pkg->details(); + +my $action = 'Quotation details'. + ( scalar(@orig_details) ? ' changed ' : ' added ' ); + +my $param = $cgi->Vars; +my @details = (); +for ( my $row = 0; exists($param->{"detail$row"}); $row++ ) { + push @details, $param->{"detail$row"} + if $param->{"detail$row"} =~ /\S/; +} + +my $error = $quotation_pkg->set_details(@details); + +</%init> diff --git a/httemplate/edit/process/rate_detail.html b/httemplate/edit/process/rate_detail.html index f8a744418..4020ce9e2 100644 --- a/httemplate/edit/process/rate_detail.html +++ b/httemplate/edit/process/rate_detail.html @@ -2,7 +2,7 @@ 'table' => 'rate_detail', 'popup_reload' => 'Rate changed', #a popup "parent reload" for now #someday change the individual element and go away instead - 'noerror_callback' => $set_default_detail + #'noerror_callback' => $set_default_detail &> <%init> @@ -12,19 +12,11 @@ die "access denied" my $set_default_detail = sub { my ($cgi, $rate_detail) = @_; - if (!$rate_detail->dest_regionnum) { + if (!$rate_detail->dest_regionnum and !$rate_detail->cdrtypenum) { # then this is a global default rate + # default_detailnum is no longer used, but maintain it anyway (and point + # it at the one with null cdrtypenum) my $rate = $rate_detail->rate; - if ($rate->default_detailnum) { - if ($rate->default_detailnum == $rate_detail->ratedetailnum) { - return; - } else { - # there's somehow an existing default rate. remove it. - my $old_default = $rate->default_detail; - my $error = $old_default->delete; - die "$error (removing old default rate)\n" if $error; - } - } $rate->set('default_detailnum' => $rate_detail->ratedetailnum); my $error = $rate->replace; die "$error (setting default rate)\n" if $error; diff --git a/httemplate/edit/quotation_pkg_detail.html b/httemplate/edit/quotation_pkg_detail.html new file mode 100644 index 000000000..b8f589a9a --- /dev/null +++ b/httemplate/edit/quotation_pkg_detail.html @@ -0,0 +1,116 @@ +<% include("/elements/header-popup.html", $title, '', + ( $cgi->param('error') ? '' : 'onload="addRow()"' ), + ) +%> + +%# <% include('/elements/error.html') %> + +<FORM ACTION="process/quotation_pkg_detail.html" NAME="DetailForm" ID="DetailForm" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>"> + +<TABLE ID="DetailTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=1 STYLE="background-color: #cccccc"> + + <TR> + <TD ALIGN="right">Package</TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD> + </TR> + + <TR> + <TD ALIGN="right">Comment</TD> + <TD BGCOLOR="#ffffff"><% $part_pkg->comment |h %></TD> + </TR> + + <TR> + <TD COLSPAN=2>Detail: </TD> + </TR> + +% my $row = 0; +% for ( @details ) { + + <TR> + <TD></TD> + <TD> + <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_ |h %>" rownum="<% $row++ %>" onkeyup="possiblyAddRow" onchange="possiblyAddrow"> + </TD> + </TR> + +% } + +</TABLE> + +<BR> +<INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="<% $title %>"> + +</FORM> + +<SCRIPT TYPE="text/javascript"> +% # abject false laziness with edit/cust_pkg_detail.html + + var rownum = <% $row %>; + + function possiblyAddRow() { + if ( ( rownum - this.getAttribute('rownum') ) == 1 ) { + addRow(); + } + } + + function addRow() { + + var table = document.getElementById('DetailTable'); + var tablebody = table.getElementsByTagName('tbody').item(0); + + var row = document.createElement('TR'); + + var empty_cell = document.createElement('TD'); + row.appendChild(empty_cell); + + var detail_cell = document.createElement('TD'); + + var detail_input = document.createElement('INPUT'); + detail_input.setAttribute('name', 'detail'+rownum); + detail_input.setAttribute('id', 'detail'+rownum); + detail_input.setAttribute('size', 60); + detail_input.setAttribute('maxLength', 65); + detail_input.setAttribute('rownum', rownum); + detail_input.onkeyup = possiblyAddRow; + detail_input.onchange = possiblyAddRow; + detail_cell.appendChild(detail_input); + + row.appendChild(detail_cell); + + tablebody.appendChild(row); + + rownum++; + + } + +</SCRIPT> + +</BODY> +</HTML> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right('Generate quotation'); + +$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum'; +my $pkgnum = $1; + +my $quotation_pkg = qsearchs({ + 'table' => 'quotation_pkg', + 'addl_from' => 'LEFT JOIN quotation USING ( quotationnum )'. + 'LEFT JOIN cust_main USING ( custnum )', + 'hashref' => { 'quotationpkgnum' => $pkgnum }, + 'extra_sql' => ' AND '. $curuser->agentnums_sql, +}); + +my $part_pkg = $quotation_pkg->part_pkg; + +my @details = $quotation_pkg->details; + +my $title = ( scalar(@details) ? 'Edit ' : 'Add ' ). 'Quotation Details'; + +</%init> diff --git a/httemplate/edit/rate.cgi b/httemplate/edit/rate.cgi index 1b052d62d..5bfc108c1 100644 --- a/httemplate/edit/rate.cgi +++ b/httemplate/edit/rate.cgi @@ -1,7 +1,11 @@ -<% include("/elements/header.html","$action Rate plan", menubar( +<& /elements/header.html, + "$action Rate plan", + menubar( 'View all rate plans' => "${p}browse/rate.cgi", - )) -%> + 'View packages that use this plan' => "${p}browse/part_pkg.cgi?ratenum=" + . $rate->ratenum, + ) +&> <% include('/elements/progress-init.html', 'OneTrueForm', diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index ea6933198..dcc02c2b1 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -787,6 +787,10 @@ $config_misc{'Inventory classes and inventory'} = [ $fsurl.'browse/inventory_cla $config_misc{'Upload targets'} = [ $fsurl.'browse/upload_target.html', 'Billing and payment upload destinations' ] if $curuser->access_right('Configuration'); +$config_misc{'System log emails'} = [ $fsurl.'browse/log_email.html', 'Configure conditions for sending email when logging' ] + if $curuser->access_right('View system logs') + || $curuser->access_right('Configuration'); + tie my %config_menu, 'Tie::IxHash'; if ( $curuser->access_right('Configuration' ) ) { %config_menu = ( diff --git a/httemplate/elements/popup_link.html b/httemplate/elements/popup_link.html index 2b6b187e9..e9728ac1c 100644 --- a/httemplate/elements/popup_link.html +++ b/httemplate/elements/popup_link.html @@ -18,11 +18,13 @@ Example: 'height' => 336, 'color' => '#ff0000', 'closetext' => 'Go Away', # the value '' removes the link + 'title' => 'Hover Text', #uncommon opt 'aname' => "target", # link NAME= value, useful for #targets 'target' => '_parent', 'style' => 'css-attribute:value', + 'html_label' => '<IMG SRC="something.png">', # overrides label } &> @@ -30,6 +32,7 @@ Example: % if ($params->{'action'} && $label) { <A HREF="javascript:void(0);" onClick="<% $onclick |n %>" + <% $params->{'title'} ? 'TITLE="' . $params->{'title'}. '"' : '' |n %> <% $params->{'aname'} ? 'NAME="'. $params->{'aname'}. '"' : '' |n %> <% $params->{'target'} ? 'TARGET="'. $params->{'target'}. '"' : '' |n %> <% $params->{'style'} ? 'STYLE="'. $params->{'style'}. '"' : '' |n %> @@ -48,6 +51,7 @@ if (ref($_[0]) eq 'HASH') { my $label = $params->{'label'}; $label =~ s/ / /g; +$label = $params->{'html_label'} || $label; my $onclick = include('/elements/popup_link_onclick.html', $params); </%init> diff --git a/httemplate/elements/select-months.html b/httemplate/elements/select-months.html new file mode 100644 index 000000000..1cd72fcba --- /dev/null +++ b/httemplate/elements/select-months.html @@ -0,0 +1,11 @@ +<%init> +my %opt = @_; +$opt{id} ||= $opt{field}; # should be the default everywhere +my $max = $opt{max} || 36; +$opt{options} = [ '', 1 .. $max ]; +$opt{labels} = { '' => '', + map { $_ => emt('[quant,_1,month]', $_) } 1 .. $max + }; + +</%init> +<& select.html, %opt &> diff --git a/httemplate/elements/tr-select-months.html b/httemplate/elements/tr-select-months.html index b90ce1ed7..4d8576411 100644 --- a/httemplate/elements/tr-select-months.html +++ b/httemplate/elements/tr-select-months.html @@ -1,11 +1,5 @@ -<%init> -my %opt = @_; -$opt{id} ||= $opt{field}; # should be the default everywhere -my $max = $opt{max} || 36; -$opt{options} = [ '', 1 .. $max ]; -$opt{labels} = { '' => '', - map { $_ => emt('[quant,_1,month]', $_) } 1 .. $max - }; - -</%init> -<& tr-select.html, %opt &> +<& tr-td-label.html, @_ &> + <td> + <& select-months.html, @_ &> + </td> +</tr> diff --git a/httemplate/elements/tr-select-msg_template.html b/httemplate/elements/tr-select-msg_template.html new file mode 100644 index 000000000..1f899e01c --- /dev/null +++ b/httemplate/elements/tr-select-msg_template.html @@ -0,0 +1,12 @@ +<% include('/elements/tr-td-label.html', + 'label' => $opt{'label'} || 'Message template: ', + 'required' => $opt{'required'} ) %> + <TD><% include('select-msg_template.html', %opt) %></TD> +</TR> + +<%init> + +my %opt = @_; + +</%init> + diff --git a/httemplate/elements/tr-select-pkg-discount.html b/httemplate/elements/tr-select-pkg-discount.html new file mode 100644 index 000000000..dc38cff3e --- /dev/null +++ b/httemplate/elements/tr-select-pkg-discount.html @@ -0,0 +1,196 @@ +<%doc> + +In order_pkg.html or similar: + +<& /elements/tr-select-pkg-discount.html, + curr_value_setup => ($cgi->param('setup_discountnum') || ''), + curr_value_recur => ($cgi->param('recur_discountnum') || ''), + disable_setup => 0, + disable_recur => 0, +&> + +This provides the following: +- If the user can waive setup fees or apply a discount, they get a + select box for the setup discount, with "Waive setup fee" as an option. +- If they can custom discount, they will also get "Custom discount" as an + option. If selected, this will show fields to enter the custom discount + amount/percentage. +- If they can waive setup fees but NOT apply a discount, they only get a + checkbox to waive setup fee. +- Same for recurring fee, but without the "waive setup fee" stuff, obviously. +- Custom recurring discounts also have an option for a duration in months. + +"disable_setup" locks the setup discount, but will still show a static +description if curr_value_setup is set. Likewise "disable_recur". + +</%doc> +% # SETUP DISCOUNT + +% # select-discount knows about the "custom discount" ACL +% if ( $curuser->access_right('Discount customer package') +% and !$opt{disable_setup} ) +% { +% my $pre_options = [ '' => '(none)' ]; +% if ( $curuser->access_right('Waive setup fee') ) { +% push @$pre_options, -2 => 'Waive setup fee'; +% } +<& tr-td-label.html, label => emt('Setup fee') &> + <td> + <& select-discount.html, + field => 'setup_discountnum', + id => 'setup_discountnum', + hashref => { disabled => '', + setup => 'Y' + }, + extra_sql => ' AND (percent > 0 OR months = 1)', + curr_value => $opt{'curr_value_setup'}, + disable_empty => 1, + pre_options => $pre_options, + &> + </td> +</tr> +% # custom discount +<tr class="setup_discount_custom"> + <td></td> + <td>Amount <% $money_char %> + <& input-text.html, + field => 'setup_discountnum_amount', + curr_value => ($cgi->param('setup_discountnum_amount') || ''), + size => 5, + &> + or percentage + <& input-text.html, + field => 'setup_discountnum_percent', + curr_value => ($cgi->param('setup_discountnum_percent') || ''), + size => 5, + &> % + </td> +</tr> + +% } elsif ( $curuser->access_right('Waive setup fee') +% and !$opt{disable_setup} ) +% { + +<& tr-td-label.html, label => emt('Waive setup fee') &> + <td> + <& checkbox.html, + field => 'setup_discountnum', + id => 'setup_discountnum', + value => '-2', + curr_value => $opt{'curr_value_setup'}, + &> + </td> +</tr> + +% } elsif ( $opt{'curr_value_setup'} ) { # user can't do anything +% +% my $discount = FS::discount->by_key($opt{'curr_value_setup'}); + + <INPUT TYPE="hidden" NAME="setup_discountnum" VALUE="<% $opt{curr_value_setup} %>"> + + <% $discount->description_short %> + +% } + +% # RECUR DISCOUNT + +% if ( $curuser->access_right('Discount customer package') +% and !$opt{disable_recur} ) { + +<& tr-td-label.html, label => emt('Recurring fee') &> + <td> + <& select-discount.html, + field => 'recur_discountnum', + id => 'recur_discountnum', + hashref => { disabled => '' }, + curr_value => $opt{'curr_value_recur'}, + &> + + </td> +</tr> +% # custom discount +<tr class="recur_discount_custom"> + <td></td> + <td>Amount <% $money_char %> + <& input-text.html, + field => 'recur_discountnum_amount', + curr_value => ($cgi->param('recur_discountnum_amount') || ''), + size => 5, + &> + or percentage + <& input-text.html, + field => 'recur_discountnum_percent', + curr_value => ($cgi->param('recur_discountnum_percent') || ''), + size => 5, + &> % + </td> +</tr> +<tr class="recur_discount_custom"> + <td></td> + <td>Expires after + <& /elements/select-months.html, + field => 'recur_discountnum_months', + curr_value => ($cgi->param('recur_discountnum_months') || ''), + &> + </td> +</tr> + +% } elsif ( $opt{'curr_value_recur'} ) { +% +% my $discount = FS::discount->by_key($opt{'curr_value_recur'}); + + <INPUT TYPE="hidden" NAME="recur_discountnum" VALUE="<% $opt{curr_value_recur} %>"> + + <% $discount->description %> + +% } + +<SCRIPT TYPE="text/javascript"> +$(document).ready(function() { + ['setup', 'recur'].forEach(function(x) { + var discountnum = $('#'+x+'_discountnum'); + + // if it's been set to a custom discount, show custom discount inputs + var discountnum_changed = function() { + var val = this.value; + var custom = $('.'+x+'_discount_custom'); + if ( val == -1 ) { + custom.show(); + } else { + custom.hide(); + } + }; + + discountnum.on('change', discountnum_changed); + discountnum.trigger('change'); + + // if amount contains a value, disable percent, and vice versa + var amount_percent_keyup = function(event) { + var other = event.data; + if (this.value.length > 0) { + other.disabled = true; + } else { + other.disabled = false; + } + }; + var amount = $('#'+x+'_discountnum_amount'); + var percent = $('#'+x+'_discountnum_percent'); + amount.on('keyup', percent, amount_percent_keyup); + percent.on('keyup', amount, amount_percent_keyup); + + amount.trigger('keyup'); + percent.trigger('keyup'); + }); +}); +</script> +<%init> + +my %opt = ( + 'curr_value_setup' => ($cgi->param('setup_discountnum') || ''), + 'curr_value_recur' => ($cgi->param('recur_discountnum') || ''), + @_ +); +my $curuser = $FS::CurrentUser::CurrentUser; +my $money_char = FS::Conf->new->config('money_char') || '$'; + +</%init> diff --git a/httemplate/images/Actions-document-edit-icon.png b/httemplate/images/Actions-document-edit-icon.png Binary files differnew file mode 100644 index 000000000..8bfc32943 --- /dev/null +++ b/httemplate/images/Actions-document-edit-icon.png diff --git a/httemplate/misc/delete-log_email.html b/httemplate/misc/delete-log_email.html new file mode 100644 index 000000000..cc17b15a0 --- /dev/null +++ b/httemplate/misc/delete-log_email.html @@ -0,0 +1,20 @@ +% if ($error) { +<P STYLE="color: red"><% $error %></P> +% } else { +<H1>Log email condition deleted</H1> +<SCRIPT> +window.top.location.reload(); +</SCRIPT> +% } + +<%init> +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right([ 'View system logs', 'Configuration' ]); + +my $logemailnum = $cgi->param('logemailnum'); +$logemailnum =~ /^\d+$/ or die "bad logemailnum '$logemailnum'"; +my $log_email = FS::log_email->by_key($logemailnum) + or die "logemailnum '$logemailnum' not found"; +my $error = $log_email->delete; +</%init> + diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html index cb2bd4832..e28250110 100644 --- a/httemplate/misc/order_pkg.html +++ b/httemplate/misc/order_pkg.html @@ -127,22 +127,7 @@ % if ( $discount_cust_pkg || $waive_setup_fee ) { <FONT CLASS="fsinnerbox-title"><% mt('Discounting') |h %></FONT> <% ntable("#cccccc") %> - -% if ( $waive_setup_fee ) { - <TR> - <TH ALIGN="right"><% mt('Waive setup fee') |h %> </TH> - <TD COLSPAN=6><INPUT TYPE="checkbox" NAME="waive_setup" VALUE="Y"></TD> - </TR> -% } - -% if ( $discount_cust_pkg ) { - <& /elements/tr-select-discount.html, - 'element_etc' => 'DISABLED', - 'colspan' => 7, - 'cgi' => $cgi, - &> -% } - + <& /elements/tr-select-pkg-discount.html &> </TABLE><BR> % } diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi index 278382feb..ab5aad776 100644 --- a/httemplate/search/cust_bill_pkg.cgi +++ b/httemplate/search/cust_bill_pkg.cgi @@ -705,9 +705,19 @@ my $pay_sub = "SELECT SUM(cust_bill_pay_pkg.amount) "; push @select, "($pay_sub) AS pay_amount"; -#total credits -my $credit_sub = 'SELECT SUM(amount) AS credit_amount, billpkgnum - FROM cust_credit_bill_pkg GROUP BY billpkgnum'; +# showing credited amount, optionally with date filtering +my $credit_where = ''; +if ( $cgi->param('credit_begin') or $cgi->param('credit_end') ) { + my($cr_begin, $cr_end) = FS::UI::Web::parse_beginning_ending($cgi, 'credit'); + $credit_where = "WHERE cust_credit_bill._date >= $cr_begin " . + "AND cust_credit_bill._date <= $cr_end"; +} + +my $credit_sub = "SELECT SUM(cust_credit_bill_pkg.amount) AS credit_amount, billpkgnum + FROM cust_credit_bill_pkg + JOIN cust_credit_bill USING (creditbillnum) + $credit_where + GROUP BY billpkgnum"; $join_pkg .= " LEFT JOIN ($credit_sub) AS item_credit ON (cust_bill_pkg.billpkgnum = item_credit.billpkgnum)"; @@ -737,6 +747,10 @@ if ( $cgi->param('salesnum') =~ /^(\d+)$/ ) { $cgi->param('classnum', 0) unless $cgi->param('classnum'); } +#credit flag (include only those that have credit(s) applied) +if ( $cgi->param('credit') ) { + push @where, 'credit_amount > 0'; +} my $where = join(' AND ', @where); $where &&= "WHERE $where"; @@ -775,7 +789,13 @@ my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ]; my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ]; my $pay_link = ''; #[, 'billpkgnum', ]; -my $credit_link = [ "${p}search/cust_credit_bill_pkg.html?billpkgnum=", 'billpkgnum', ]; +my $credit_param = ''; +foreach ('credit_begin', 'credit_end') { + if ( $cgi->param($_) ) { + $credit_param .= "$_=" . $cgi->param($_) . ';'; + } +} +my $credit_link = [ "${p}search/cust_credit_bill_pkg.html?${credit_param}billpkgnum=", 'billpkgnum', ]; warn "\n\nQUERY:\n".Dumper($query)."\n\nCOUNT_QUERY:\n$count_query\n\n" if $cgi->param('debug'); diff --git a/httemplate/search/cust_bill_pkg_discount.html b/httemplate/search/cust_bill_pkg_discount.html index 6da5787a0..c63482827 100644 --- a/httemplate/search/cust_bill_pkg_discount.html +++ b/httemplate/search/cust_bill_pkg_discount.html @@ -20,9 +20,7 @@ sub { $_[0]->cust_pkg_discount->discount->description }, sub { $_[0]->cust_pkg_discount->discount->classname }, sub { sprintf($money_char.'%.2f', shift->amount ) }, - sub { my $m = shift->months; - $m =~ /\./ ? sprintf('%.2f', $m) : $m; - }, + $months_sub, 'pkg',#sub { $_[0]->cust_bill_pkg->cust_pkg->part_pkg->pkg }, 'invnum', sub { time2str('%b %d %Y', shift->_date ) }, @@ -218,4 +216,11 @@ my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ]; my $conf = new FS::Conf; my $money_char = $conf->config('money_char') || '$'; +my $months_sub = sub { + my $cust_bill_pkg_discount = shift; + return 'Setup' + if $cust_bill_pkg_discount->cust_pkg_discount->setuprecur eq 'setup'; + sprintf('%.2f', $cust_bill_pkg_discount->months); +}; + </%init> diff --git a/httemplate/search/cust_credit_bill_pkg.html b/httemplate/search/cust_credit_bill_pkg.html index b5a0ee9f6..5facd4ab3 100644 --- a/httemplate/search/cust_credit_bill_pkg.html +++ b/httemplate/search/cust_credit_bill_pkg.html @@ -3,11 +3,12 @@ 'name_singular' => 'credit application', 'query' => $query, 'count_query' => $count_query, - 'count_addl' => [ $money_char. '%.2f total', ], + 'count_addl' => \@count_addl, 'header' => [ #'#', 'Amount', + 'Tax exempt', #credit 'Date', @@ -26,7 +27,9 @@ ], 'fields' => [ #'creditbillpkgnum', - sub { sprintf($money_char.'%.2f', shift->amount ) }, + sub { sprintf($money_char.'%.2f', shift->amount ) }, + + sub { sprintf($money_char.'%.2f', shift->get('exempt_credited') ) }, sub { time2str('%b %d %Y', shift->get('cust_credit_date') ) }, sub { shift->cust_credit_bill->cust_credit->otaker }, @@ -44,6 +47,7 @@ ], 'sort_fields' => [ 'amount', + 'exempt_credited', 'cust_credit_date', '', #'otaker', '', #reason @@ -61,6 +65,7 @@ '', '', '', + '', @post_desc_null, $ilink, $ilink, @@ -68,11 +73,12 @@ FS::UI::Web::cust_header() ), ], - 'align' => 'rrllll'. + 'align' => 'rrrllll'. $post_desc_align. 'rr'. FS::UI::Web::cust_aligns(), - 'color' => [ + 'color' => [ + '', '', '', '', @@ -91,6 +97,7 @@ '', '', '', + '', @post_desc_null, '', '', @@ -286,7 +293,6 @@ if ( $cgi->param('out') ) { #} push @where, $loc_sql; -warn $loc_sql; } my($title, $name); @@ -369,7 +375,7 @@ if ( $cgi->param('report_group') =~ /^(=|!=) (.*)$/ && $cgi->param('istax') ) { } -push @where, 'cust_bill_pkg.pkgnum != 0' if $cgi->param('nottax'); +push @where, '(cust_bill_pkg.pkgnum != 0 OR cust_bill_pkg.feepart is not null)' if $cgi->param('nottax'); push @where, 'cust_bill_pkg.pkgnum = 0' if $cgi->param('istax'); if ( $cgi->param('cust_tax') ) { @@ -393,6 +399,9 @@ if ( $cgi->param('cust_tax') ) { my $count_query = "SELECT COUNT(DISTINCT creditbillpkgnum), SUM(cust_credit_bill_pkg.amount)"; +if ( $cgi->param('nottax') ) { + $count_query .= ", SUM(exempt_credited)"; +} my $join_cust = ' JOIN cust_bill ON ( cust_bill_pkg.invnum = cust_bill.invnum )'. @@ -405,6 +414,21 @@ my $join_cust_bill_pkg = 'LEFT JOIN cust_bill_pkg USING ( billpkgnum )'; if ( $cgi->param('nottax') ) { + # There can be multiple cust_tax_exempt_pkg records with the same + # creditbillpkgnum iff the line item is exempt from multiple taxes. + # They will all have the same amount, except in the case where there are + # different exemption types and so the exemption amounts are different. + # In that case, show the amount of the largest exemption. + + $join_cust_bill_pkg .= ' + LEFT JOIN( + SELECT creditbillpkgnum, + MAX(0 - cust_tax_exempt_pkg.amount) AS exempt_credited + FROM cust_tax_exempt_pkg + WHERE creditbillpkgnum IS NOT NULL + GROUP BY creditbillpkgnum + ) AS exempt_credit USING (creditbillpkgnum) + '; $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum ) LEFT JOIN part_pkg USING ( pkgpart ) LEFT JOIN part_pkg AS override @@ -472,6 +496,12 @@ push @select, 'part_pkg.pkg' unless $cgi->param('istax'); push @select, 'cust_main.custnum', FS::UI::Web::cust_sql_fields(); +if ( $cgi->param('istax') ) { + push @select, 'NULL AS exempt_credited'; # just display zero +} elsif ( $cgi->param('nottax') ) { + push @select, 'exempt_credited'; +} + my @post_desc_header = (); my @post_desc = (); my @post_desc_null = (); @@ -555,4 +585,13 @@ my $location_sub = sub { }; +my @count_addl = ( $money_char. '%.2f total', ); +if ( $cgi->param('nottax') ) { + push @count_addl, ( $money_char. '%.2f tax exempt' ); +} + +if ( $cgi->param('debug') ) { + warn "\nQUERY:\n" . Dumper($query) . "\nCOUNT_QUERY:\n$count_query\n\n"; +} + </%init> diff --git a/httemplate/search/cust_pkg_discount.html b/httemplate/search/cust_pkg_discount.html index f0c7447a5..ab6ad2bd0 100644 --- a/httemplate/search/cust_pkg_discount.html +++ b/httemplate/search/cust_pkg_discount.html @@ -18,9 +18,7 @@ sub { ucfirst( shift->status ) }, sub { shift->discount->description }, sub { shift->discount->classname }, - sub { my $m = shift->months_used; - $m =~ /\./ ? sprintf('%.2f',$m) : $m; - }, + $months_used_sub, 'otaker', 'pkg', \&FS::UI::Web::cust_fields, @@ -165,4 +163,9 @@ my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ]; my $conf = new FS::Conf; +my $months_used_sub = sub { + my $cust_pkg_discount = shift; + return 'Setup only' if $cust_pkg_discount->setuprecur eq 'setup'; + return sprintf('%.2f', $cust_pkg_discount->months_used); +}; </%init> diff --git a/httemplate/search/log.html b/httemplate/search/log.html index d1bfb6cc9..a707928d7 100644 --- a/httemplate/search/log.html +++ b/httemplate/search/log.html @@ -1,6 +1,7 @@ <& elements/search.html, 'title' => 'System Log', 'name_singular' => 'event', + 'menubar' => \@menubar, 'html_init' => include('.head'), 'query' => $query, 'count_query' => $count_query, @@ -204,6 +205,9 @@ my $curuser = $FS::CurrentUser::CurrentUser; die "access denied" unless $curuser->access_right([ 'View system logs', 'Configuration' ]); +my @menubar = (); +push @menubar, qq(<A HREF="${fsurl}browse/log_email.html" STYLE="text-decoration: underline;">Configure conditions for sending email when logging</A>), + $cgi->param('min_level', 0) unless defined($cgi->param('min_level')); $cgi->param('max_level', 7) unless defined($cgi->param('max_level')); diff --git a/httemplate/search/report_tax-xls.cgi b/httemplate/search/report_tax-xls.cgi index 743f14788..773b403f1 100755 --- a/httemplate/search/report_tax-xls.cgi +++ b/httemplate/search/report_tax-xls.cgi @@ -122,7 +122,7 @@ my %default = ( border => 1, ); my @widths = ( #ick - 30, (13) x 5, 3, 7.5, 3, 11, 11, 3, 11, 3, 11 + 30, (13) x 6, 3, 7.5, 3, 11, 11, 3, 11, 3, 11 ); my @format = ( {}, {}, {} ); # white row, gray row, yellow (totals) row @@ -134,29 +134,34 @@ foreach (keys(%formatdef)) { italic => 1, %f); } -my $ws = $workbook->add_worksheet('taxreport'); +my $ws = $workbook->add_worksheet('Sales and Tax'); # main title $ws->merge_range(0, 0, 0, 14, $report->title, $format[0]->{title}); +$ws->set_row(0, 30); # excel position my $x = 0; my $y = 2; my $colhead = $format[0]->{colhead}; # print header -$ws->merge_range($y, 1, $y, 5, 'Sales', $colhead); -$ws->merge_range($y, 6, $y+1, 8, 'Rate', $colhead); -$ws->merge_range($y, 9, $y, 15, 'Tax', $colhead); +$ws->merge_range($y, 1, $y, 6, 'Sales', $colhead); +$ws->merge_range($y, 7, $y+1, 9, 'Rate', $colhead); +$ws->merge_range($y, 10, $y, 16, 'Tax', $colhead); $y++; $colhead = $format[0]->{colhead_small}; -$ws->write($y, 1, [ 'Total', 'Exempt customer', 'Exempt package', 'Monthly exemption', +$ws->write($y, 1, [ 'Total', + 'Exempt customer', + 'Exempt package', + 'Monthly exemption', + 'Credited', 'Taxable' ], $colhead); -$ws->write($y, 9, 'Estimated', $colhead); -$ws->write($y, 10, 'Invoiced', $colhead); -$ws->write($y, 12, 'Credited', $colhead); -$ws->write($y, 14, 'Net due', $colhead); -$ws->write($y, 15, 'Collected',$colhead); +$ws->write($y, 10, 'Estimated', $colhead); +$ws->write($y, 11, 'Invoiced', $colhead); +$ws->write($y, 13, 'Credited', $colhead); +$ws->write($y, 15, 'Net due', $colhead); +$ws->write($y, 16, 'Collected',$colhead); $y++; # print data @@ -168,7 +173,7 @@ foreach my $row (@rows) { if ( $row->{pkgclass} ne $prev_row->{pkgclass} ) { $rownum = 1; if ( $params{breakdown}->{pkgclass} ) { - $ws->merge_range($y, 0, $y, 14, + $ws->merge_range($y, 0, $y, 15, $pkgclass_name{$row->{pkgclass}}, $format[0]->{sectionhead} ); @@ -182,7 +187,7 @@ foreach my $row (@rows) { } $ws->write($y, $x, $row->{label}, $f->{rowhead}); $x++; - foreach (qw(sales exempt_cust exempt_pkg exempt_monthly taxable)) { + foreach (qw(sales exempt_cust exempt_pkg exempt_monthly sales_credited taxable)) { $ws->write($y, $x, $row->{$_} || 0, $f->{currency}); $x++; } @@ -229,6 +234,69 @@ for my $x (0..scalar(@widths)-1) { $ws->set_column($x, $x, $widths[$x]); } +# do the same for the credit worksheet +$ws = $workbook->add_worksheet('Credits'); + +my $title = $report->title; +$title =~ s/Tax Report/Credits/; +# main title +$ws->merge_range(0, 0, 0, 14, $title, $format[0]->{title}); +$ws->set_row(0, 30); # height +# excel position +$x = 0; +$y = 2; + +$colhead = $format[0]->{colhead}; +# print header +$ws->merge_range($y, 1, $y+1, 1, 'Total', $colhead); +$ws->merge_range($y, 2, $y, 4, 'Applied to', $colhead); + +$y++; +$colhead = $format[0]->{colhead_small}; +$ws->write($y, 2, [ 'Taxable sales', + 'Tax-exempt sales', + 'Taxes' + ], $colhead); +$y++; + +# print data +$rownum = 1; +$prev_row = { pkgclass => 'DUMMY PKGCLASS' }; + +foreach my $row (@rows) { + $x = 0; + if ( $row->{pkgclass} ne $prev_row->{pkgclass} ) { + $rownum = 1; + if ( $params{breakdown}->{pkgclass} ) { + $ws->merge_range($y, 0, $y, 4, + $pkgclass_name{$row->{pkgclass}}, + $format[0]->{sectionhead} + ); + $y++; + } + } + # pick a format set + my $f = $format[$rownum % 2]; + if ( $row->{total} ) { + $f = $format[2]; + } + $ws->write($y, $x, $row->{label}, $f->{rowhead}); + $x++; + foreach (qw(credits sales_credited exempt_credited tax_credited)) { + $ws->write($y, $x, $row->{$_} || 0, $f->{currency}); + $x++; + } + + $rownum++; + $y++; + $prev_row = $row; +} + +for my $x (0..4) { + $ws->set_column($x, $x, $widths[$x]); +} + + $workbook->close; http_header('Content-Length' => length($data)); diff --git a/httemplate/search/report_tax.cgi b/httemplate/search/report_tax.cgi index 0ad143f01..9e625c80f 100644 --- a/httemplate/search/report_tax.cgi +++ b/httemplate/search/report_tax.cgi @@ -18,6 +18,7 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px } .bigmath { font-size: large; font-weight: bold; font: sans-serif; text-align: center } .total { font-style: italic } </STYLE> + <& /elements/table-grid.html &> <THEAD> <TR> @@ -77,18 +78,18 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px } % # cust_bill_pkg.cgi wants a list of specific taxnums (and package class) % # cust_credit_bill_pkg.html wants a geographic scope (and package class) % my $rowlink = ';taxnum=' . $row->{taxnums}; -% my $rowregion = ';country=' . $cgi->param('country'); -% foreach my $loc (qw(state county city district)) { -% if ( $row->{$loc} ) { -% $rowregion .= ";$loc=" . uri_escape($row->{$loc}); -% } -% } +% # DON'T EVER USE THIS +% # my $rowregion = ';country=' . $cgi->param('country'); +% # foreach my $loc (qw(state county city district)) { +% # if ( $row->{$loc} ) { +% # $rowregion .= ";$loc=" . uri_escape($row->{$loc}); +% # } +% # } % # and also the package class, if we're limiting package class % if ( $params{breakdown}->{pkgclass} ) { % $rowlink .= ';classnum=' . ($row->{pkgclass} || 0); -% $rowregion .= ';classnum=' . ($row->{pkgclass} || 0); +% # $rowregion .= ';classnum=' . ($row->{pkgclass} || 0); % } -%warn $rowregion; % % if ( $row->{total} ) { </TBODY><TBODY CLASS="total"> @@ -122,7 +123,7 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px } </TD> % # credited sales <TD> - <A HREF="<% $salescreditlink . $rowregion %>"> + <A HREF="<% $salescreditlink . $rowlink %>"> <% $money_sprintf->( $row->{sales_credited} ) %> </A> </TD> @@ -183,6 +184,80 @@ TD.rowhead { font-weight: bold; text-align: left; padding: 0px 3px } % } </TABLE> +<BR> +<& /elements/table-grid.html &> + <THEAD> + <TR> + <TH ROwSPAN=2></TH> + <TH ROWSPAN=2>Total credits</TH> + <TH COLSPAN=3>Applied to</TH> + </TR> + <TR STYLE="font-size: small"> + <TH>Taxable sales</TH> + <TH>Tax-exempt sales</TH> + <TH>Taxes</TH> + </TR> + </THEAD> + +% $rownum = 0; +% $prev_row = { pkgclass => 'DUMMY PKGCLASS' }; + + <TBODY> +% # mostly duplicates the stuff above... +% # but putting it all in one giant table is no good +% foreach my $row (@rows) { +% if ( $row->{pkgclass} ne $prev_row->{pkgclass} ) { +% if ( $rownum > 0 ) { # start a new section +% $rownum = 0; + </TBODY><TBODY> +% } +% if ( $params{breakdown}->{pkgclass} ) { # and caption the new section + <TR> + <TD COLSPAN=5 CLASS="sectionhead"> + <% $pkgclass_name{$row->{pkgclass}} %> + </TD> + </TR> +% } +% } # if $row->{pkgclass} ne ... + +% my $rowlink = ';taxnum=' . $row->{taxnums}; +% +% if ( $row->{total} ) { + </TBODY><TBODY CLASS="total"> +% } + <TR CLASS="row<% $rownum % 2 %>"> + <TD CLASS="rowhead"><% $row->{label} |h %></TD> + <TD> +% # Total credits + <% $money_sprintf->( $row->{credits} ) %> + </TD> +% # Credits to taxable sales + <TD> + <A HREF="<% $salescreditlink . $rowlink %>"> + <% $money_sprintf->( $row->{sales_credited} ) %> + </A> + </TD> +% # ... to exempt sales (link is the same, it shows both exempt and taxable) + <TD> + <A HREF="<% $salescreditlink . $rowlink %>"> + <% $money_sprintf->( $row->{exempt_credited} ) %> + </A> + </TD> +% # ... to taxes + <TD> +%# <A HREF="<% $creditlink . $rowlink %>"> currently broken + <% $money_sprintf->( $row->{tax_credited} ) %> +%# </A> + </TD> + </TR> +% $rownum++; +% $prev_row = $row; +% } # foreach my $row +% # no "out of taxable region" for credits (yet) + </TBODY> +</TABLE> + + <& /elements/footer.html &> <%init> @@ -240,10 +315,11 @@ if ( $params{agentnum} ) { my $saleslink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;nottax=1"; my $taxlink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;istax=1"; my $exemptlink = $p. "search/cust_tax_exempt_pkg.cgi?$dateagentlink"; -my $salescreditlink = $p. "search/cust_credit_bill_pkg.html?$dateagentlink;nottax=1"; +my $salescreditlink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;nottax=1;credit=1"; if ( $params{'credit_date'} eq 'cust_credit_bill' ) { $salescreditlink =~ s/begin/credit_begin/; $salescreditlink =~ s/end/credit_end/; + $saleslink .= ";credit_begin=$beginning;credit_end=$ending"; } #my $creditlink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;credit=1;istax=1"; #if ( $params{'credit_date'} eq 'cust_credit_bill' ) { diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index 4903e185b..0c67843d4 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -42,6 +42,7 @@ table.hiddenrows { z-index: 1; text-align: center; } + </STYLE> % # activate rolldown buttons for hidden package blocks <SCRIPT TYPE="text/javascript"> diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html index 8aa64039c..e98b95e1c 100644 --- a/httemplate/view/cust_main/packages/package.html +++ b/httemplate/view/cust_main/packages/package.html @@ -53,10 +53,7 @@ ( <%onetime_change_link($cust_pkg)%> ) % } % # also, you can discount it -% if ( $curuser->access_right('Discount customer package') -% && ! scalar($cust_pkg->cust_pkg_discount_active) -% && ! scalar($cust_pkg->part_pkg->part_pkg_discount) -% ) { +% if ( $can_discount_pkg ) { ( <%pkg_discount_link($cust_pkg)%> ) % } <BR> @@ -79,14 +76,7 @@ ( <%pkg_change_link($cust_pkg)%> ) % } % -% if ( $curuser->access_right('Discount customer package') -% && $part_pkg->can_discount -% && ! scalar( @{ $cust_pkg->{_cust_pkg_discount_active} } ) -% && ( ! $opt{'term_discounts'} -% || ! scalar($cust_pkg->part_pkg->part_pkg_discount) -% ) -% ) -% { +% if ( $can_discount_pkg ) { % $br=1; ( <%pkg_discount_link($cust_pkg)%> ) % } @@ -437,4 +427,21 @@ sub pkg_event_link { '</a>'; } +# figure out if this user will be able to edit either the setup or recurring +# discounts for this package +my $can_discount_pkg = ( + $part_pkg->can_discount + and + ( ( $curuser->access_right(['Discount customer package', 'Waive setup fee']) + and $cust_pkg->base_setup > 0 + and !$cust_pkg->setup + ) + or + ( $curuser->access_right('Discount customer package') + and $cust_pkg->base_recur > 0 + and $cust_pkg->freq ne '0' + ) + ) +); + </%init> diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html index 7e125f72e..13bd2024b 100644 --- a/httemplate/view/cust_main/packages/status.html +++ b/httemplate/view/cust_main/packages/status.html @@ -461,10 +461,11 @@ sub pkg_status_row_changed { my $part_pkg = $old->part_pkg; $html .= pkg_status_row_colspan( $cust_pkg, - emt("Changed from [_1]: [_2]", - $cust_pkg->change_pkgnum, - $part_pkg->pkg_comment(cust_pkg=>$old, nopartpkg=>1) - ), +# emt("Changed from [_1]: [_2]", +# $cust_pkg->change_pkgnum, +# $part_pkg->pkg_comment(cust_pkg=>$old, nopartpkg=>1) +# ), + '', '', 'size' => '-1', 'align' => 'right', @@ -529,17 +530,25 @@ sub pkg_status_row_discount { my $discount = $cust_pkg_discount->discount; - my $label = '<B>'.emt('Discount').'</B>: '. $discount->description; - if ( $discount->months ) { + my $label = '<SPAN STYLE="font-size: small"><B>'; + if ( $cust_pkg_discount->setuprecur eq 'setup' ) { + $label .= emt('Setup Discount'); + } else { + $label .= emt('Recurring Discount'); + } + $label .= '</B>: '. $discount->description; + warn Dumper $cust_pkg_discount; + if ( $discount->months > 0 and $cust_pkg_discount->months_used > 0 ) { my $remaining = $discount->months - $cust_pkg_discount->months_used; $remaining = sprintf('%.2f', $remaining) if $remaining =~ /\./; - $label .= emt("([_1] months remaining)",$remaining); + $label .= <br> . emt("([_1] months remaining)",$remaining); } + $label .= '</SPAN>'; - $label .= ' <FONT SIZE="-1">('. - '<A HREF="../misc/delete-cust_pkg_discount.html?'. - $cust_pkg_discount->pkgdiscountnum. - '">'.emt('remove discount').'</A>)</FONT>'; + #$label .= ' <FONT SIZE="-1">('. + # '<A HREF="../misc/delete-cust_pkg_discount.html?'. + # $cust_pkg_discount->pkgdiscountnum. + # '">'.emt('remove discount').'</A>)</FONT>'; $html .= pkg_status_row_colspan( $cust_pkg, $label, '', %opt ); diff --git a/httemplate/view/quotation.html b/httemplate/view/quotation.html index 67609a1c6..18625095e 100755 --- a/httemplate/view/quotation.html +++ b/httemplate/view/quotation.html @@ -91,6 +91,8 @@ my $curuser = $FS::CurrentUser::CurrentUser; #die "access denied" # unless $curuser->access_right('View quotations'); +my $can_generate_quotation = $curuser->access_right('Generate quotation'); + my $quotationnum; my($query) = $cgi->keywords; if ( $query =~ /^(\d+)$/ ) { @@ -119,11 +121,23 @@ my $link = "quotationnum=$quotationnum"; #$link .= ';notice_name='. $notice_name if $notice_name; my $preref_callback = sub { - areyousure_link("${p}misc/delete-quotation_pkg.html?". shift->quotationpkgnum, + my $quotation_pkg = shift; + $can_generate_quotation ? + areyousure_link("${p}misc/delete-quotation_pkg.html?". $quotation_pkg->quotationpkgnum, emt('Are you sure you want to remove this package from the quotation?'), emt('Remove this package'), #tooltip qq(<img src="${p}images/cross.png">), #link - ); + ) . + include('/elements/popup_link.html', + action => "${p}edit/quotation_pkg_detail.html?pkgnum=" . + $quotation_pkg->quotationpkgnum, + html_label => qq(<IMG SRC="${p}images/Actions-document-edit-icon.png">), + title => emt('Edit quotation details'), + actionlabel => emt('Edit quotation details'), + color => '#333399', + width => 763, + ) + : ''; }; sub areyousure_link { |