From 00938b30a69411a743aa01db5e27100818a3c82b Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Tue, 14 Aug 2012 17:02:47 -0700 Subject: [PATCH] unsuspend fees, #6587 --- FS/FS/Schema.pm | 2 + FS/FS/cust_pkg.pm | 37 +++++++++- FS/FS/reason.pm | 27 ++++++- httemplate/browse/reason.html | 17 +++++ httemplate/edit/reason.html | 116 ++++++++++++++++++------------ httemplate/elements/tr-select-reason.html | 57 ++++++++++++++- 6 files changed, 206 insertions(+), 50 deletions(-) diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm index 6b32d7153..0f1d1513a 100644 --- a/FS/FS/Schema.pm +++ b/FS/FS/Schema.pm @@ -3671,6 +3671,8 @@ sub tables_hashref { 'reason_type', 'int', '', '', '', '', 'reason', 'text', '', '', '', '', 'disabled', 'char', 'NULL', 1, '', '', + 'unsuspend_pkgpart', 'int', 'NULL', '', '', '', + 'unsuspend_hold','char', 'NULL', 1, '', '', ], 'primary_key' => 'reasonnum', 'unique' => [], diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index aed99e51d..f56e1f0ed 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -1319,7 +1319,8 @@ sub credit_remaining { Unsuspends all services (see L and L) in this package, then unsuspends the package itself (clears the susp field and the -adjourn field if it is in the past). +adjourn field if it is in the past). If the suspend reason includes an +unsuspension package, that package will be ordered. Available options are: @@ -1423,6 +1424,8 @@ sub unsuspend { } + my $reason = $self->last_cust_pkg_reason('susp')->reason; + my %hash = $self->hash; my $inactive = time - $hash{'susp'}; @@ -1449,6 +1452,33 @@ sub unsuspend { return $error; } + my $unsusp_pkg; + + if ( $reason->unsuspend_pkgpart ) { + my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart) + or $error = "Unsuspend package definition ".$reason->unsuspend_pkgpart. + " not found."; + my $start_date = $self->cust_main->next_bill_date + if $reason->unsuspend_hold; + + if ( $part_pkg ) { + $unsusp_pkg = FS::cust_pkg->new({ + 'custnum' => $self->custnum, + 'pkgpart' => $reason->unsuspend_pkgpart, + 'start_date' => $start_date, + 'locationnum' => $self->locationnum, + # discount? probably not... + }); + + $error ||= $self->cust_main->order_pkg( 'cust_pkg' => $unsusp_pkg ); + } + + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + if ( $conf->config('unsuspend_email_admin') ) { my $error = send_email( @@ -1462,6 +1492,11 @@ sub unsuspend { 'Customer: #'. $self->custnum. ' '. $self->cust_main->name. "\n", 'Package : #'. $self->pkgnum. " (". $self->part_pkg->pkg_comment. ")\n", ( map { "Service : $_\n" } @labels ), + ($unsusp_pkg ? + "An unsuspension fee was charged: Package #".$unsusp_pkg->pkgnum. + " (.".$unsusp_pkg->pkg_comment.")\n" + : '' + ), ], ); diff --git a/FS/FS/reason.pm b/FS/FS/reason.pm index 377da4985..a9a7d745d 100644 --- a/FS/FS/reason.pm +++ b/FS/FS/reason.pm @@ -46,6 +46,15 @@ FS::Record. The following fields are currently supported: =item disabled - 'Y' or '' +=item unsuspend_pkgpart - for suspension reasons only, the pkgpart (see +L) of a package to be ordered when the package is unsuspended. +Typically this will be some kind of reactivation fee. Attaching it to +a suspension reason allows the reactivation fee to be charged for some +suspensions but not others. + +=item unsuspend_hold - 'Y' or ''. If unsuspend_pkgpart is set, this tells +whether to bill the unsuspend package immediately ('') or to wait until +the customer's next invoice ('Y'). =back @@ -97,16 +106,30 @@ sub check { my $error = $self->ut_numbern('reasonnum') + || $self->ut_number('reason_type') + || $self->ut_foreign_key('reason_type', 'reason_type', 'typenum') || $self->ut_text('reason') + || $self->ut_flag('disabled') ; return $error if $error; + if ( $self->reasontype->class eq 'S' ) { + $error = $self->ut_numbern('unsuspend_pkgpart') + || $self->ut_foreign_keyn('unsuspend_pkgpart', 'part_pkg', 'pkgpart') + || $self->ut_flag('unsuspend_hold') + ; + return $error if $error; + } else { + $self->set('unsuspend_pkgpart' => ''); + $self->set('unsuspend_hold' => ''); + } + $self->SUPER::check; } =item reasontype -Returns the reason_type (see FS::reason_type) associated with this reason. +Returns the reason_type (see L) associated with this reason. =cut @@ -118,7 +141,7 @@ sub reasontype { =head1 BUGS -Here be termintes. Don't use on wooden computers. +Here by termintes. Don't use on wooden computers. =head1 SEE ALSO diff --git a/httemplate/browse/reason.html b/httemplate/browse/reason.html index fe285be4a..14e97bf2f 100644 --- a/httemplate/browse/reason.html +++ b/httemplate/browse/reason.html @@ -17,14 +17,17 @@ 'header' => [ '#', ucfirst($classname) . ' Reason Type', ucfirst($classname) . ' Reason', + ($class eq 'S' ? 'Unsuspension Fee' : ()), ], 'fields' => [ 'reasonnum', sub { shift->reasontype->type }, 'reason', + $unsuspend_pkg_comment, ], 'links' => [ $link, $link, '', + $unsuspend_pkg_link, ], ) %> @@ -50,4 +53,18 @@ my $count_query = 'SELECT COUNT(*) FROM reason LEFT JOIN reason_type on ' . my $link = [ $p."edit/reason.html?class=$class&reasonnum=", 'reasonnum' ]; +my ($unsuspend_pkg_comment, $unsuspend_pkg_link); +if ( $class eq 'S' ) { + $unsuspend_pkg_comment = sub { + my $pkgpart = shift->unsuspend_pkgpart or return ''; + my $part_pkg = FS::part_pkg->by_key($pkgpart) or return ''; + $part_pkg->pkg_comment; + }; + + my $unsuspend_pkg_link = sub { + my $pkgpart = shift->unsuspend_pkgpart or return ''; + [ $p."edit/part_pkg.cgi?", $pkgpart ]; + }; +} + diff --git a/httemplate/edit/reason.html b/httemplate/edit/reason.html index 620a2ea15..78d044755 100644 --- a/httemplate/edit/reason.html +++ b/httemplate/edit/reason.html @@ -1,50 +1,78 @@ -% -% $cgi->param('class') =~ /^(\w)$/ or die "illegal class"; -% my $class=$1; -% -% my $classname = $FS::reason_type::class_name{$class}; -% -% my (@types) = qsearch( 'reason_type', { 'class' => $class } ); -% -% unless (scalar(@types)) { -% print $cgi->redirect( "reason_type.html?class=$class" ); -% } -<% include( 'elements/edit.html', - 'name' => ucfirst($classname) . ' Reason', - 'table' => 'reason', - 'labels' => { - 'reasonnum' => ucfirst($classname) . ' Reason', - 'reason_type' => ucfirst($classname) . ' Reason type', - 'reason' => ucfirst($classname) . ' Reason', - 'disabled' => 'Disabled', - 'class' => '', - }, - 'fields' => [ - { 'field' => 'reason_type', - 'type' => 'select', - #XXX use something more sane than a hashref - #then fix tr-select.html - 'value' => { 'vcolumn' => 'typenum', - 'ccolumn' => 'type', - 'values' => \@types, - }, - }, - 'reason', - { 'field' => 'class', - 'type' => 'hidden', - 'value' => $class, - }, - { 'field' => 'disabled', - 'type' => 'checkbox', - 'value' => 'Y' - }, - ], - 'viewall_url' => $p . "browse/reason.html?class=$class", - ) -%> +<& elements/edit.html, + 'menubar'=> [ "View all $classname Reasons" => + $p.'browse/reason.html?class='.$class, + "View $classname Reason Types" => + $p.'browse/reason_type.html?class='.$class, + ], + 'name' => ucfirst($classname) . ' Reason', + + 'table' => 'reason', + 'labels' => { + 'reasonnum' => $classname . ' Reason', + 'reason_type' => $classname . ' Reason type', + 'reason' => $classname . ' Reason', + 'disabled' => 'Disabled', + 'class' => '', + 'unsuspend_pkgpart' => 'Unsuspension fee', + 'unsuspend_hold' => 'Delay until next bill', + }, + 'fields' => \@fields, +&> <%init> die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); +$cgi->param('class') =~ /^(\w)$/ or die "illegal class"; +my $class=$1; + +my $classname = ucfirst($FS::reason_type::class_name{$class}); + +my (@types) = qsearch( 'reason_type', { 'class' => $class } ); + +unless (scalar(@types)) { + print $cgi->redirect( "reason_type.html?class=$class" ); +} + +my @fields = ( + { 'field' => 'reason_type', + 'type' => 'select-table', + 'table' => 'reason_type', + 'name_col' => 'type', + 'value_col' => 'typenum', + 'hashref' => { 'class' => $class }, + 'disable_empty' => 1, +# #then fix tr-select.html +# +# 'value' => { 'vcolumn' => 'typenum', +# 'ccolumn' => 'type', +# 'values' => \@types, +# }, +# # that wasn't so hard...did this do something else that I'm missing? + }, + 'reason', + { 'field' => 'class', + 'type' => 'hidden', + 'value' => $class, + }, + { 'field' => 'disabled', + 'type' => 'checkbox', + 'value' => 'Y' + }, +); + +push @fields, + { 'field' => 'unsuspend_pkgpart', + 'type' => 'select-part_pkg', + 'hashref' => { 'disabled' => '', + 'freq' => 0 }, # one-time charges only + }, + { 'field' => 'unsuspend_hold', + 'type' => 'checkbox', + 'value' => 'Y', + }, + if ( $class eq 'S' ); + + + diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html index 5a79d68ef..c1df10b94 100755 --- a/httemplate/elements/tr-select-reason.html +++ b/httemplate/elements/tr-select-reason.html @@ -32,8 +32,15 @@ Example: