1 <& /elements/header-popup.html, 'Credit line items' &>
3 <FORM ACTION="process/credit-cust_bill_pkg.html" METHOD="POST">
4 <INPUT TYPE="hidden" NAME="crednum" VALUE="">
5 <INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum |h %>">
6 <INPUT TYPE="hidden" NAME="paybatch" VALUE="">
7 <INPUT TYPE="hidden" NAME="_date" VALUE="<% time %>">
11 %# foreach my $cust_bill_pkg ( @cust_bill_pkg ) {
12 % foreach my $item ( @items ) {
13 % my( $setuprecur, $cust_bill_pkg ) = @$item;
15 % my $method = $setuprecur eq 'setup' ? 'setup' : 'recur';
16 % my $amount = $cust_bill_pkg->$method();
17 % my $credited = $cust_bill_pkg->credited('', '', 'setuprecur'=>$method);
18 % $amount -= $credited;
19 % $amount = sprintf('%.2f', $amount);
20 % next unless $amount > 0;
22 % if ( $cust_bill_pkg->invnum ne $old_invnum ) {
23 <TR><TD COLSPAN=4 BGCOLOR="#f8f8f8"> </TD></TR>
24 <TR><TH COLSPAN=4 BGCOLOR="#f8f8f8" ALIGN="left">Invoice #<% $cust_bill_pkg->invnum %> - <% time2str($date_format, $cust_bill_pkg->cust_bill->_date) %></TD></TR>
25 % $old_invnum = $cust_bill_pkg->invnum;
28 % my $el_name = 'billpkgnum'. $cust_bill_pkg->billpkgnum. '-'. $setuprecur;
31 <INPUT TYPE = "checkbox"
32 NAME = "<% $el_name %>"
34 onClick = "calc_total(this)"
35 data-billpkgnum = "<% $cust_bill_pkg->billpkgnum %>"
36 data-setuprecur = "<% $setuprecur %>"
39 <TD BGCOLOR="#ffffff"><% $cust_bill_pkg->desc |h %></TD>
40 %# show one-time/setup vs recur vs usage?
41 <TD BGCOLOR="#ffffff" ALIGN="right"><% $money_char. $amount %></TD>
43 <% $money_char %><INPUT TYPE = "text"
44 NAME = "<% $el_name %>-amount"
45 ID = "<% $el_name %>-amount"
46 VALUE = "<% $amount %>"
48 onChange = "calc_total(this)"
49 STYLE = "text-align:right;"
57 <TR><TD COLSPAN=4 BGCOLOR="#f8f8f8"> </TD></TR>
60 <TD ALIGN="right" COLSPAN=2>Subtotal: </TD>
61 <TD ALIGN="right" ID="subtotal_td"><% $money_char %><% sprintf('%.2f', 0) %></TD>
65 <TD ALIGN="right" COLSPAN=2>Taxes: </TD>
66 <TD ALIGN="right" ID="taxtotal_td"><% $money_char %><% sprintf('%.2f', 0) %></TD>
70 <TH ALIGN="right" COLSPAN=2>Total credit amount: </TD>
71 <TH ALIGN="right" ID="total_td"><% $money_char %><% sprintf('%.2f', 0) %></TD>
76 <INPUT TYPE="hidden" NAME="amount" ID="total_el" VALUE="0.00">
80 <& /elements/tr-select-reason.html,
81 'field' => 'reasonnum',
82 'reason_class' => 'R',
83 #XXX reconcile both this and show_taxes wanteding to enable this
84 'id' => 'select_reason',
85 'control_button' => "document.getElementById('credit_button')",
90 <TD ALIGN="right"><% mt('Additional info') |h %></TD>
92 <INPUT TYPE="text" NAME="addlinfo" VALUE="<% $cgi->param('addlinfo') |h %>">
96 % if ( $conf->exists('credits-auto-apply-disable') ) {
97 <INPUT TYPE="HIDDEN" NAME="apply" VALUE="no">
100 <TD ALIGN="right"><% mt('Apply to selected line items') |h %></TD>
101 <TD><SELECT NAME="apply"><OPTION VALUE="yes" SELECTED><% mt('yes') |h %><OPTION><% mt('no') |h %></SELECT></TD>
108 <INPUT TYPE="submit" ID="credit_button" VALUE="Credit" DISABLED>
112 <% include( '/elements/xmlhttp.html',
113 'url' => $p.'misc/xmlhttp-cust_bill_pkg-calculate_taxes.html',
114 'subs' => [ 'calculate_taxes' ],
117 <SCRIPT TYPE="text/javascript">
119 document.getElementById('select_reason').disabled = true;
120 // start it disabled because no line items are selected yet
121 function show_taxes(arg) {
122 var argsHash = eval('(' + arg + ')');
124 //XXX add an 'ErrorMessage' section to the HTML and re-enable
125 //var error = argsHash['error'];
127 //var paragraph = document.getElementById('ErrorMessage');
129 // paragraph.innerHTML = 'Error: ' + error;
130 // paragraph.style.color = '#ff0000';
132 // paragraph.innerHTML = '';
135 var taxlines = argsHash['taxlines'];
137 //XXX display the tax lines? just a total will do for now
139 // var table = document.getElementById('ApplicationTable');
141 // var aFoundRow = 0;
142 // for (i = 0; taxlines[i]; i++) {
143 // var itemdesc = taxlines[i][0];
144 // var locnum = taxlines[i][2];
145 // if (taxlines[i][3]) {
146 // locnum = taxlines[i][3];
150 // for (var row = 2; table.rows[row]; row++) {
151 // var inputs = table.rows[row].getElementsByTagName('input');
152 // if (! inputs.length) {
153 // while ( table.rows[row] ) {
154 // table.deleteRow(row);
158 // if ( inputs.item(4).value == itemdesc && inputs.item(2).value == locnum )
160 // inputs.item(0).value = taxlines[i][1];
161 // aFoundRow = found = row;
166 // var row = table.insertRow(table.rows.length);
167 // var warning_cell = document.createElement('TD');
168 // warning_cell.style.color = '#ff0000';
169 // warning_cell.colSpan = 2;
170 // warning_cell.innerHTML = 'Calculated Tax - ' + itemdesc + ' - ' +
171 // taxlines[i][1] + ' will not be applied';
172 // row.appendChild(warning_cell);
177 // sub_changed(table.rows[aFoundRow].getElementsByTagName('input').item(0));
180 var subtotal = parseFloat( argsHash['subtotal'] );
182 var taxtotal = parseFloat( argsHash['taxtotal'] );
183 document.getElementById('taxtotal_td').innerHTML =
184 '<% $money_char %>' + taxtotal.toFixed(2);
186 var total = subtotal + taxtotal;
187 document.getElementById('total_td').innerHTML =
188 '<% $money_char %>' + total.toFixed(2);
189 document.getElementById('total_el').value = total.toFixed(2);
191 //XXX reconcile both this and the reason selector wanteding to enable this
193 //document.getElementById('credit_button').disabled = false;
194 document.getElementById('select_reason').disabled = false;
199 function calc_total(what) {
201 //document.getElementById('credit_button').disabled = true;
202 document.getElementById('select_reason').disabled = true;
205 // bah, a pain, just using an attribute var re = /^billpkgnum(\d+)$/;
207 var el = what.form.elements;
208 var billpkgnums = [];
209 var setuprecurs = [];
211 for (var i=0; i<el.length; i++) {
213 if ( el[i].type == 'checkbox' ) {
214 var amount_el = document.getElementById( el[i].id + '-amount' );
215 if ( el[i].checked ) {
216 amount_el.disabled = false;
217 var amount = amount_el.value;
218 subtotal += parseFloat( amount );
219 amounts.push( amount );
220 billpkgnums.push( el[i].getAttribute('data-billpkgnum') );
221 setuprecurs.push( el[i].getAttribute('data-setuprecur') );
223 amount_el.disabled = true;
229 document.getElementById('subtotal_td').innerHTML =
230 '<% $money_char %>' + subtotal.toFixed(2);
232 var args = new Array(
233 'custnum', '<% $custnum %>',
234 'subtotal', subtotal,
235 'billpkgnums', billpkgnums.join(),
236 'setuprecurs', setuprecurs.join(),
237 'amounts', amounts.join()
240 calculate_taxes( args, show_taxes );
247 my $curuser = $FS::CurrentUser::CurrentUser;
248 die "access denied" unless $curuser->access_right('Credit line items');
250 #a tiny bit of false laziness w/search/cust_bill_pkg.cgi, but we're pretty
251 # specialized and a piece of UI, not a report
252 #slightly more false laziness w/httemplate/edit/elements/ApplicationCommon.html
253 # show_taxes & calc_total here/do_calculate_tax there
255 my $conf = new FS::Conf;
256 my $money_char = $conf->config('money_char') || '$';
257 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
259 $cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
262 my $cust_main = qsearchs({
263 'table' => 'cust_main',
264 'hashref' => { 'custnum' => $custnum },
265 'extra_sql' => ' AND '. $curuser->agentnums_sql,
266 }) or die 'unknown customer';
268 my @cust_bill_pkg = qsearch({
269 'select' => 'cust_bill_pkg.*',
270 'table' => 'cust_bill_pkg',
271 'addl_from' => 'LEFT JOIN cust_bill USING (invnum)',
272 'extra_sql' => "WHERE custnum = $custnum AND pkgnum != 0",
273 'order_by' => 'ORDER BY invnum ASC, billpkgnum ASC',
276 my @items = map { my %hash = $_->disintegrate;
277 map [ $_, $hash{$_} ],
282 #omit line items which have been previously credited? would be nice