summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2015-01-18 19:43:48 -0800
committerIvan Kohler <ivan@freeside.biz>2015-01-18 19:43:48 -0800
commitf92a083465019a7224d912cd78b6881f8aef1b52 (patch)
tree4816e5b52373ebaf62681e19bec8ed304f99f5e6
parent03c12b4dabfcaabc218f39ee13557edebc13931d (diff)
one-time charges on quotations, RT#25561
-rw-r--r--FS/FS/quotation.pm130
-rw-r--r--httemplate/edit/process/quick-charge.cgi71
-rw-r--r--httemplate/edit/quick-charge.html155
-rw-r--r--httemplate/elements/one_time_charge_link.html (renamed from httemplate/view/cust_main/one_time_charge_link.html)29
-rwxr-xr-xhttemplate/view/cust_main/packages.html2
-rwxr-xr-xhttemplate/view/quotation.html8
6 files changed, 293 insertions, 102 deletions
diff --git a/FS/FS/quotation.pm b/FS/FS/quotation.pm
index 44b72f4..75a592d 100644
--- a/FS/FS/quotation.pm
+++ b/FS/FS/quotation.pm
@@ -7,8 +7,11 @@ use Tie::RefHash;
use FS::CurrentUser;
use FS::UID qw( dbh );
use FS::Maketext qw( emt );
+use FS::Record qw( qsearchs );
use FS::cust_main;
use FS::cust_pkg;
+use FS::quotation_pkg;
+use FS::type_pkgs;
=head1 NAME
@@ -351,6 +354,133 @@ sub order {
}
+=item charge
+
+One-time charges, like FS::cust_main::charge()
+
+=cut
+
+#super false laziness w/cust_main::charge
+sub charge {
+ my $self = shift;
+ my ( $amount, $setup_cost, $quantity, $start_date, $classnum );
+ my ( $pkg, $comment, $additional );
+ my ( $setuptax, $taxclass ); #internal taxes
+ my ( $taxproduct, $override ); #vendor (CCH) taxes
+ my $no_auto = '';
+ my $cust_pkg_ref = '';
+ my ( $bill_now, $invoice_terms ) = ( 0, '' );
+ my $locationnum;
+ if ( ref( $_[0] ) ) {
+ $amount = $_[0]->{amount};
+ $setup_cost = $_[0]->{setup_cost};
+ $quantity = exists($_[0]->{quantity}) ? $_[0]->{quantity} : 1;
+ $start_date = exists($_[0]->{start_date}) ? $_[0]->{start_date} : '';
+ $no_auto = exists($_[0]->{no_auto}) ? $_[0]->{no_auto} : '';
+ $pkg = exists($_[0]->{pkg}) ? $_[0]->{pkg} : 'One-time charge';
+ $comment = exists($_[0]->{comment}) ? $_[0]->{comment}
+ : '$'. sprintf("%.2f",$amount);
+ $setuptax = exists($_[0]->{setuptax}) ? $_[0]->{setuptax} : '';
+ $taxclass = exists($_[0]->{taxclass}) ? $_[0]->{taxclass} : '';
+ $classnum = exists($_[0]->{classnum}) ? $_[0]->{classnum} : '';
+ $additional = $_[0]->{additional} || [];
+ $taxproduct = $_[0]->{taxproductnum};
+ $override = { '' => $_[0]->{tax_override} };
+ $cust_pkg_ref = exists($_[0]->{cust_pkg_ref}) ? $_[0]->{cust_pkg_ref} : '';
+ $bill_now = exists($_[0]->{bill_now}) ? $_[0]->{bill_now} : '';
+ $invoice_terms = exists($_[0]->{invoice_terms}) ? $_[0]->{invoice_terms} : '';
+ $locationnum = $_[0]->{locationnum} || $self->ship_locationnum;
+ } else {
+ $amount = shift;
+ $setup_cost = '';
+ $quantity = 1;
+ $start_date = '';
+ $pkg = @_ ? shift : 'One-time charge';
+ $comment = @_ ? shift : '$'. sprintf("%.2f",$amount);
+ $setuptax = '';
+ $taxclass = @_ ? shift : '';
+ $additional = [];
+ }
+
+ local $SIG{HUP} = 'IGNORE';
+ local $SIG{INT} = 'IGNORE';
+ local $SIG{QUIT} = 'IGNORE';
+ local $SIG{TERM} = 'IGNORE';
+ local $SIG{TSTP} = 'IGNORE';
+ local $SIG{PIPE} = 'IGNORE';
+
+ my $oldAutoCommit = $FS::UID::AutoCommit;
+ local $FS::UID::AutoCommit = 0;
+ my $dbh = dbh;
+
+ my $part_pkg = new FS::part_pkg ( {
+ 'pkg' => $pkg,
+ 'comment' => $comment,
+ 'plan' => 'flat',
+ 'freq' => 0,
+ 'disabled' => 'Y',
+ 'classnum' => ( $classnum ? $classnum : '' ),
+ 'setuptax' => $setuptax,
+ 'taxclass' => $taxclass,
+ 'taxproductnum' => $taxproduct,
+ 'setup_cost' => $setup_cost,
+ } );
+
+ my %options = ( ( map { ("additional_info$_" => $additional->[$_] ) }
+ ( 0 .. @$additional - 1 )
+ ),
+ 'additional_count' => scalar(@$additional),
+ 'setup_fee' => $amount,
+ );
+
+ my $error = $part_pkg->insert( options => \%options,
+ tax_overrides => $override,
+ );
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ my $pkgpart = $part_pkg->pkgpart;
+
+ #DIFF
+ my %type_pkgs = ( 'typenum' => $self->cust_or_prospect->agent->typenum, 'pkgpart' => $pkgpart );
+
+ unless ( qsearchs('type_pkgs', \%type_pkgs ) ) {
+ my $type_pkgs = new FS::type_pkgs \%type_pkgs;
+ $error = $type_pkgs->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
+ #except for DIFF, eveything above is idential to cust_main version
+ #but below is our own thing pretty much (adding a quotation package instead
+ # of ordering a customer package, no "bill now")
+
+ my $quotation_pkg = new FS::quotation_pkg ( {
+ 'quotationnum' => $self->quotationnum,
+ 'pkgpart' => $pkgpart,
+ 'quantity' => $quantity,
+ #'start_date' => $start_date,
+ #'no_auto' => $no_auto,
+ 'locationnum'=> $locationnum,
+ } );
+
+ $error = $quotation_pkg->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ #} elsif ( $cust_pkg_ref ) {
+ # ${$cust_pkg_ref} = $cust_pkg;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+ return '';
+
+}
+
=item disable
Disables this quotation (sets disabled to Y, which hides the quotation on
diff --git a/httemplate/edit/process/quick-charge.cgi b/httemplate/edit/process/quick-charge.cgi
index 6de746e..c130a55 100644
--- a/httemplate/edit/process/quick-charge.cgi
+++ b/httemplate/edit/process/quick-charge.cgi
@@ -24,15 +24,20 @@ for ( my $row = 0; exists($param->{"description$row"}); $row++ ) {
if ($param->{"description$row"} =~ /\S/);
}
-$param->{"custnum"} =~ /^(\d+)$/
- or $error .= "Illegal customer number " . $param->{"custnum"} . " ";
-my $custnum = $1;
-
-my $cust_main = FS::cust_main->by_key($custnum)
- or die "custnum $custnum not found";
-
-exists($curuser->agentnums_href->{$cust_main->agentnum})
- or die "access denied";
+my( $cust_main, $prospect_main, $quotation ) = ( '', '', '' );
+if ( $cgi->param('quotationnum') =~ /^(\d+)$/ ) {
+ $quotation = FS::quotation->by_key($1) or die "quotationnum $1 not found";
+}
+if ( $param->{"custnum"} =~ /^(\d+)$/ ) {
+ $cust_main = FS::cust_main->by_key($1) or die "custnum $1 not found";
+ exists($curuser->agentnums_href->{$cust_main->agentnum})
+ or die "access denied";
+}
+if ( $param->{"prospectnum"} =~ /^(\d+)$/ ) {
+ $prospect_main = FS::prospect_main->by_key($1) or die "prospectnum $1 not found";
+ exists($curuser->agentnums_href->{$prospect_main->agentnum})
+ or die "access denied";
+}
my $message;
@@ -106,30 +111,32 @@ if ( $param->{'pkgnum'} =~ /^(\d+)$/ ) { #modifying an existing one-time charge
$cgi->param('taxclass', '');
}
- unless ( $error ) {
- my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
- or $error .= "Unknown customer number $custnum. ";
-
- $error ||= $cust_main->charge( {
- 'amount' => $amount,
- 'setup_cost' => $setup_cost,
- 'quantity' => $quantity,
- 'bill_now' => scalar($cgi->param('bill_now')),
- 'invoice_terms' => scalar($cgi->param('invoice_terms')),
- 'start_date' => ( scalar($cgi->param('start_date'))
- ? parse_datetime($cgi->param('start_date'))
- : ''
- ),
- 'no_auto' => scalar($cgi->param('no_auto')),
- 'pkg' => scalar($cgi->param('pkg')),
- 'setuptax' => scalar($cgi->param('setuptax')),
- 'taxclass' => scalar($cgi->param('taxclass')),
- 'taxproductnum' => scalar($cgi->param('taxproductnum')),
- 'tax_override' => $override,
- 'classnum' => scalar($cgi->param('classnum')),
- 'additional' => \@description,
- } );
+ my %charge = (
+ 'amount' => $amount,
+ 'setup_cost' => $setup_cost,
+ 'quantity' => $quantity,
+ 'bill_now' => scalar($cgi->param('bill_now')),
+ 'invoice_terms' => scalar($cgi->param('invoice_terms')),
+ 'start_date' => ( scalar($cgi->param('start_date'))
+ ? parse_datetime($cgi->param('start_date'))
+ : ''
+ ),
+ 'no_auto' => scalar($cgi->param('no_auto')),
+ 'pkg' => scalar($cgi->param('pkg')),
+ 'setuptax' => scalar($cgi->param('setuptax')),
+ 'taxclass' => scalar($cgi->param('taxclass')),
+ 'taxproductnum' => scalar($cgi->param('taxproductnum')),
+ 'tax_override' => $override,
+ 'classnum' => scalar($cgi->param('classnum')),
+ 'additional' => \@description,
+ );
+
+ if ( $quotation ) {
+ $error ||= $quotation->charge( \%charge );
+ } else {
+ $error ||= $cust_main->charge( \%charge );
}
+
}
</%init>
diff --git a/httemplate/edit/quick-charge.html b/httemplate/edit/quick-charge.html
index ec1a580..dfaf404 100644
--- a/httemplate/edit/quick-charge.html
+++ b/httemplate/edit/quick-charge.html
@@ -100,7 +100,9 @@ function bill_now_changed (what) {
onSubmit = "document.QuickChargeForm.submit.disabled=true; return validate_quick_charge();"
>
-<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
+<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $cust_main ? $cust_main->custnum : '' %>">
+<INPUT TYPE="hidden" NAME="prospectnum" VALUE="<% $prospect_main ? $prospect_main->prospectnum : '' %>">
+<INPUT TYPE="hidden" NAME="quotationnum" VALUE="<% $quotationnum %>">
<TABLE ID="QuickChargeTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 STYLE="background-color: #cccccc">
@@ -209,63 +211,67 @@ function bill_now_changed (what) {
<& /elements/tr-select-pkg_class.html, 'curr_value' => $classnum &>
-<TR>
- <TD ALIGN="right"><% mt('Invoice now') |h %></TD>
- <TD>
- <INPUT TYPE = "checkbox"
- NAME = "bill_now"
- VALUE = "1"
- <% $cgi->param('bill_now') ? 'CHECKED' : '' %>
- onClick = "bill_now_changed(this);"
- onChange = "bill_now_changed(this);"
- >
- <% mt('with terms') |h %>
- <& /elements/select-terms.html,
- 'curr_value' => scalar($cgi->param('invoice_terms')),
- 'disabled' => ( $cgi->param('bill_now') ? 0 : 1 ),
- 'agentnum' => $cust_main->agentnum,
- &>
- </TD>
-</TR>
+% unless ( $quotationnum ) {
-%# false laziness w/misc/order_pkg.html
-<TR>
- <TD ALIGN="right"><% mt('Charge date') |h %> </TD>
- <TD>
- <INPUT TYPE = "text"
- NAME = "start_date"
- SIZE = 32
- ID = "start_date_text"
- VALUE = "<% $start_date %>"
- onKeyPress="return enable_quick_charge(event)"
- <% $cgi->param('bill_now')
- ? 'STYLE = "background-color:#dddddd" DISABLED'
- : ''
- %>
- >
- <IMG SRC = "<%$fsurl%>images/calendar.png"
- ID = "start_date_button"
- TITLE = "<% mt('Select date') |h %>"
- STYLE = "cursor:pointer<% $cgi->param('bill_now') ? ';display:none' : '' %>"
- >
- <IMG SRC = "<%$fsurl%>images/calendar-disabled.png"
- ID = "start_date_button_disabled"
- <% $cgi->param('bill_now') ? '' : 'STYLE="display:none"' %>
- >
- <FONT SIZE=-1>(<% mt('leave blank to charge immediately') |h %>)</FONT>
- </TD>
-</TR>
+ <TR>
+ <TD ALIGN="right"><% mt('Invoice now') |h %></TD>
+ <TD>
+ <INPUT TYPE = "checkbox"
+ NAME = "bill_now"
+ VALUE = "1"
+ <% $cgi->param('bill_now') ? 'CHECKED' : '' %>
+ onClick = "bill_now_changed(this);"
+ onChange = "bill_now_changed(this);"
+ >
+ <% mt('with terms') |h %>
+ <& /elements/select-terms.html,
+ 'curr_value' => scalar($cgi->param('invoice_terms')),
+ 'disabled' => ( $cgi->param('bill_now') ? 0 : 1 ),
+ 'agentnum' => $cust_or_prospect->agentnum,
+ &>
+ </TD>
+ </TR>
-<SCRIPT TYPE="text/javascript">
- Calendar.setup({
- inputField: "start_date_text",
- ifFormat: "<% $date_format %>",
- button: "start_date_button",
- align: "BR"
- });
-</SCRIPT>
+% # false laziness w/misc/order_pkg.html
+ <TR>
+ <TD ALIGN="right"><% mt('Charge date') |h %> </TD>
+ <TD>
+ <INPUT TYPE = "text"
+ NAME = "start_date"
+ SIZE = 32
+ ID = "start_date_text"
+ VALUE = "<% $start_date %>"
+ onKeyPress="return enable_quick_charge(event)"
+ <% $cgi->param('bill_now')
+ ? 'STYLE = "background-color:#dddddd" DISABLED'
+ : ''
+ %>
+ >
+ <IMG SRC = "<%$fsurl%>images/calendar.png"
+ ID = "start_date_button"
+ TITLE = "<% mt('Select date') |h %>"
+ STYLE = "cursor:pointer<% $cgi->param('bill_now') ? ';display:none' : '' %>"
+ >
+ <IMG SRC = "<%$fsurl%>images/calendar-disabled.png"
+ ID = "start_date_button_disabled"
+ <% $cgi->param('bill_now') ? '' : 'STYLE="display:none"' %>
+ >
+ <FONT SIZE=-1>(<% mt('leave blank to charge immediately') |h %>)</FONT>
+ </TD>
+ </TR>
+
+ <SCRIPT TYPE="text/javascript">
+ Calendar.setup({
+ inputField: "start_date_text",
+ ifFormat: "<% $date_format %>",
+ button: "start_date_button",
+ align: "BR"
+ });
+ </SCRIPT>
-% if ( $cust_main->payby =~ /^(CARD|CHEK)$/ ) {
+% }
+
+% if ( ! $quotationnum && $cust_main->payby =~ /^(CARD|CHEK)$/ ) {
% my $what = lc(FS::payby->shortname($cust_main->payby));
<TR>
<TD ALIGN="right"><% mt("Disable automatic $what charge") |h %> </TD>
@@ -409,7 +415,7 @@ my $conf = new FS::Conf;
my $date_format = $conf->config('date_format') || '%m/%d/%Y';
my $money_char = $conf->config('money_char') || '$';
-my ($cust_main, $cust_pkg);
+my( $cust_main, $cust_pkg, $prospect_main, $quotationnum ) = ( '', '', '', '' );
if ( $cgi->param('change_pkgnum') ) {
# change an existing one-time charge
die "access denied"
@@ -419,18 +425,37 @@ if ( $cgi->param('change_pkgnum') ) {
$cust_pkg = FS::cust_pkg->by_key($1) or die "pkgnum $1 not found";
$cust_main = $cust_pkg->cust_main;
} else {
- $cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
- $cust_main = FS::cust_main->by_key($1) or die "custnum $1 not found";
+ if ( $cgi->param('quotationnum') =~ /^(\d+)$/ ) {
+ $quotationnum = $1;
+ }
+ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
+ $cust_main = FS::cust_main->by_key($1) or die "custnum $1 not found";
+ }
+ if ( $cgi->param('prospectnum') =~ /^(\d+)$/ ) {
+ $prospect_main = FS::prospect_main->by_key($1) or die "prospectnum $1 not found";
+ }
+ die "custnum or prospectnum must be specified"
+ unless $cust_main || $prospect_main;
}
-my $custnum = $cust_main->custnum;
-# agent-virt
-if (!exists($curuser->agentnums_href->{$cust_main->agentnum})) {
- die "custnum $custnum not found";
+my $cust_or_prospect = $cust_main || $prospect_main;
+
+if ( $cust_main ) {
+ my $custnum = $cust_main->custnum;
+ # agent-virt
+ if (!exists($curuser->agentnums_href->{$cust_main->agentnum})) {
+ die "custnum $custnum not found";
+ }
+} elsif ( $prospect_main ) {
+ my $prospectnum = $prospect_main->prospectnum;
+ # agent-virt
+ if (!exists($curuser->agentnums_href->{$prospect_main->agentnum})) {
+ die "prospectnum $prospectnum not found";
+ }
}
my $format = "%m/%d/%Y %T %z (%Z)"; #false laziness w/REAL_cust_pkg.cgi?
-my $start_date = $cust_main->next_bill_date;
+my $start_date = $cust_main ? $cust_main->next_bill_date : '';
$start_date = $start_date ? time2str($format, $start_date) : '';
my $amount = '';
@@ -453,12 +478,12 @@ $cgi->param('pkg') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/
my $pkg = $1;
my $default_terms;
-if ( $cust_main->invoice_terms ) {
+if ( $cust_main && $cust_main->invoice_terms ) {
$default_terms = emt("Customer default ([_1])", $cust_main->invoice_terms);
} else {
$default_terms =
emt( "Default ([_1])",
- ( $conf->config('invoice_default_terms', $cust_main->agentnum)
+ ( $conf->config('invoice_default_terms', $cust_or_prospect->agentnum)
|| emt('Payable upon receipt')
)
);
diff --git a/httemplate/view/cust_main/one_time_charge_link.html b/httemplate/elements/one_time_charge_link.html
index 1efd2d0..4ef5ede 100644
--- a/httemplate/view/cust_main/one_time_charge_link.html
+++ b/httemplate/elements/one_time_charge_link.html
@@ -1,3 +1,16 @@
+<%doc>
+
+Example:
+
+ <& /elements/one_time_charge_link.html,
+
+ #one of these is required
+ 'custnum' => $custnum,
+ 'prospectnum' => $prospectnum,
+
+ &>
+
+</%doc>
<SCRIPT TYPE="text/javascript">
function taxproductmagic(which) {
@@ -72,10 +85,16 @@ function taxoverridequickchargemagic() {
</SCRIPT>
-<FORM NAME='quickcharge' STYLE="margin:0; padding:0; display:inline"><INPUT NAME="taxproductnum" ID="taxproductnum" TYPE="hidden"><INPUT NAME="tax_override" ID="tax_override" TYPE="hidden"><INPUT NAME="charge_storage" ID="charge_storage" TYPE="hidden"><INPUT NAME="taxproductnum_description" ID="taxproductnum_description" TYPE="hidden"></FORM>
+<FORM NAME='quickcharge' STYLE="margin:0; padding:0; display:inline">
+% for (qw(
+% taxproductnum tax_override charge_storage taxproductnum_description
+% )) {
+ <INPUT NAME="<% $_ %>" ID="<% $_ %>" TYPE="hidden">
+% }
+</FORM>
<% include('/elements/popup_link.html', {
- 'action' => $p.'edit/quick-charge.html?custnum='. $cust_main->custnum,
+ 'action' => $p. 'edit/quick-charge.html?'. $query,
'label' => emt('One-time charge'),
'actionlabel' => emt('One-time charge'),
'color' => '#333399',
@@ -86,6 +105,10 @@ function taxoverridequickchargemagic() {
<%init>
-my($cust_main) = @_;
+my %opt = @_;
+
+my $query = $opt{custnum} ? 'custnum='.$opt{custnum}
+ : 'prospectnum='.$opt{prospectnum};
+$query .= ';quotationnum='.$opt{quotationnum} if $opt{quotationnum};
</%init>
diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html
index 228b04e..9a2332b 100755
--- a/httemplate/view/cust_main/packages.html
+++ b/httemplate/view/cust_main/packages.html
@@ -96,7 +96,7 @@ if ( el ) el.scrollIntoView(true);
% && $conf->config('payby-default') ne 'HIDE'
% ) {
<% $s++ ? ' | ' : '' %>
- <& one_time_charge_link.html, $cust_main &>
+ <& /elements/one_time_charge_link.html, 'custnum'=>$cust_main->custnum &>
% }
% if ( $curuser->access_right('Bulk move customer services') ) {
diff --git a/httemplate/view/quotation.html b/httemplate/view/quotation.html
index 81c7cdd..bd998bb 100755
--- a/httemplate/view/quotation.html
+++ b/httemplate/view/quotation.html
@@ -17,9 +17,15 @@ function areyousure(href, message) {
'actionlabel' => emt('Add package'),
map { $_ => $quotation->$_ } qw( quotationnum custnum prospectnum )
&>
- <BR><BR>
% }
+% if ( $curuser->access_right('One-time charge') ) {
+ | <& /elements/one_time_charge_link.html,
+ map { $_ => $quotation->$_ } qw( quotationnum custnum prospectnum )
+ &>
+% }
+ <BR><BR>
+
% if ( 1 ) { #if ( $curuser->access_right('Send quotations') )
<& /elements/popup_link.html,