4.x style one-time charge
[freeside.git] / httemplate / edit / quick-charge.html
1 % if ( $quotationnum ) {
2 <& /elements/header.html, mt('One-time charge') &>
3 % } else {
4 <& /elements/header-cust_main.html,
5      view      => 'packages',
6      cust_main => $cust_main,
7      etc       => $cgi->param('error') ? '' : 'onload="addRow()"',
8 &>
9 <h2>One-time charge</h2>
10 % }
11
12 <& /elements/init_calendar.html &>
13 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/xregexp-all.js"></SCRIPT>
14
15 <& /elements/error.html &>
16
17 <SCRIPT TYPE="text/javascript">
18
19 function enable_quick_charge (e) {
20
21   if (    document.QuickChargeForm.amount.value
22        && document.QuickChargeForm.pkg.value    ) {
23     document.QuickChargeForm.submit.disabled = false;
24   } else {
25     document.QuickChargeForm.submit.disabled = true;
26   }
27
28 % if ( $curuser->option('disable_enter_submit_onetimecharge') ) {
29
30     var key;
31     if (window.event)
32           key = window.event.keyCode; //IE
33     else
34
35           key = e.which; //firefox, others
36
37     return (key != 13);
38
39 % } else {
40     return true;
41 % }
42
43 }
44
45 function validate_quick_charge () {
46   var pkg = document.QuickChargeForm.pkg.value;
47   var pkg_regex = XRegExp('^([\\p{L}\\p{N} \_\!\@\#\$\%\&\(\)\+\;\:\'\"\,\.\?\/\=\\-\\[\\]]*)$');
48   var amount = document.QuickChargeForm.amount.value;
49   var amount_regex = /^\s*\$?\s*(\d*(\.?\d{1,2}))\s*$/ ;
50   var rval = true;
51
52   if ( ! amount_regex.test(amount) ) {
53     alert('Illegal amount - enter an amount to charge, for example, "5" or "43" or "21.46".');
54     return false;
55   }
56   if ( String(pkg).length < 1 ) {
57     rval = false;
58   }
59   if ( ! pkg_regex.test(pkg) ) {
60     rval = false;
61   }
62   var i=0;
63   for (i=0; i < rownum; i++) {
64     if (! eval('pkg_regex.test(document.QuickChargeForm.description' + i + '.value)')){
65       rval = false;
66       break;
67     }
68   }
69   if (rval == true) {
70     return true;
71   }
72
73   if ( ! pkg ) {
74     alert('Enter a description for the one-time charge');
75     return false;
76   }
77
78   alert('Illegal description - spaces, letters, numbers, and the following punctuation characters are allowed: . , ! ? @ # $ % & ( ) - + ; : ' + "'" + ' " = [ ]' );
79   return false;
80 }
81
82 function bill_now_changed (what) {
83   var form = what.form;
84   if ( what.checked ) {
85     form.start_date_text.disabled = true;
86     form.start_date.style.backgroundColor = '#dddddd';
87     form.start_date_button.style.display = 'none';
88     form.start_date_button_disabled.style.display = '';
89     form.invoice_terms.disabled = false;
90   } else {
91     form.start_date_text.disabled = false;
92     form.start_date.style.backgroundColor = '#ffffff';
93     form.start_date_button.style.display = '';
94     form.start_date_button_disabled.style.display = 'none';
95     form.invoice_terms.disabled = true;
96   }
97 }
98
99 </SCRIPT>
100
101 <P>
102 % if ( $prospect_main ) {
103 <& /elements/small_prospect_view.html, $prospect_main &>
104 % }
105 </P>
106
107 <FORM ACTION   = "process/quick-charge.cgi"
108       NAME     = "QuickChargeForm"
109       ID       = "QuickChargeForm"
110       METHOD   = "POST"
111       onSubmit = "document.QuickChargeForm.submit.disabled=true; return validate_quick_charge();"
112 >
113
114 <INPUT TYPE="hidden" NAME="custnum"     VALUE="<% $cust_main ? $cust_main->custnum : '' %>">
115 <INPUT TYPE="hidden" NAME="prospectnum" VALUE="<% $prospect_main ? $prospect_main->prospectnum : '' %>">
116 <INPUT TYPE="hidden" NAME="quotationnum" VALUE="<% $quotationnum %>">
117
118 <TABLE ID="QuickChargeTable" CLASS="fsinnerbox">
119
120 % if ( $cust_pkg ) { #modify one-time charge
121
122 <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $cust_pkg->pkgnum %>">
123 % my $field = '/elements/tr-input-text.html';
124 % # don't allow changing these after the fact
125 % $field = '/elements/tr-fixed.html' if $billed;
126 <& $field,
127   label  => mt('Amount to charge'),
128   field  => 'amount',
129   value  => sprintf('%.2f',$part_pkg->option('setup_fee')),
130   size   => 8,
131   prefix => $money_char,
132 &> 
133
134 % if ( $curuser->access_right('Edit package definition costs') ) {
135   <& $field,
136     label  => mt('Cost'),
137     field  => 'setup_cost',
138     value  => sprintf('%.2f',$part_pkg->setup_cost),
139     size   => 8,
140     prefix => $money_char,
141   &> 
142 % }
143
144 %   if ( $conf->exists('invoice-unitprice') ) {
145 <& $field,
146   label => 'Quantity',
147   field => 'quantity',
148   value => $cust_pkg->quantity
149 &>
150 %   }
151
152 <& /elements/tr-select-pkg_class.html, 'curr_value' => $classnum  &>
153
154 % # crudely estimate whether any agent commission credits might exist
155 %   my @events = grep { $_->part_event->action =~ /credit/ }
156 %                $cust_pkg->cust_event;
157 %   if ( scalar @events ) {
158 <TR><TD></TD>
159   <TD><INPUT TYPE="checkbox" NAME="adjust_commission" VALUE="Y" CHECKED>
160 <% emt('Adjust commission credits if necessary') %>
161 </TD>
162 </TR>
163 %   }
164
165 % #display the future or past charge date, but don't allow changes
166 % # XXX we probably _could_ let as-yet unbilled charges be rescheduled, but
167 % # there's no compelling need yet
168 %   if ( $billed ) {
169       <& /elements/tr-fixed-date.html,
170         label => emt('Billed on'),
171         value => $cust_pkg->get('setup')
172       &>
173 %   } else {
174       <& /elements/tr-input-date-field.html,
175         {
176           name    => 'start_date',
177           label   => emt('Will be billed'),
178           value   => $cust_pkg->get('start_date'),
179           format  => $date_format,
180           noinit  => 1,
181         }
182       &>
183
184       <& /elements/tr-checkbox.html,
185         label => emt('Invoice this charge separately'),
186         field => 'separate_bill',
187         value => 'Y',
188         curr_value => $cust_pkg->get('separate_bill'),
189       &>
190       <TR>
191         <TD ALIGN="right"><% mt('Tax exempt') |h %> </TD>
192         <TD><INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y" <% $cgi->param('setuptax') ? 'CHECKED' : '' %>></TD>
193       </TR>
194
195       <& /elements/tr-select-taxclass.html, 'curr_value' => $part_pkg->get('taxclass')  &>
196
197       <& /elements/tr-select-taxproduct.html,
198         'label' => emt('Tax product'),
199         'curr_value' => $part_pkg->get('taxproductnum')
200       &>
201 % }
202
203 % } else { # new one-time charge
204
205     <TR>
206       <TH ALIGN="right"><% mt('Amount to charge') |h %> </TH>
207       <TD>
208         <% $money_char %><INPUT TYPE       = "text"
209                                 NAME       = "amount"
210                                 SIZE       = 8
211                                 VALUE      = "<% $amount %>"
212                                 onChange   = "return enable_quick_charge(event)"
213                                 onKeyPress = "return enable_quick_charge(event)"
214                          >
215       </TD>
216     </TR>
217
218 %   if ( $curuser->access_right('Edit package definition costs') ) {
219       <& /elements/tr-input-text.html,
220            label  => mt('Cost'),
221            field  => 'setup_cost',
222            value  => $setup_cost,
223            size   => 8,
224            prefix => $money_char,
225       &> 
226 %   }
227
228 %   if ( $conf->exists('invoice-unitprice') ) {
229     <TR>
230       <TH ALIGN="right"><% mt('Quantity') |h %> </TH>
231       <TD>
232         <INPUT TYPE       = "text"
233                NAME       = "quantity"
234                SIZE       = 4
235                VALUE      = "<% $quantity %>"
236                onKeyPress = "return enable_quick_charge(event)">
237       </TD>
238     </TR>
239 %   }
240
241 <& /elements/tr-select-pkg_class.html, 'curr_value' => $classnum  &>
242
243 % unless ( $quotationnum ) {
244
245     <TR>
246       <TH ALIGN="right"><% mt('Invoice now') |h %></TH>
247       <TD>
248         <INPUT TYPE  = "checkbox"
249                NAME  = "bill_now"
250                VALUE = "1"
251                <% $cgi->param('bill_now') ? 'CHECKED' : '' %>
252                onClick  = "bill_now_changed(this);"
253                onChange = "bill_now_changed(this);"
254         >
255         <% mt('with terms') |h %> 
256         <& /elements/select-terms.html,
257              'curr_value' => scalar($cgi->param('invoice_terms')),
258              'disabled'   => ( $cgi->param('bill_now') ? 0 : 1 ),
259              'agentnum'   => $cust_or_prospect->agentnum,
260         &>
261       </TD>
262     </TR>
263
264 %   # false laziness w/misc/order_pkg.html
265     <TR>
266       <TH ALIGN="right"><% mt('Charge date') |h %> </TH>
267       <TD>
268         <INPUT TYPE  = "text"
269                NAME  = "start_date"
270                SIZE  = 32
271                ID    = "start_date_text"
272                VALUE = "<% $start_date %>"
273                onKeyPress="return enable_quick_charge(event)"
274                <% $cgi->param('bill_now')
275                     ? 'STYLE = "background-color:#dddddd" DISABLED'
276                     : ''
277                %>
278         >
279         <IMG SRC   = "<%$fsurl%>images/calendar.png"
280              ID    = "start_date_button"
281              TITLE = "<% mt('Select date') |h %>"
282              STYLE = "cursor:pointer<% $cgi->param('bill_now') ? ';display:none' : '' %>"
283         >
284         <IMG SRC   = "<%$fsurl%>images/calendar-disabled.png"
285              ID    = "start_date_button_disabled"
286              <% $cgi->param('bill_now') ? '' : 'STYLE="display:none"' %>
287         >
288         <FONT SIZE=-1>(<% mt('leave blank to charge immediately') |h %>)</FONT>
289       </TD>
290     </TR>
291
292     <SCRIPT TYPE="text/javascript">
293       Calendar.setup({
294         inputField: "start_date_text",
295         ifFormat:   "<% $date_format %>",
296         button:     "start_date_button",
297         align:      "BR"
298       });
299     </SCRIPT>
300
301 <& /elements/tr-checkbox.html,
302   label => emt('Invoice this charge separately'),
303   field => 'separate_bill',
304   value => 'Y'
305 &>
306
307 % }
308
309 % if ( ! $quotationnum && $cust_main->payby =~ /^(CARD|CHEK)$/ ) {
310 %   my $what = lc(FS::payby->shortname($cust_main->payby));
311     <TR>
312       <TD ALIGN="right"><% mt("Disable automatic $what charge") |h %> </TD>
313       <TD COLSPAN=6><INPUT TYPE="checkbox" NAME="no_auto" VALUE="Y"></TD>
314     </TR>
315 % }
316
317 <TR>
318   <TH ALIGN="right"><% mt('Tax exempt') |h %> </TH>
319   <TD><INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y" <% $cgi->param('setuptax') ? 'CHECKED' : '' %>></TD>
320 </TR>
321
322 <& /elements/tr-select-taxclass.html, 'curr_value' => scalar($cgi->param('taxclass')) &>
323
324 <& /elements/tr-select-taxproduct.html,
325   'label'      => emt('Tax product'),
326   'curr_value' => scalar($cgi->param('taxproductnum')),
327 &>
328
329 <& /elements/tr-select-taxoverride.html,
330   'curr_value' => scalar($cgi->param('tax_override')),
331 &>
332
333 % } # if !$cust_pkg
334
335 <TR>
336   <TH ALIGN="right"><% mt('Description') |h %> </TH>
337   <TD>
338     <INPUT TYPE       = "text"
339            NAME       = "pkg"
340            SIZE       = "50"
341            MAXLENGTH  = "50"
342            VALUE      = "<% $pkg %>"
343            onChange   = "return enable_quick_charge(event)"
344            onKeyPress = "return enable_quick_charge(event)"
345     >
346   </TD>
347 </TR>
348
349 % my $row = 0;
350 % # quotation details are handled by quotation_pkg_detail records, added via link from view/quotation.html
351 % # the details below get attached to the part_pkg record, and there's no way to edit that from quotations
352 % unless ($quotationnum) {
353 <TR>
354   <TD></TD>
355   <TD><FONT SIZE="-1"><% mt('Optional additional description (also printed on invoice):') |h %> </FONT></TD>
356 </TR>
357
358 %   foreach (@description) {
359     <TR>
360       <TD></TD>
361       <TD>
362         <INPUT TYPE       = "text"
363                NAME       = "description<% $row %>"
364                SIZE       = "60"
365                MAXLENGTH  = "65"
366                VALUE      = "<% $_ |h %>"
367                rownum     = "<% $row %>"
368                onKeyPress = "return enable_quick_charge(event)"
369                onKeyUp    = "return possiblyAddRow(event)"
370         >
371       </TD>
372     </TR>
373 %     $row++;
374 %   }
375 % }
376
377
378 </TABLE>
379
380 <BR>
381 % my $label = $cust_pkg
382 %             ? emt('Modify one-time charge')
383 %             : emt('Add one-time charge');
384 <INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="<% $label %>" \
385 <% ($cgi->param('error') || $cust_pkg) ? '' :' DISABLED' %>>
386
387 </FORM>
388
389
390 <SCRIPT TYPE="text/javascript">
391
392   var rownum = <% $row %>;
393
394   function possiblyAddRow(e) {
395
396     if ( ( rownum - this.getAttribute('rownum') ) == 1 ) {
397       addRow();
398     }
399
400 %   if ( $curuser->option('disable_enter_submit_onetimecharge') ) {
401
402       var key;
403       if (window.event)
404             key = window.event.keyCode; //IE
405       else
406             key = e.which; //firefox, others
407
408       return (key != 13);
409
410 %   } else {
411       return true;
412 %   }
413
414   }
415
416   function addRow() {
417
418     var table = document.getElementById('QuickChargeTable');
419     var tablebody = table.getElementsByTagName('tbody').item(0);
420
421     var row = document.createElement('TR');
422
423     var empty_cell = document.createElement('TD');
424     row.appendChild(empty_cell);
425
426     var description_cell = document.createElement('TD');
427
428       //var description_input = document.createElement('INPUT');
429       var di = document.createElement('INPUT');
430       di.setAttribute('name', 'description'+rownum);
431       di.setAttribute('id',   'description'+rownum);
432       di.setAttribute('size', 60);
433       di.setAttribute('maxLength', 65);
434       di.setAttribute('rownum',   rownum);
435       di.onkeyup = possiblyAddRow;
436       di.onkeypress = enable_quick_charge;
437       description_cell.appendChild(di);
438
439     row.appendChild(description_cell);
440
441     tablebody.appendChild(row);
442
443     rownum++;
444
445   }
446
447 </SCRIPT>
448
449 </BODY>
450 </HTML>
451 <%init>
452
453 my $curuser = $FS::CurrentUser::CurrentUser;
454
455 die "access denied"
456   unless $curuser->access_right('One-time charge');
457
458 my $conf = new FS::Conf;
459 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
460 my $money_char = $conf->config('money_char') || '$';
461
462 my( $cust_main, $cust_pkg, $prospect_main, $quotationnum ) = ( '', '', '', '' );
463 if ( $cgi->param('change_pkgnum') ) {
464   # change an existing one-time charge
465   die "access denied"
466     unless $curuser->access_right('Modify one-time charge');
467
468   $cgi->param('change_pkgnum') =~ /^(\d+)$/ or die "illegal pkgnum";
469   $cust_pkg = FS::cust_pkg->by_key($1) or die "pkgnum $1 not found";
470   $cust_main = $cust_pkg->cust_main;
471 } else {
472   if ( $cgi->param('quotationnum') =~ /^(\d+)$/ ) {
473     $quotationnum = $1;
474   }
475   if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
476     $cust_main = FS::cust_main->by_key($1) or die "custnum $1 not found";
477   }
478   if ( $cgi->param('prospectnum') =~ /^(\d+)$/ ) {
479     $prospect_main = FS::prospect_main->by_key($1) or die "prospectnum $1 not found";
480   }
481   die "custnum or prospectnum must be specified"
482     unless $cust_main || $prospect_main;
483 }
484
485 my $cust_or_prospect = $cust_main || $prospect_main;
486
487 if ( $cust_main ) {
488   my $custnum = $cust_main->custnum;
489   # agent-virt
490   if (!exists($curuser->agentnums_href->{$cust_main->agentnum})) {
491     die "custnum $custnum not found";
492   }
493 } elsif ( $prospect_main ) {
494   my $prospectnum = $prospect_main->prospectnum;
495   # agent-virt
496   if (!exists($curuser->agentnums_href->{$prospect_main->agentnum})) {
497     die "prospectnum $prospectnum not found";
498   }
499 }
500
501 my $format = "%m/%d/%Y %T %z (%Z)"; #false laziness w/REAL_cust_pkg.cgi?
502 my $start_date = $cust_main ? $cust_main->next_bill_date : '';
503 $start_date = $start_date ? time2str($format, $start_date) : '';
504
505 my $amount = '';
506 if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) {
507   $amount = $1;
508 }
509
510 my $setup_cost = '';
511 if ( $cgi->param('setup_cost') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) {
512   $setup_cost = $1;
513 }
514
515 my $quantity = 1;
516 if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) {
517   $quantity = $1;
518 }
519
520 $cgi->param('pkg') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ 
521   or die 'illegal description';
522 my $pkg = $1;
523
524 my $default_terms;
525 if ( $cust_main && $cust_main->invoice_terms ) {
526   $default_terms = emt("Customer default ([_1])", $cust_main->invoice_terms);
527 } else {
528   $default_terms =
529     emt( "Default ([_1])",
530          ( $conf->config('invoice_default_terms', $cust_or_prospect->agentnum)
531              || emt('Payable upon receipt')
532          )
533        );
534 }
535
536 my @description;
537 my %param = $cgi->Vars;
538 for (my $i = 0; exists($param{"description$i"}); $i++) {
539   push @description, $param{"description$i"};
540 }
541
542 my $classnum;
543 if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
544   $classnum = $1;
545 }
546
547 my $part_pkg;
548 my $billed = 0;
549
550 if ( $cust_pkg ) { # set defaults
551   $part_pkg = $cust_pkg->part_pkg;
552   $pkg ||= $part_pkg->pkg;
553   $classnum ||= $part_pkg->classnum;
554   if (!@description) {
555     for (my $i = 0; $i < ($part_pkg->option('additional_count',1) || 0); $i++) 
556     {
557       push @description, $part_pkg->option("additional_info$i",1);
558     }
559   }
560   $billed = $cust_pkg->get('setup') ? 1 : 0;
561 }
562
563 </%init>