summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS/cust_pkg.pm269
-rw-r--r--FS/FS/cust_pkg_discount.pm20
-rw-r--r--httemplate/edit/process/quick-cust_pkg.cgi1
-rw-r--r--httemplate/elements/select-discount.html20
-rw-r--r--httemplate/elements/tr-select-discount.html27
-rw-r--r--httemplate/misc/order_pkg.html8
-rw-r--r--httemplate/view/cust_main/packages/package.html2
-rw-r--r--httemplate/view/cust_main/packages/status.html39
8 files changed, 266 insertions, 120 deletions
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index a95a67d2e..5cdca09ac 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -25,6 +25,8 @@ use FS::reg_code;
use FS::part_svc;
use FS::cust_pkg_reason;
use FS::reason;
+use FS::cust_pkg_discount;
+use FS::discount;
use FS::UI::Web;
# need to 'use' these instead of 'require' in sub { cancel, suspend, unsuspend,
@@ -274,6 +276,22 @@ sub insert {
'params' => $self->refnum,
);
+ if ( $self->discountnum ) {
+ #XXX new/custom discount case
+ my $cust_pkg_discount = new FS::cust_pkg_discount {
+ 'pkgnum' => $self->pkgnum,
+ 'discountnum' => $self->discountnum,
+ 'months_used' => 0,
+ 'end_date' => '', #XXX
+ 'otaker' => $self->otaker,
+ };
+ my $error = $cust_pkg_discount->insert;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+ }
+
#if ( $self->reg_code ) {
# my $reg_code = qsearchs('reg_code', { 'code' => $self->reg_code } );
# $error = $reg_code->delete;
@@ -2211,6 +2229,141 @@ sub reexport {
}
+=item insert_reason
+
+Associates this package with a (suspension or cancellation) reason (see
+L<FS::cust_pkg_reason>, possibly inserting a new reason on the fly (see
+L<FS::reason>).
+
+Available options are:
+
+=over 4
+
+=item reason
+
+can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason.
+
+=item reason_otaker
+
+the access_user (see L<FS::access_user>) providing the reason
+
+=item date
+
+a unix timestamp
+
+=item action
+
+the action (cancel, susp, adjourn, expire) associated with the reason
+
+=back
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub insert_reason {
+ my ($self, %options) = @_;
+
+ my $otaker = $options{reason_otaker} ||
+ $FS::CurrentUser::CurrentUser->username;
+
+ my $reasonnum;
+ if ( $options{'reason'} =~ /^(\d+)$/ ) {
+
+ $reasonnum = $1;
+
+ } elsif ( ref($options{'reason'}) ) {
+
+ return 'Enter a new reason (or select an existing one)'
+ unless $options{'reason'}->{'reason'} !~ /^\s*$/;
+
+ my $reason = new FS::reason({
+ 'reason_type' => $options{'reason'}->{'typenum'},
+ 'reason' => $options{'reason'}->{'reason'},
+ });
+ my $error = $reason->insert;
+ return $error if $error;
+
+ $reasonnum = $reason->reasonnum;
+
+ } else {
+ return "Unparsable reason: ". $options{'reason'};
+ }
+
+ my $cust_pkg_reason =
+ new FS::cust_pkg_reason({ 'pkgnum' => $self->pkgnum,
+ 'reasonnum' => $reasonnum,
+ 'otaker' => $otaker,
+ 'action' => substr(uc($options{'action'}),0,1),
+ 'date' => $options{'date'}
+ ? $options{'date'}
+ : time,
+ });
+
+ $cust_pkg_reason->insert;
+}
+
+=item set_usage USAGE_VALUE_HASHREF
+
+USAGE_VALUE_HASHREF is a hashref of svc_acct usage columns and the amounts
+to which they should be set (see L<FS::svc_acct>). Currently seconds,
+upbytes, downbytes, and totalbytes are appropriate keys.
+
+All svc_accts which are part of this package have their values reset.
+
+=cut
+
+sub set_usage {
+ my ($self, $valueref, %opt) = @_;
+
+ foreach my $cust_svc ($self->cust_svc){
+ my $svc_x = $cust_svc->svc_x;
+ $svc_x->set_usage($valueref, %opt)
+ if $svc_x->can("set_usage");
+ }
+}
+
+=item recharge USAGE_VALUE_HASHREF
+
+USAGE_VALUE_HASHREF is a hashref of svc_acct usage columns and the amounts
+to which they should be set (see L<FS::svc_acct>). Currently seconds,
+upbytes, downbytes, and totalbytes are appropriate keys.
+
+All svc_accts which are part of this package have their values incremented.
+
+=cut
+
+sub recharge {
+ my ($self, $valueref) = @_;
+
+ foreach my $cust_svc ($self->cust_svc){
+ my $svc_x = $cust_svc->svc_x;
+ $svc_x->recharge($valueref)
+ if $svc_x->can("recharge");
+ }
+}
+
+=item cust_pkg_discount
+
+=cut
+
+sub cust_pkg_discount {
+ my $self = shift;
+ qsearch('cust_pkg_discount', { 'pkgnum' => $self->pkgnum } );
+}
+
+=item cust_pkg_discount_active
+
+=cut
+
+sub cust_pkg_discount_active {
+ my $self = shift;
+ grep { my $d = $_->discount;
+ ! $d->months || $_->months_used < $d->months; # XXX also end date
+ }
+ $self->cust_pkg_discount;
+}
+
=back
=head1 CLASS METHODS
@@ -2631,7 +2784,7 @@ sub search {
'cust_pkg.*',
( map "part_pkg.$_", qw( pkg freq ) ),
'pkg_class.classname',
- 'cust_main.custnum as cust_main_custnum',
+ 'cust_main.custnum AS cust_main_custnum',
FS::UI::Web::cust_sql_fields(
$params->{'cust_fields'}
),
@@ -2937,120 +3090,6 @@ sub bulk_change {
'';
}
-=item insert_reason
-
-Associates this package with a (suspension or cancellation) reason (see
-L<FS::cust_pkg_reason>, possibly inserting a new reason on the fly (see
-L<FS::reason>).
-
-Available options are:
-
-=over 4
-
-=item reason
-
-can be set to a cancellation reason (see L<FS:reason>), either a reasonnum of an existing reason, or passing a hashref will create a new reason. The hashref should have the following keys: typenum - Reason type (see L<FS::reason_type>, reason - Text of the new reason.
-
-=item reason_otaker
-
-the access_user (see L<FS::access_user>) providing the reason
-
-=item date
-
-a unix timestamp
-
-=item action
-
-the action (cancel, susp, adjourn, expire) associated with the reason
-
-=back
-
-If there is an error, returns the error, otherwise returns false.
-
-=cut
-
-sub insert_reason {
- my ($self, %options) = @_;
-
- my $otaker = $options{reason_otaker} ||
- $FS::CurrentUser::CurrentUser->username;
-
- my $reasonnum;
- if ( $options{'reason'} =~ /^(\d+)$/ ) {
-
- $reasonnum = $1;
-
- } elsif ( ref($options{'reason'}) ) {
-
- return 'Enter a new reason (or select an existing one)'
- unless $options{'reason'}->{'reason'} !~ /^\s*$/;
-
- my $reason = new FS::reason({
- 'reason_type' => $options{'reason'}->{'typenum'},
- 'reason' => $options{'reason'}->{'reason'},
- });
- my $error = $reason->insert;
- return $error if $error;
-
- $reasonnum = $reason->reasonnum;
-
- } else {
- return "Unparsable reason: ". $options{'reason'};
- }
-
- my $cust_pkg_reason =
- new FS::cust_pkg_reason({ 'pkgnum' => $self->pkgnum,
- 'reasonnum' => $reasonnum,
- 'otaker' => $otaker,
- 'action' => substr(uc($options{'action'}),0,1),
- 'date' => $options{'date'}
- ? $options{'date'}
- : time,
- });
-
- $cust_pkg_reason->insert;
-}
-
-=item set_usage USAGE_VALUE_HASHREF
-
-USAGE_VALUE_HASHREF is a hashref of svc_acct usage columns and the amounts
-to which they should be set (see L<FS::svc_acct>). Currently seconds,
-upbytes, downbytes, and totalbytes are appropriate keys.
-
-All svc_accts which are part of this package have their values reset.
-
-=cut
-
-sub set_usage {
- my ($self, $valueref, %opt) = @_;
-
- foreach my $cust_svc ($self->cust_svc){
- my $svc_x = $cust_svc->svc_x;
- $svc_x->set_usage($valueref, %opt)
- if $svc_x->can("set_usage");
- }
-}
-
-=item recharge USAGE_VALUE_HASHREF
-
-USAGE_VALUE_HASHREF is a hashref of svc_acct usage columns and the amounts
-to which they should be set (see L<FS::svc_acct>). Currently seconds,
-upbytes, downbytes, and totalbytes are appropriate keys.
-
-All svc_accts which are part of this package have their values incremented.
-
-=cut
-
-sub recharge {
- my ($self, $valueref) = @_;
-
- foreach my $cust_svc ($self->cust_svc){
- my $svc_x = $cust_svc->svc_x;
- $svc_x->recharge($valueref)
- if $svc_x->can("recharge");
- }
-}
-
=back
=head1 BUGS
diff --git a/FS/FS/cust_pkg_discount.pm b/FS/FS/cust_pkg_discount.pm
index 76118ad92..87f8c5283 100644
--- a/FS/FS/cust_pkg_discount.pm
+++ b/FS/FS/cust_pkg_discount.pm
@@ -2,7 +2,7 @@ package FS::cust_pkg_discount;
use strict;
use base qw( FS::Record );
-use FS::Record; # qw( qsearch qsearchs );
+use FS::Record qw( qsearchs ); # qsearch );
use FS::cust_pkg;
use FS::discount;
@@ -131,6 +131,24 @@ sub check {
$self->SUPER::check;
}
+=item cust_pkg
+
+=cut
+
+sub cust_pkg {
+ my $self = shift;
+ qsearchs('cust_pkg', { 'pkgnum' => $self->pkgnum } );
+}
+
+=item discount
+
+=cut
+
+sub discount {
+ my $self = shift;
+ qsearchs('discount', { 'discountnum' => $self->discountnum } );
+}
+
=back
=head1 BUGS
diff --git a/httemplate/edit/process/quick-cust_pkg.cgi b/httemplate/edit/process/quick-cust_pkg.cgi
index 7a0f08280..c60156746 100644
--- a/httemplate/edit/process/quick-cust_pkg.cgi
+++ b/httemplate/edit/process/quick-cust_pkg.cgi
@@ -53,6 +53,7 @@ my $cust_pkg = new FS::cust_pkg {
? str2time($cgi->param('start_date'))
: ''
),
+ 'discountnum' => scalar($cgi->param('discountnum')),
'refnum' => $refnum,
'locationnum' => $locationnum,
};
diff --git a/httemplate/elements/select-discount.html b/httemplate/elements/select-discount.html
new file mode 100644
index 000000000..e0b6e6cbe
--- /dev/null
+++ b/httemplate/elements/select-discount.html
@@ -0,0 +1,20 @@
+<% include( '/elements/select-table.html',
+ 'table' => 'discount',
+ 'name_col' => 'description',
+ 'order_by' => 'ORDER BY discountnum', #XXX weight
+ 'value' => $discountnum,
+ 'empty_label' => '(none)',
+ 'hashref' => { 'disabled' => '' },
+ %opt,
+ )
+%>
+<%init>
+
+my %opt = @_;
+my $discountnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'records'} = delete $opt{'discount'}
+ if $opt{'discount'};
+
+</%init>
+
diff --git a/httemplate/elements/tr-select-discount.html b/httemplate/elements/tr-select-discount.html
new file mode 100644
index 000000000..4ff8d1357
--- /dev/null
+++ b/httemplate/elements/tr-select-discount.html
@@ -0,0 +1,27 @@
+% if ( scalar(@{ $opt{'discount'} }) == 0 ) {
+
+ <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'discountnum' %>" VALUE="">
+
+% } else {
+
+ <TR>
+ <TD ALIGN="right"><% $opt{'label'} || '<B>Discount</B>' %></TD>
+ <TD>
+ <% include( '/elements/select-discount.html',
+ 'curr_value' => $discountnum,
+ %opt,
+ )
+ %>
+ </TD>
+ </TR>
+
+% }
+<%init>
+
+my %opt = @_;
+my $discountnum = $opt{'curr_value'} || $opt{'value'};
+
+$opt{'discount'} ||= [ qsearch( 'discount', { disabled=>'' } ) ];
+
+</%init>
+
diff --git a/httemplate/misc/order_pkg.html b/httemplate/misc/order_pkg.html
index a7571ca58..684f94e7c 100644
--- a/httemplate/misc/order_pkg.html
+++ b/httemplate/misc/order_pkg.html
@@ -60,6 +60,10 @@
});
</SCRIPT>
+% if ( $curuser->access_right('Discount customer package') ) {
+ <% include('/elements/tr-select-discount.html') %>
+% }
+
% if ( $conf->exists('pkg_referral') ) {
<% include('/elements/tr-select-part_referral.html',
'curr_value' => scalar( $cgi->param('refnum') ), #get rid of empty_label first# || $cust_main->refnum,
@@ -86,8 +90,10 @@
</HTML>
<%init>
+my $curuser = $FS::CurrentUser::CurrentUser;
+
die "access denied"
- unless $FS::CurrentUser::CurrentUser->access_right('Order customer package');
+ unless $curuser->access_right('Order customer package');
my $conf = new FS::Conf;
diff --git a/httemplate/view/cust_main/packages/package.html b/httemplate/view/cust_main/packages/package.html
index 280a01682..33bcd2ad4 100644
--- a/httemplate/view/cust_main/packages/package.html
+++ b/httemplate/view/cust_main/packages/package.html
@@ -1,4 +1,4 @@
-<TD CLASS="inv" BGCOLOR="<% $bgcolor %>">
+<TD CLASS="inv" BGCOLOR="<% $bgcolor %>" VALIGN="top">
<TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
<TR>
<TD COLSPAN=2>
diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html
index 6667a554d..40d6438f3 100644
--- a/httemplate/view/cust_main/packages/status.html
+++ b/httemplate/view/cust_main/packages/status.html
@@ -42,6 +42,8 @@
)
%>
+ <% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
+
% unless ( $cust_pkg->get('setup') ) {
<% pkg_status_row_colspan( $cust_pkg, 'Never billed', '', 'colspan'=>$colspan, %opt ) %>
% } else {
@@ -70,10 +72,12 @@
%
% unless ( $cust_pkg->get('setup') ) { #not setup
%
-% unless ( $part_pkg->freq ) {
+% unless ( $part_pkg->freq ) {
<% pkg_status_row_colspan( $cust_pkg, 'Not&nbsp;yet&nbsp;billed&nbsp;(one-time&nbsp;charge)', '', 'colspan'=>$colspan, %opt ) %>
+ <% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
+
<% pkg_status_row_if(
$cust_pkg,
( $part_pkg->freq ? 'Start billing' : 'Bill on' ),
@@ -94,7 +98,9 @@
% } else {
- <% pkg_status_row_colspan($cust_pkg, "Not&nbsp;yet&nbsp;billed&nbsp;($billed_or_prepaid&nbsp;". myfreq($part_pkg). ')', '', 'colspan'=>$colspan, %opt ) %>
+ <% pkg_status_row_colspan($cust_pkg, "Not&nbsp;yet&nbsp;billed&nbsp;($billed_or_prepaid&nbsp;". myfreq($part_pkg). ')', '', 'colspan'=>$colspan, %opt ) %>
+
+ <% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
<% pkg_status_row_if($cust_pkg, 'Start billing', 'start_date', %opt) %>
@@ -108,6 +114,8 @@
<% pkg_status_row($cust_pkg, 'Billed', 'setup', %opt) %>
+ <% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
+
% } else {
%
% if (scalar($cust_pkg->overlimit)) {
@@ -130,6 +138,8 @@
%>
% }
+ <% pkg_status_row_discount( $cust_pkg, %opt, 'colspan'=>$colspan ) %>
+
<% pkg_status_row($cust_pkg, 'Setup', 'setup', %opt) %>
% }
@@ -273,12 +283,37 @@ sub pkg_status_row_changed {
'size' => '-1',
'align' => 'right',
'colspan' => $opt{'colspan'},
+ #%opt,
);
}
$html;
}
+sub pkg_status_row_discount {
+ my( $cust_pkg, %opt ) = @_;
+
+ my $html;
+
+ foreach my $cust_pkg_discount ( $cust_pkg->cust_pkg_discount_active ) {
+
+ my $discount = $cust_pkg_discount->discount;
+
+ my $label = '<B>Discount</B>: '. $discount->description;
+ $label .= ' ('. ( $discount->months - $cust_pkg_discount->months_used ).
+ ' months remaining)'
+ if $discount->months;
+
+ $html .= pkg_status_row_colspan( $cust_pkg, $label, '',
+ 'colspan' => $opt{'colspan'},
+ #%opt,
+ );
+
+ }
+
+ $html;
+}
+
sub pkg_status_row_colspan {
my($cust_pkg, $title, $addl, %opt) = @_;