From 89525f062092c185344ec7318406b1c9086d1eda Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Mon, 17 Aug 2015 23:01:31 -0500 Subject: RT#18830: Upload file to message template --- httemplate/misc/email-customers.html | 2 +- httemplate/misc/process/template_image-delete.cgi | 28 +++++++++++++ httemplate/misc/process/template_image-upload.cgi | 26 ++++++++++++ httemplate/misc/xmlhttp-template_image.cgi | 48 +++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 httemplate/misc/process/template_image-delete.cgi create mode 100644 httemplate/misc/process/template_image-upload.cgi create mode 100644 httemplate/misc/xmlhttp-template_image.cgi (limited to 'httemplate/misc') diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html index 47e6a5b48..8ac44afc1 100644 --- a/httemplate/misc/email-customers.html +++ b/httemplate/misc/email-customers.html @@ -36,7 +36,7 @@ should be used to set msgnum or from/subject/html_body cgi params % } -
+ %# Mixing search params with from address, subject, etc. required special-case %# handling of those, risked name conflicts, and caused massive problems with diff --git a/httemplate/misc/process/template_image-delete.cgi b/httemplate/misc/process/template_image-delete.cgi new file mode 100644 index 000000000..58c3f2c68 --- /dev/null +++ b/httemplate/misc/process/template_image-delete.cgi @@ -0,0 +1,28 @@ +<% $server->process %> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +# make sure user can generally edit +die "access denied" + unless $curuser->access_right([ 'Edit templates', 'Edit global templates' ]); + +# make sure user can edit this particular image +my %arg = $cgi->param('arg'); +my $imgnum = $arg{'imgnum'}; +die "bad imgnum" unless $imgnum =~ /^\d+$/; +die "access denied" unless qsearchs({ + 'table' => 'template_image', + 'select' => 'imgnum', + 'hashref' => { 'imgnum' => $imgnum }, + 'extra_sql' => ' AND ' . + $curuser->agentnums_sql( + 'null_right' => ['Edit global templates'] + ), + }); + +my $server = + new FS::UI::Web::JSRPC 'FS::template_image::process_image_delete', $cgi; + + diff --git a/httemplate/misc/process/template_image-upload.cgi b/httemplate/misc/process/template_image-upload.cgi new file mode 100644 index 000000000..c3c905981 --- /dev/null +++ b/httemplate/misc/process/template_image-upload.cgi @@ -0,0 +1,26 @@ +<% $server->process %> + +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right([ 'Edit templates', 'Edit global templates' ]); + +my %arg = $cgi->param('arg'); +my $agentnum = $arg{'agentnum'}; + +if (!$agentnum) { + die "access denied" + unless $curuser->access_right([ 'Edit global templates' ]); +} else { + die "bad agentnum" + unless $agentnum =~ /^\d+$/; + die "access denied" + unless $curuser->agentnum($agentnum); +} + +my $server = + new FS::UI::Web::JSRPC 'FS::template_image::process_image_upload', $cgi; + + diff --git a/httemplate/misc/xmlhttp-template_image.cgi b/httemplate/misc/xmlhttp-template_image.cgi new file mode 100644 index 000000000..a8c50edf0 --- /dev/null +++ b/httemplate/misc/xmlhttp-template_image.cgi @@ -0,0 +1,48 @@ +<%doc> +Returns JSON encoded array of objects with details about FS::template_image +objects. Attributes in each returned object are imgnum, name, and src. + +Accepts the following options: + +imgnum - only return object for this imgnum + +no_src - do not include the src field + + +<% encode_json(\@result) %>\ +<%init> +use FS::template_image; + +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" + unless $curuser->access_right([ 'View templates', 'View global templates', + 'Edit templates', 'Edit global templates', ]); + +my %arg = $cgi->param('arg'); + +my $search = { + 'table' => 'template_image', + 'hashref' => {}, +}; + +my $imgnum = $arg{'imgnum'} || ''; +die "Bad imgnum" unless $imgnum =~ /^\d*$/; +$search->{'hashref'}->{'imgnum'} = $imgnum if $imgnum; + +$search->{'select'} = 'imgnum, name' if $arg{'no_src'}; + +$search->{'extra_sql'} = ($imgnum ? ' AND ' : ' WHERE ') + . $curuser->agentnums_sql( + 'null_right' => ['View global templates','Edit global templates'] + ); + +my @images = qsearch($search); #needs agent virtualization + +my @result = map { +{ + 'imgnum' => $_->imgnum, + 'name' => $_->name, + 'src' => $arg{'no_src'} ? '' : $_->src, +} } @images; + + -- cgit v1.2.1 From 9e4ef67fa35a301ba23a8f6107e12b7db33f83c8 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Tue, 25 Aug 2015 09:25:37 -0700 Subject: warning about param in list context (in a substitution?) --- httemplate/misc/void-cust_bill.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/void-cust_bill.html b/httemplate/misc/void-cust_bill.html index 1608fd051..39b071229 100644 --- a/httemplate/misc/void-cust_bill.html +++ b/httemplate/misc/void-cust_bill.html @@ -14,7 +14,7 @@ <% ntable("#cccccc", 2) %> Reason - + -- cgit v1.2.1 From f4fc0bd2f813272ed1a878dd9f130fe155a6e3ff Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Tue, 25 Aug 2015 09:32:05 -0700 Subject: param in list context --- httemplate/misc/process/void-cust_bill.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/process/void-cust_bill.html b/httemplate/misc/process/void-cust_bill.html index accee27fd..7773b0ba9 100755 --- a/httemplate/misc/process/void-cust_bill.html +++ b/httemplate/misc/process/void-cust_bill.html @@ -21,6 +21,6 @@ my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum}); my $custnum = $cust_bill->custnum; -my $error = $cust_bill->void( $cgi->param('reason') ); +my $error = $cust_bill->void( scalar($cgi->param('reason')) ); -- cgit v1.2.1 From 76e8fffdfe3b6f6f8ab422038b62e40cc10f95e8 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Thu, 27 Aug 2015 19:18:42 -0700 Subject: #21564, external message services: preview and send messages through the UI --- httemplate/misc/email-customers.html | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html index 8ac44afc1..bffd0cf81 100644 --- a/httemplate/misc/email-customers.html +++ b/httemplate/misc/email-customers.html @@ -1,10 +1,11 @@ <%doc> -Allows emailing one or more customers, based on a search for customers. Search can -be specified either through cust_main fields as cgi params, or through a base64 encoded -frozen hash in the 'search' cgi param. Form allows selecting an existing msg_template, -or creating a custom message, and shows a preview of the message before sending. -If linked to as a popup, include the cgi parameter 'popup' for proper header handling. +Allows emailing one or more customers, based on a search for customers. +Search can be specified either through cust_main fields as cgi params, or +through a base64 encoded frozen hash in the 'search' cgi param. Form allows +selecting an existing msg_template, or creating a custom message, and shows a +preview of the message before sending. If linked to as a popup, include the +cgi parameter 'popup' for proper header handling. This may also be used as an element in other pages, enabling you to provide an alternate initial form while using this for search freezing/thawing and @@ -21,12 +22,13 @@ title - the title of the page no_search_fields - arrayref of additional fields that are not search parameters alternate_form - subroutine that returns alternate html for the initial form, -replaces msgnum/from/subject/html_body/action inputs and submit button, -not used if an action is specified +replaces msgnum/from/subject/html_body/action inputs and submit button, not +used if an action is specified -post_search_hook - sub hook for additional processing after search has been processed from cgi, -gets passed options 'conf' and 'search' (a reference to the unfrozen %search hash), -should be used to set msgnum or from/subject/html_body cgi params +post_search_hook - sub hook for additional processing after search has been +processed from cgi, gets passed options 'conf' and 'search' (a reference to +the unfrozen %search hash), should be used to set msgnum or +from/subject/html_body cgi params % if ($popup) { @@ -288,8 +290,13 @@ if ( $cgi->param('action') eq 'preview' ) { 'cust_main' => $cust, 'object' => $object, ); - my %message = $msg_template->prepare(%msgopts); - ($from, $subject, $html_body) = @message{'from', 'subject', 'html_body'}; + + my $cust_msg = $msg_template->prepare(%msgopts); + $from = $cust_msg->env_from; + $html_body = $cust_msg->preview; + if ( $cust_msg->header =~ /^subject: (.*)/mi ) { + $subject = $1; + } } } -- cgit v1.2.1 From 46bbbb1a78fd822805226abea832b6206273c091 Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Fri, 28 Aug 2015 00:56:49 -0500 Subject: RT#37064: Add action link to manually refund a payment --- httemplate/misc/unapply-cust_pay.cgi | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/unapply-cust_pay.cgi b/httemplate/misc/unapply-cust_pay.cgi index 8cdac180b..b0343d034 100755 --- a/httemplate/misc/unapply-cust_pay.cgi +++ b/httemplate/misc/unapply-cust_pay.cgi @@ -12,9 +12,7 @@ my $paynum = $1; my $cust_pay = qsearchs('cust_pay', { 'paynum' => $paynum } ); my $custnum = $cust_pay->custnum; -foreach my $cust_bill_pay ( $cust_pay->cust_bill_pay ) { - my $error = $cust_bill_pay->delete; - errorpage($error) if $error; -} +my $error = $cust_pay->delete_cust_bill_pay; +errorpage($error) if $error; -- cgit v1.2.1 From bb70ee978959d0489e6a049aedbb18250ee2e594 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Thu, 3 Sep 2015 13:42:45 -0700 Subject: quick payment entry: fix preloading of rows when some of them contain bad amounts, #15861 --- httemplate/misc/batch-cust_pay.html | 97 +++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 26 deletions(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/batch-cust_pay.html b/httemplate/misc/batch-cust_pay.html index 9f2540cc7..197ade14f 100644 --- a/httemplate/misc/batch-cust_pay.html +++ b/httemplate/misc/batch-cust_pay.html @@ -101,6 +101,10 @@ function select_discount_term(row) { var invoices_for_row = new Object; +var preloading = 0; // the number of preloading threads currently running + +// callback from toggle_application_row: we've received a list of +// the customer's open invoices. store them. function update_invoices(rownum, invoices) { invoices_for_row[rownum] = new Object; // only called before create_application_row @@ -113,6 +117,12 @@ function toggle_application_row(ev, next) { if (!next) next = function(){}; //optional continuation var rownum = this.getAttribute('rownum'); if ( this.checked ) { + // the user has opted to apply the payment to specific invoices. + // - lock the customer + // - fetch the list of open invoices + // - create a row to select an invoice + // - then optionally call "next", with this as the invocant + // and the rownum as argument; we use this to preload rows. var custnum = document.getElementById('custnum'+rownum).value; if (!custnum) return; lock_payment_row(rownum, true); @@ -124,6 +134,9 @@ function toggle_application_row(ev, next) { } ); } else { + // the user has opted not to do that. + // - remove all application rows + // - unlock the customer var row = document.getElementById('row'+rownum); var table_rows = row.parentNode.rows; for (i = row.sectionRowIndex; i < table_rows.count; i++) { @@ -183,6 +196,16 @@ function amount_unapplied(rownum) { var change_app_amount; +// the user has chosen an invoice. the previously chosen invoice is still +// in curr_invoice +// - if there is a value there, put it back on the invoices_for_row list for +// this customer. +// - then _remove_ the newly chosen invoice from that list. +// - find the "owed" element for this application row and set its value to the +// amount owed on that invoice. +// - find the "amount" element for this application row and set its value to +// either "owed" or the remaining payment amount, whichever is less. +// - call change_app_amount() on that element. function choose_app_invnum() { var rownum = this.getAttribute('rownum'); var appnum = this.getAttribute('appnum'); @@ -210,8 +233,10 @@ function choose_app_invnum() { } } +// the invoice selector has gained focus. clear its list of options, and +// replace them with the list of open invoices (from invoices_for_row). +// if there's already a selected invoice, prepend that to the list. function focus_app_invnum() { -% # invoice numbers just display as invoice numbers var rownum = this.getAttribute('rownum'); var add_opt = function(obj, value, label) { var o = document.createElement('OPTION'); @@ -233,14 +258,15 @@ function focus_app_invnum() { } } +// an application amount has been changed. if there's any unapplied payment +// amount, and any remaining invoices_for_row, add a blank application row. +// (but don't do this while preloading; it will unconditionally add enough +// rows to show all the attempted applications) function change_app_amount() { var rownum = this.getAttribute('rownum'); var appnum = this.getAttribute('appnum'); -%# maybe some kind of warning if amount_unapplied < 0? -%# only spawn a new application row if there are open invoices left, -%# and this is the highest-numbered application row for the customer, -%# and the sum of the applied amounts is < the amount of the payment, - if ( Object.keys(invoices_for_row[rownum]).length > 0 + if ( preloading == 0 + && Object.keys(invoices_for_row[rownum]).length > 0 && !document.getElementById( 'row'+rownum+'.'+(parseInt(appnum) + 1) ) && amount_unapplied(rownum) > 0 ) { @@ -248,6 +274,9 @@ function change_app_amount() { } } +// we're creating a payment application row. +// create the following elements: , s, "Apply to invoice" caption, +// invnum selector, "owed" display, amount input box, delete button. function create_application_row(rownum, appnum) { var payment_row = document.getElementById('row'+rownum); var tr_app = document.createElement('TR'); @@ -341,29 +370,45 @@ function preload() { var enable = document.getElementById('enable_app'+rownum); enable.checked = true; var preload_row = function(r) {//continuation from toggle_application_row - for (appnum=0; appnum < row_obj[r].length; appnum++) { - this_app = row_obj[r][appnum]; - var x = r + '.' + appnum; - //set invnum - var select_invnum = document.getElementById('invnum'+x); - focus_app_invnum.call(select_invnum); - for (i=0; i Date: Thu, 10 Sep 2015 21:33:37 -0500 Subject: RT#33410: Package GB add-ons --- httemplate/misc/change_pkg.cgi | 23 ++++- httemplate/misc/cust_pkg_usageprice.html | 121 +++++++++++++++++++++++ httemplate/misc/order_pkg.html | 21 +--- httemplate/misc/xmlhttp-part_pkg_usageprice.html | 18 +++- 4 files changed, 155 insertions(+), 28 deletions(-) create mode 100644 httemplate/misc/cust_pkg_usageprice.html (limited to 'httemplate/misc') diff --git a/httemplate/misc/change_pkg.cgi b/httemplate/misc/change_pkg.cgi index e74747e82..b562d24cd 100755 --- a/httemplate/misc/change_pkg.cgi +++ b/httemplate/misc/change_pkg.cgi @@ -19,13 +19,13 @@ <& /elements/tr-select-cust-part_pkg.html, 'pre_label' => emt('New'), - 'curr_value' => scalar($cgi->param('pkgpart')), + 'curr_value' => scalar($cgi->param('pkgpart')) || $cust_pkg->pkgpart, 'classnum' => $part_pkg->classnum, 'cust_main' => $cust_main, &> <& /elements/tr-input-pkg-quantity.html, - 'curr_value' => $cust_pkg->quantity + 'curr_value' => scalar($cgi->param('quantity')) || $cust_pkg->quantity &> % if ($use_contract_end) { @@ -39,6 +39,11 @@
+<% include('/misc/cust_pkg_usageprice.html', + 'pkgpart' => (scalar($cgi->param('pkgpart')) || $cust_pkg->pkgpart), + 'pkgnum' => ($cust_pkg->change_to_pkgnum || $pkgnum), + ) %> +
<% mt('Change') |h %> <% ntable('#cccccc') %> @@ -49,8 +54,16 @@ document.getElementById('start_date_text').disabled = !enable; document.getElementById('start_date_button').style.display = (enable ? '' : 'none'); - document.getElementById('start_date_button_disabled').style.display = - (enable ? 'none' : ''); + if (document.getElementById('start_date_button_disabled')) { // does this ever exist anymore? + document.getElementById('start_date_button_disabled').style.display = + (enable ? 'none' : ''); + } + if (enable) { + usageprice_disable(1); + } else { + var form = document.OrderPkgForm; + usageprice_disable(0,form.pkgpart.options[form.pkgpart.selectedIndex].value); + } } <&| /elements/onload.js &> delay_changed(); @@ -96,7 +109,7 @@ TYPE = "button" VALUE = "<% mt("Change package") |h %>" onClick = "this.disabled=true; standardize_new_location();" - <% scalar($cgi->param('pkgpart')) ? '' : 'DISABLED' %> + <% #scalar($cgi->param('pkgpart')) ? '' : 'DISABLED' %> > diff --git a/httemplate/misc/cust_pkg_usageprice.html b/httemplate/misc/cust_pkg_usageprice.html new file mode 100644 index 000000000..f2e0f57e6 --- /dev/null +++ b/httemplate/misc/cust_pkg_usageprice.html @@ -0,0 +1,121 @@ +<%doc> +Sets up the xmlhttp, javascript and initial (empty) table for selecting cust_pkg_usageprice. +Available values are based on pkgpart, and can be updated when changing pkgpart +by passing the new pkgpart to the following javascript: + + usageprice_pkg_changed( pkgpart, pkgnum ) + +The pkgnum input is optional, and will be used to set initial selected values. + +If pkgpart is passed as an option to this element, will run usageprice_pkg_changed +once to initialize table; pkgnum can be passed as an option along with this. + +You can disable usageprice selection temporarily (remove the fields from the form) +with the javascript usageprice_disable(1), and restore it with usageprice_disable(0,pkgnum). +While disabled, calling usageprice_pkg_changed will have no effect. + + +<& /elements/xmlhttp.html, + 'url' => $p.'misc/xmlhttp-part_pkg_usageprice.html', + 'subs' => [ 'get_part_pkg_usageprice' ], +&> + + + + +
+ + + +<%init> +my %opt = @_; + + + diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html index 799165fe0..cb2bd4832 100644 --- a/httemplate/misc/order_pkg.html +++ b/httemplate/misc/order_pkg.html @@ -5,11 +5,6 @@ } &> -<& /elements/xmlhttp.html, - 'url' => $p.'misc/xmlhttp-part_pkg_usageprice.html', - 'subs' => [ 'get_part_pkg_usageprice' ], -&> - <& /elements/init_calendar.html &> @@ -121,19 +116,9 @@
-%#so: -%# - hide until you selecdt a pacakge with add-ons -%# -lookup and display the available add-ons when -%# -add them to the (recur if there is one, otherwise setup) price and display magically like processing fees do on edit/cust_pay.cgi - -%# better label? - - - -
+<% include('/misc/cust_pkg_usageprice.html', + 'pkgpart' => $pkgpart + ) %>
% my $discount_cust_pkg = $curuser->access_right('Discount customer package'); diff --git a/httemplate/misc/xmlhttp-part_pkg_usageprice.html b/httemplate/misc/xmlhttp-part_pkg_usageprice.html index d4e2d8469..9decdeff9 100644 --- a/httemplate/misc/xmlhttp-part_pkg_usageprice.html +++ b/httemplate/misc/xmlhttp-part_pkg_usageprice.html @@ -1,24 +1,32 @@ <% encode_json( \@return ) %>\ <%init> -my( $pkgpart ) = $cgi->param('arg'); +my( $pkgpart, $pkgnum ) = $cgi->param('arg'); #could worry about agent-virting this so you can't see the add-on pricing of # other agents, but not a real-world big worry my $part_pkg = qsearchs( 'part_pkg', { pkgpart=>$pkgpart } ); +my %curr_quantity; +if ($pkgnum) { + my $cust_pkg = qsearchs( 'cust_pkg', { pkgnum=>$pkgnum } ); + %curr_quantity = map { $_->usagepricepart, $_->quantity } $cust_pkg->cust_pkg_usageprice; +} + my $num = 0; -my @return = map { +# probably don't need to be returning js_only anymore? +my @return = ($pkgpart, map { + my $usagepricepart = $_->usagepricepart; my @inc = ('/elements/cust_pkg_usageprice.html', - 'usagepricepart' => $_->usagepricepart, + 'usagepricepart' => $usagepricepart, ); - + push(@inc,'curr_quantity',($curr_quantity{$usagepricepart} || 0)); ( include(@inc, field=>'usagepricenum'.$num, html_only=>1 ), include(@inc, field=>'usagepricenum'.$num++, js_only=>1 ), ); } - $part_pkg->part_pkg_usageprice; + $part_pkg->part_pkg_usageprice); -- cgit v1.2.1 From c0c5709fb022b83a482d0b35f7094505766d5868 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Fri, 18 Sep 2015 10:18:43 -0700 Subject: send commission reports by email, #33101 --- httemplate/misc/process/send-report.html | 7 ++ httemplate/misc/send-report.html | 166 +++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 httemplate/misc/process/send-report.html create mode 100644 httemplate/misc/send-report.html (limited to 'httemplate/misc') diff --git a/httemplate/misc/process/send-report.html b/httemplate/misc/process/send-report.html new file mode 100644 index 000000000..3bceebc0c --- /dev/null +++ b/httemplate/misc/process/send-report.html @@ -0,0 +1,7 @@ +<%init> +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Send reports to customers'); + +my $server = FS::UI::Web::JSRPC->new('FS::report_batch::process_send_report', $cgi); + +<% $server->process %> diff --git a/httemplate/misc/send-report.html b/httemplate/misc/send-report.html new file mode 100644 index 000000000..557767a57 --- /dev/null +++ b/httemplate/misc/send-report.html @@ -0,0 +1,166 @@ +<%doc> + +Parameters: + +- reportname: the report name (per FS::report_batch) + + +<& /elements/header-popup.html, { title => $report_info->{name} } &> + +
+ + + + + <& /elements/tr-select-agent.html &> + + <& /elements/tr-td-label.html, label => emt('Message template') &> + + + + <& /elements/tr-input-beginning_ending.html &> + + <& /elements/progress-init.html, + 'OneTrueForm', + [ qw( reportname msgnum agentnum beginning ending ) ], + $p.'misc/process/send-report.html', + { message => 'Reports sent', + url => $cgi->referer } + &> + +
+ <& /elements/select-msg_template.html, field => 'msgnum' &> +
+ + +
+ + + +% if ( @report_history ) { +
+ + + + + + + + + +% my $row = 0; +% foreach my $report (@report_history) { +% my $agent = ($report->agentnum ? +% $report->agent->agent : 'All agents'); + + + + + + + +% $row++; +% } + +
<% emt('Report history') %>
AgentSent onDate rangeUser
<% $agent %><% time2str($date_format, $report->send_date) %><% time2str($date_format, $report->sdate) %><% time2str($date_format, $report->edate) %><% $report->access_user->username %>
+% } + +<& /elements/footer.html &> + +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Send reports to customers'); + +$cgi->param('reportname') =~ /^(\w+)$/ + or die "bad reportname"; +my $reportname = $1; +my $report_info = $FS::report_batch::sendable_reports{$reportname} + or die "bad reportname"; + +my $date_format = FS::Conf->new->config('date_format') || '%x'; + +my @report_history = qsearch({ + table => 'report_batch', + hashref => { reportname => $reportname }, + order_by => ' ORDER BY send_date DESC', +}); + +# defaults per agent that could be selected for the report +my %agent; + +foreach my $report ( @report_history ) { + my $agentnum = $report->agentnum; + next if $agent{$agentnum}; + + # estimate the width of the report period, in months + my $last_sdate = DateTime->from_epoch( epoch => $report->sdate ); + my $last_edate = DateTime->from_epoch( epoch => $report->edate ); + + my $days = $last_sdate->delta_days( $last_edate )->delta_days; + my $months = sprintf('%.0f', $days / 6) / 5; + + my $next_sdate = $last_edate->clone->add(days => 1); + my $next_edate = $next_sdate->clone; + if ( $months >= 1 ) { # then treat as an interval in months + $next_edate->add( months => sprintf('%.0f', $months) ); + $next_edate->subtract(days => 1); + } else { # treat as a number of days + $next_edate->add( days => $days ); + } + + my $name = $agentnum ? FS::agent->by_key($agentnum)->agent : 'All agents'; + $agent{$agentnum} = { + name => $name, + beginning => $next_sdate->strftime($date_format), + ending => $next_edate->strftime($date_format), + msgnum => $report->msgnum, + }; +} + + -- cgit v1.2.1 From 15a4e1674694b76ecc2af87de479aabe370ac03d Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Tue, 22 Sep 2015 01:08:04 -0500 Subject: RT#37908: Convert existing email-sending code to use common interface [removals and switches to FS::Log] --- httemplate/misc/delete-cust_credit.cgi | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100755 httemplate/misc/delete-cust_credit.cgi (limited to 'httemplate/misc') diff --git a/httemplate/misc/delete-cust_credit.cgi b/httemplate/misc/delete-cust_credit.cgi deleted file mode 100755 index 03eb47299..000000000 --- a/httemplate/misc/delete-cust_credit.cgi +++ /dev/null @@ -1,21 +0,0 @@ -% if ( $error ) { -% errorpage($error); -% } else { -<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %> -% } -<%init> - -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Delete credit'); - -#untaint crednum -my($query) = $cgi->keywords; -$query =~ /^(\d+)$/ || die "Illegal crednum"; -my $crednum = $1; - -my $cust_credit = qsearchs('cust_credit',{'crednum'=>$crednum}); -my $custnum = $cust_credit->custnum; - -my $error = $cust_credit->delete; - - -- cgit v1.2.1 From d3880a1402fd46454ab448d59c2e4ff861094981 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Mon, 28 Sep 2015 20:21:27 -0700 Subject: remove payment deletion, RT#37908 --- httemplate/misc/delete-cust_pay.cgi | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100755 httemplate/misc/delete-cust_pay.cgi (limited to 'httemplate/misc') diff --git a/httemplate/misc/delete-cust_pay.cgi b/httemplate/misc/delete-cust_pay.cgi deleted file mode 100755 index 38e7e4ba1..000000000 --- a/httemplate/misc/delete-cust_pay.cgi +++ /dev/null @@ -1,21 +0,0 @@ -% if ( $error ) { -% errorpage($error); -% } else { -<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %> -% } -<%init> - -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Delete payment'); - -#untaint paynum -my($query) = $cgi->keywords; -$query =~ /^(\d+)$/ || die "Illegal paynum"; -my $paynum = $1; - -my $cust_pay = qsearchs('cust_pay',{'paynum'=>$paynum}); -my $custnum = $cust_pay->custnum; - -my $error = $cust_pay->delete; - - -- cgit v1.2.1 From c74ee632580cc2b90175bb266e442bce54d4b400 Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Tue, 29 Sep 2015 00:00:42 -0500 Subject: RT#38048 not storing credit card #s --- httemplate/misc/process/payment.cgi | 77 +++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 38 deletions(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi index 27b818660..d9299e5bd 100644 --- a/httemplate/misc/process/payment.cgi +++ b/httemplate/misc/process/payment.cgi @@ -135,6 +135,45 @@ $cgi->param('discount_term') =~ /^(\d*)$/ or errorpage("illegal discount_term"); my $discount_term = $1; +# save first, for proper tokenization later +if ( $cgi->param('save') ) { + my $new = new FS::cust_main { $cust_main->hash }; + if ( $payby eq 'CARD' ) { + $new->set( 'payby' => ( $cgi->param('auto') ? 'CARD' : 'DCRD' ) ); + } elsif ( $payby eq 'CHEK' ) { + $new->set( 'payby' => ( $cgi->param('auto') ? 'CHEK' : 'DCHK' ) ); + } else { + die "unknown payby $payby"; + } + $new->payinfo($payinfo); #to properly set paymask + $new->set( 'paydate' => "$year-$month-01" ); + $new->set( 'payname' => $payname ); + + #false laziness w/FS:;cust_main::realtime_bop - check both to make sure + # working correctly + if ( $payby eq 'CARD' && + grep { $_ eq cardtype($payinfo) } $conf->config('cvv-save') ) { + $new->set( 'paycvv' => $paycvv ); + } else { + $new->set( 'paycvv' => ''); + } + + if ( $payby eq 'CARD' ) { + my $bill_location = FS::cust_location->new; + $bill_location->set( $_ => $cgi->param($_) ) + foreach @{$payby2fields{$payby}}; + $new->set('bill_location' => $bill_location); + # will do nothing if the fields are all unchanged + } else { + $new->set( $_ => $cgi->param($_) ) foreach @{$payby2fields{$payby}}; + } + + my $error = $new->replace($cust_main); + errorpage("error saving info, payment not processed: $error") + if $error; + $cust_main = $new; +} + my $error = ''; my $paynum = ''; if ( $cgi->param('batch') ) { @@ -190,44 +229,6 @@ if ( $cgi->param('batch') ) { } -if ( $cgi->param('save') ) { - my $new = new FS::cust_main { $cust_main->hash }; - if ( $payby eq 'CARD' ) { - $new->set( 'payby' => ( $cgi->param('auto') ? 'CARD' : 'DCRD' ) ); - } elsif ( $payby eq 'CHEK' ) { - $new->set( 'payby' => ( $cgi->param('auto') ? 'CHEK' : 'DCHK' ) ); - } else { - die "unknown payby $payby"; - } - $new->set( 'payinfo' => $cust_main->card_token || $payinfo ); - $new->set( 'paydate' => "$year-$month-01" ); - $new->set( 'payname' => $payname ); - - #false laziness w/FS:;cust_main::realtime_bop - check both to make sure - # working correctly - if ( $payby eq 'CARD' && - grep { $_ eq cardtype($payinfo) } $conf->config('cvv-save') ) { - $new->set( 'paycvv' => $paycvv ); - } else { - $new->set( 'paycvv' => ''); - } - - if ( $payby eq 'CARD' ) { - my $bill_location = FS::cust_location->new; - $bill_location->set( $_ => $cgi->param($_) ) - foreach @{$payby2fields{$payby}}; - $new->set('bill_location' => $bill_location); - # will do nothing if the fields are all unchanged - } else { - $new->set( $_ => $cgi->param($_) ) foreach @{$payby2fields{$payby}}; - } - - my $error = $new->replace($cust_main); - errorpage("payment processed successfully, but error saving info: $error") - if $error; - $cust_main = $new; -} - #success! -- cgit v1.2.1 From d07c72046444319e0811c6a00b504885da091992 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Wed, 30 Sep 2015 22:49:38 -0700 Subject: graphical selection of deployment zones and automatic block lookup, #30260 --- httemplate/misc/process/deploy_zone-block_lookup.cgi | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 httemplate/misc/process/deploy_zone-block_lookup.cgi (limited to 'httemplate/misc') diff --git a/httemplate/misc/process/deploy_zone-block_lookup.cgi b/httemplate/misc/process/deploy_zone-block_lookup.cgi new file mode 100644 index 000000000..8f4eac7e9 --- /dev/null +++ b/httemplate/misc/process/deploy_zone-block_lookup.cgi @@ -0,0 +1,13 @@ +<% $server->process %> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; +die "access denied" + unless $curuser->access_right([ + 'Edit FCC report configuration', + 'Edit FCC report configuration for all agents', + ]); + +my $server = FS::UI::Web::JSRPC->new( + 'FS::deploy_zone::process_block_lookup', $cgi +); + -- cgit v1.2.1 From ecc93e41eac485b6e84b844d9db8d25576dab3b9 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Mon, 5 Oct 2015 20:13:55 -0700 Subject: show customer name on appointments, RT#34237 --- httemplate/misc/xmlhttp-ticket-update.html | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 httemplate/misc/xmlhttp-ticket-update.html (limited to 'httemplate/misc') diff --git a/httemplate/misc/xmlhttp-ticket-update.html b/httemplate/misc/xmlhttp-ticket-update.html new file mode 100644 index 000000000..147fbef16 --- /dev/null +++ b/httemplate/misc/xmlhttp-ticket-update.html @@ -0,0 +1,66 @@ +<% encode_json($return) %>\ +<%init> + +my $id = $cgi->param('id'); +my $starts = $cgi->param('starts'); +my $due = $cgi->param('due'); +my $username = $cgi->param('username'); + +my $ticket = FS::TicketSystem->get_ticket_object( \%session, ticket_id=>$id ); + +#hmm, this should happen in a single transaction and either commit or rollback, +# but in reality failures "Don't Happen" so its not like a ticket gets +# half changed + +my $return; +if ( $ticket ) { + + my($orv, $omsg) = $ticket->SetOwner( $username, 'Steal' ); + $orv = 1 if ! $orv && $omsg =~ /already owns/i; + + if ( $orv ) { + + my $date = RT::Date->new( $session{CurrentUser} ); + $date->Set( Format=>'unix', Value=>$starts, ); + my($srv, $smsg) = $ticket->SetStarts( $date->ISO ); + + my $ddate; + unless ( ! $srv ) { + $ddate = RT::Date->new( $session{CurrentUser} ); + $ddate->Set( Format=>'unix', Value=>$due, ); + my($drv, $dmsg) = $ticket->SetDue( $ddate->ISO ); + #XXX lame + } + + if ( $srv ) { + + my ($sm, $sh) = ( $date->Localtime('user'))[1,2]; + my ($em, $eh) = ($ddate->Localtime('user'))[1,2]; + + #false laziness w/CalendarSlotSchedule and CalendarDaySchedule + my %hash = $m->comp('/rt/Ticket/Elements/Customers', Ticket => $ticket); + my @cust_main = values( %{$hash{cust_main}} ); + + $return = { 'error' => '', + #'starts' => $starts, + #'due' => $due, + #'username' => $username, + #false laziness w/CalendarSlotSchedule + 'sched_label' => + FS::sched_avail::pretty_time($sh*60+$sm). '-'. + FS::sched_avail::pretty_time($eh*60+$em). ': '. + $cust_main[0]->_FreesideURILabel, + }; + } else { + $return = { 'error' => $smsg }; + } + + } else { + $return = { 'error' => $omsg }; + } + +} else { + $return = { 'error' => 'Unknown ticket id' }; +} + + -- cgit v1.2.1 From a267a869ad2f2c9b6ba4e306aea6103e3a6bfe4e Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Tue, 6 Oct 2015 00:35:18 -0500 Subject: RT#38314: Declined payment shows card as tokenized after first attempt --- httemplate/misc/process/payment.cgi | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/process/payment.cgi b/httemplate/misc/process/payment.cgi index d9299e5bd..efba9ed9a 100644 --- a/httemplate/misc/process/payment.cgi +++ b/httemplate/misc/process/payment.cgi @@ -74,11 +74,13 @@ $cgi->param('balance') =~ /^\s*(\-?\s*\d*(\.\d\d)?)\s*$/ my $balance = $1; my $payinfo; +my $paymask; # override only used by loaded cust payinfo, only implemented for realtime processing my $paycvv = ''; if ( $payby eq 'CHEK' ) { if ($cgi->param('payinfo1') =~ /xx/i || $cgi->param('payinfo2') =~ /xx/i ) { $payinfo = $cust_main->payinfo; + $paymask = $cust_main->paymask; } else { $cgi->param('payinfo1') =~ /^(\d+)$/ or errorpage("Illegal account number ". $cgi->param('payinfo1')); @@ -99,6 +101,7 @@ if ( $payby eq 'CHEK' ) { $payinfo = $cgi->param('payinfo'); if ($payinfo eq $cust_main->paymask) { $payinfo = $cust_main->payinfo; + $paymask = $cust_main->paymask; } $payinfo =~ s/\D//g; $payinfo =~ /^(\d{13,16}|\d{8,9})$/ @@ -145,7 +148,8 @@ if ( $cgi->param('save') ) { } else { die "unknown payby $payby"; } - $new->payinfo($payinfo); #to properly set paymask + $new->payinfo($payinfo); # sets default paymask, but not if it's already tokenized + $new->paymask($paymask) if $paymask; # in case it's been tokenized, override with loaded paymask $new->set( 'paydate' => "$year-$month-01" ); $new->set( 'payname' => $payname ); @@ -199,6 +203,7 @@ if ( $cgi->param('batch') ) { 'manual' => 1, 'balance' => $balance, 'payinfo' => $payinfo, + 'paymask' => $paymask, 'paydate' => "$year-$month-01", 'payname' => $payname, 'payunique' => $payunique, -- cgit v1.2.1 From fc672686f119da0b3b34fd3c73acc3fea81262e6 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Wed, 7 Oct 2015 14:18:15 -0700 Subject: #37098: convert one-shot email notices to use message templates --- httemplate/misc/email-customers.html | 191 +++++++++++++++++++++-------------- 1 file changed, 115 insertions(+), 76 deletions(-) (limited to 'httemplate/misc') diff --git a/httemplate/misc/email-customers.html b/httemplate/misc/email-customers.html index bffd0cf81..8e2863455 100644 --- a/httemplate/misc/email-customers.html +++ b/httemplate/misc/email-customers.html @@ -22,13 +22,13 @@ title - the title of the page no_search_fields - arrayref of additional fields that are not search parameters alternate_form - subroutine that returns alternate html for the initial form, -replaces msgnum/from/subject/html_body/action inputs and submit button, not +replaces msgnum/from/subject/body/action inputs and submit button, not used if an action is specified post_search_hook - sub hook for additional processing after search has been processed from cgi, gets passed options 'conf' and 'search' (a reference to the unfrozen %search hash), should be used to set msgnum or -from/subject/html_body cgi params +from/subject/body cgi params % if ($popup) { @@ -37,6 +37,7 @@ from/subject/html_body cgi params <& /elements/header.html, $title &> % } +<& /elements/error.html &>
@@ -48,48 +49,40 @@ from/subject/html_body cgi params -% if ( $cgi->param('action') eq 'send' ) { - - Sending notice +% if ( $cgi->param('preview') ) { +% # preview mode: at this point we have a msg_template (either "real" or +% # draft) and $html_body and $text_body contain the preview message. +% # give the user a chance to back out (by going back to edit mode). + Preview notice <& /elements/progress-init.html, 'OneTrueForm', - [ qw( search table from subject html_body text_body msgnum ) ], + [ qw( search table msgnum ) ], $process_url, $pdest, &> -% } elsif ( $cgi->param('action') eq 'preview' ) { - - Preview notice - -% } - -% if ( $cgi->param('action') ) { - - - -% if ( $msg_template ) { - <& /elements/tr-fixed.html, - 'label' => 'Template:', - 'value' => $msg_template->msgname, - &> -% } + +% # kludge these through hidden inputs because they're not really part +% # of the template, but should be sticky during draft editing + + + +% if ( !$msg_template->disabled ) { + <& /elements/tr-td-label.html, 'label' => 'Template:' &> + + +% } - <& /elements/tr-fixed.html, - 'field' => 'from', - 'label' => 'From:', - 'value' => $from, - &> + <& /elements/tr-td-label.html, 'label' => 'From:' &> + + - <& /elements/tr-fixed.html, - 'field' => 'subject', - 'label' => 'Subject:', - 'value' => $subject, - &> + <& /elements/tr-td-label.html, 'label' => 'Subject:' &> + + -
<% $msg_template->msgname |h %>
<% $from |h %>
<% $subject |h %>
 
Message (HTML display): @@ -101,7 +94,6 @@ from/subject/html_body cgi params % $html_body % ) % ); -
 
Message (Text display): @@ -114,38 +106,37 @@ from/subject/html_body cgi params
-% if ( $cgi->param('action') eq 'preview' ) { + + } + -
- - - -% } +
+ + % } elsif ($opt{'alternate_form'}) { <% &{$opt{'alternate_form'}}() %> % } else { +% # Edit mode. +% if ( $msg_template and $msg_template->disabled ) { +% # if we've already established a draft template, don't let msgnum be changed + <& /elements/hidden.html, + field => 'msgnum', + curr_value => ($cgi->param('msgnum') || ''), + &> +% } else { Template: <& /elements/select-msg_template.html, - onchange => 'toggle(this)', + onchange => 'toggle(this)', + curr_value => ($cgi->param('msgnum') || ''), &>
+% } <& /elements/tr-td-label.html, 'label' => 'From:' &> <& /elements/tr-input-text.html, 'field' => 'subject', 'label' => 'Subject:', 'size' => 50, + 'curr_value' => $subject, &>
<& /elements/input-text.html, @@ -165,46 +165,41 @@ Template: 'value' => $conf->config('invoice_from_name', $agent_virt_agentnum) || $conf->config('company_name', $agent_virt_agentnum), #? 'size' => 20, + 'curr_value' => $cgi->param('from_name'), &> <\ <& /elements/input-text.html, 'field' => 'from_addr', 'type' => 'email', # HTML5, woot 'value' => $conf->config('invoice_from', $agent_virt_agentnum), 'size' => 20, + 'curr_value' => $cgi->param('from_addr'), &>>
Message: <& /elements/htmlarea.html, - 'field' => 'html_body', + 'field' => 'body', 'width' => 763, + 'curr_value' => $body, &>
-%#Substitution vars: - - - + % } #end not action or alternate form
-% if ( $cgi->param('action') eq 'send' ) { - -% } - <& /elements/footer.html &> <%init> @@ -217,7 +212,7 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right($opt{'acl'}); my $conf = FS::Conf->new; -my @no_search_fields = qw( action table from subject html_body text_body popup url ); +my @no_search_fields = qw( table from subject html_body text_body popup url ); my $form_action = $opt{'form_action'} || 'email-customers.html'; my $process_url = $opt{'process_url'} || 'process/email-customers.html'; @@ -261,12 +256,26 @@ if ( $cgi->param('from') ) { $from = $cgi->param('from_addr'); } -my $subject = $cgi->param('subject') || ''; -my $html_body = $cgi->param('html_body') || ''; - my $msg_template = ''; +if ( $cgi->param('msgnum') =~ /^(\d+)$/ ) { + $msg_template = FS::msg_template->by_key($1) + or die "template not found: ".$cgi->param('msgnum'); +} + +my $subject = $cgi->param('subject'); +my $body = $cgi->param('body'); +my ($html_body, $text_body); -if ( $cgi->param('action') eq 'preview' ) { +if ( !$cgi->param('preview') ) { + + # edit mode: initialize the fields from the saved draft, if there is one + if ( $msg_template and $msg_template->disabled eq 'D' ) { + my $content = $msg_template->content(''); # no localization on these yet + $subject ||= $content->subject; + $body ||= $content->body; + } + +} else { my $sql_query = "FS::$table"->search(\%search); my $count_query = delete($sql_query->{'count_query'}); @@ -277,10 +286,40 @@ if ( $cgi->param('action') eq 'preview' ) { my $count_arrayref = $count_sth->fetchrow_arrayref; $num_cust = $count_arrayref->[0]; - if ( $cgi->param('msgnum') ) { - $msg_template = qsearchs('msg_template', - { msgnum => scalar($cgi->param('msgnum')) } ) - or die "template not found: ".$cgi->param('msgnum'); + if ( !$msg_template or $msg_template->disabled eq 'D' ) { + # then this is a one-off template; edit it in place + my $subject = $cgi->param('subject') || ''; + my $body = $cgi->param('body') || ''; + + # create a draft template + $msg_template ||= FS::msg_template->new({ + msgclass => 'email', + disabled => 'D', + }); + # anyone have a better idea for msgname? + $msg_template->set('msgname' => "Notice " . DateTime->now->iso8601); + $msg_template->set('from_addr' => $from); + my %content = ( + subject => $subject, + body => $body, + ); + my $error; + if ( $msg_template->msgnum ) { + $error = $msg_template->replace(%content); + } else { + $error = $msg_template->insert(%content); + } + + if ( $error ) { + $cgi->param('error', $error); + $cgi->delete('preview'); # don't go on to preview stage yet + undef $msg_template; + } + } + # unless creating the msg_template failed, we now have one, so construct a + # preview message from the first customer/whatever in the search results + + if ( $msg_template ) { $sql_query->{'extra_sql'} .= ' LIMIT 1'; $sql_query->{'select'} = "$table.*"; $sql_query->{'order_by'} = ''; -- cgit v1.2.1