From a3696f50ea05e5b8f87c24c5298fc35bee03fc75 Mon Sep 17 00:00:00 2001 From: Jonathan Prykop Date: Mon, 15 Jun 2015 23:37:48 -0500 Subject: [PATCH] RT#30705 Change contract end date when changing packages --- FS/FS/cust_pkg.pm | 77 +++++++++++++++++++++++--- httemplate/edit/process/change-cust_pkg.html | 61 ++++++++++++-------- httemplate/misc/change_pkg.cgi | 13 +++++ httemplate/view/cust_main/packages/status.html | 4 ++ 4 files changed, 123 insertions(+), 32 deletions(-) diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index dcbcfeb80..5ec2f5a25 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -1979,6 +1979,13 @@ can't be transferred (also see the I config option). If unprotect_svcs is true, this method will transfer as many services as it can and then unconditionally cancel the old package. +=item contract_end + +If specified, sets this value for the contract_end date on the new package +(without regard for keep_dates or the usual date-preservation behavior.) +Will throw an error if defined but false; the UI doesn't allow editing +this unless it already exists, making removal impossible to undo. + =back At least one of locationnum, cust_location, pkgpart, refnum, cust_main, or @@ -1992,6 +1999,36 @@ For example: =cut +#used by change and change_later +#didn't put with documented check methods because it depends on change-specific opts +#and it also possibly edits the value of opts +sub _check_change { + my $self = shift; + my $opt = shift; + if ( defined($opt->{'contract_end'}) ) { + my $current_contract_end = $self->get('contract_end'); + unless ($opt->{'contract_end'}) { + if ($current_contract_end) { + return "Cannot remove contract end date when changing packages"; + } else { + #shouldn't even pass this option if there's not a current value + #but can be handled gracefully if the option is empty + warn "Contract end date passed unexpectedly"; + delete $opt->{'contract_end'}; + return ''; + } + } + unless ($current_contract_end) { + #option shouldn't be passed, throw error if it's non-empty + return "Cannot add contract end date when changing packages " . $self->pkgnum; + } + if ($opt->{'start_date'} && ($opt->{'contract_end'} < $opt->{'start_date'})) { + return "Contract end date is before change date"; + } + } + return ''; +} + #some false laziness w/order sub change { my $self = shift; @@ -1999,6 +2036,16 @@ sub change { my $conf = new FS::Conf; + # handle contract_end on cust_pkg same as passed option + if ( $opt->{'cust_pkg'} ) { + $opt->{'contract_end'} = $opt->{'cust_pkg'}->contract_end; + delete $opt->{'contract_end'} unless $opt->{'contract_end'}; + } + + # check contract_end, prevent adding/removing + my $error = $self->_check_change($opt); + return $error if $error; + # Transactionize this whole mess local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -2011,8 +2058,6 @@ sub change { local $FS::UID::AutoCommit = 0; my $dbh = dbh; - my $error; - if ( $opt->{'cust_location'} ) { $error = $opt->{'cust_location'}->find_or_insert; if ( $error ) { @@ -2037,6 +2082,9 @@ sub change { if ( $opt->{'pkgpart'} and $opt->{'pkgpart'} != $self->pkgpart ) { $self->set_initial_timers; } + # but if contract_end was explicitly specified, that overrides all else + $self->set('contract_end', $opt->{'contract_end'}) + if $opt->{'contract_end'}; $error = $self->replace; if ( $error ) { $dbh->rollback if $oldAutoCommit; @@ -2094,6 +2142,9 @@ sub change { start_date contract_end)) { $hash{$date} = $self->getfield($date); } + # but if contract_end was explicitly specified, that overrides all else + $hash{'contract_end'} = $opt->{'contract_end'} + if $opt->{'contract_end'}; # allow $opt->{'locationnum'} = '' to specifically set it to null # (i.e. customer default location) @@ -2366,8 +2417,10 @@ The date for the package change. Required, and must be in the future. =item quantity -The pkgpart. locationnum, and quantity of the new package, with the same -meaning as in C. +=item contract_end + +The pkgpart, locationnum, quantity and optional contract_end of the new +package, with the same meaning as in C. =back @@ -2377,6 +2430,10 @@ sub change_later { my $self = shift; my $opt = ref($_[0]) ? shift : { @_ }; + # check contract_end, prevent adding/removing + my $error = $self->_check_change($opt); + return $error if $error; + my $oldAutoCommit = $FS::UID::AutoCommit; local $FS::UID::AutoCommit = 0; my $dbh = dbh; @@ -2390,8 +2447,6 @@ sub change_later { return "start_date $date is in the past"; } - my $error; - if ( $self->change_to_pkgnum ) { my $change_to = FS::cust_pkg->by_key($self->change_to_pkgnum); my $new_pkgpart = $opt->{'pkgpart'} @@ -2400,7 +2455,9 @@ sub change_later { if $opt->{'locationnum'} and $opt->{'locationnum'} != $change_to->locationnum; my $new_quantity = $opt->{'quantity'} if $opt->{'quantity'} and $opt->{'quantity'} != $change_to->quantity; - if ( $new_pkgpart or $new_locationnum or $new_quantity ) { + my $new_contract_end = $opt->{'contract_end'} + if $opt->{'contract_end'} and $opt->{'contract_end'} != $change_to->contract_end; + if ( $new_pkgpart or $new_locationnum or $new_quantity or $new_contract_end ) { # it hasn't been billed yet, so in principle we could just edit # it in place (w/o a package change), but that's bad form. # So change the package according to the new options... @@ -2442,8 +2499,10 @@ sub change_later { if $opt->{'locationnum'} and $opt->{'locationnum'} != $self->locationnum; my $new_quantity = $opt->{'quantity'} if $opt->{'quantity'} and $opt->{'quantity'} != $self->quantity; + my $new_contract_end = $opt->{'contract_end'} + if $opt->{'contract_end'} and $opt->{'contract_end'} != $self->contract_end; - return '' unless $new_pkgpart or $new_locationnum or $new_quantity; # wouldn't do anything + return '' unless $new_pkgpart or $new_locationnum or $new_quantity or $new_contract_end; # wouldn't do anything # allow $opt->{'locationnum'} = '' to specifically set it to null # (i.e. customer default location) @@ -2454,7 +2513,7 @@ sub change_later { locationnum => $opt->{'locationnum'}, start_date => $date, map { $_ => ( $opt->{$_} || $self->$_() ) } - qw( pkgpart quantity refnum salesnum ) + qw( pkgpart quantity refnum salesnum contract_end ) } ); $error = $new->insert('change' => 1, 'allow_pkgpart' => ($new_pkgpart ? 0 : 1)); diff --git a/httemplate/edit/process/change-cust_pkg.html b/httemplate/edit/process/change-cust_pkg.html index 96175e1e4..c066ff5b0 100644 --- a/httemplate/edit/process/change-cust_pkg.html +++ b/httemplate/edit/process/change-cust_pkg.html @@ -41,34 +41,49 @@ if ( $cgi->param('locationnum') == -1 ) { } my $error; -if ( $cgi->param('delay') ) { - my $date = parse_datetime($cgi->param('start_date')); - if (!$date) { - $error = "Invalid change date '".$cgi->param('start_date')."'."; - } elsif ( $date < time ) { - $error = "Change date ".$cgi->param('start_date')." is in the past."; +my $contract_end; +my $now = time; +if (defined($cgi->param('contract_end'))) { + $contract_end = parse_datetime($cgi->param('contract_end')); + if ($contract_end < $now) { + $error = "Contract end ".$cgi->param('contract_end')." is in the past."; } else { - # schedule the change - $change{'start_date'} = $date; - $error = $cust_pkg->change_later(\%change); + $change{'contract_end'} = $contract_end; } -} else { - # special case: if there's a package change scheduled, and it matches - # the parameters the user requested this time, then change to the existing - # future package. - if ( $cust_pkg->change_to_pkgnum ) { - my $change_to = FS::cust_pkg->by_key($cust_pkg->change_to_pkgnum); - if ( $change_to->pkgpart == $change{'pkgpart'} and - $change_to->locationnum == $change{'locationnum'} ) { - - %change = ( 'cust_pkg' => $change_to ); +} +unless ($error) { + if ( $cgi->param('delay') ) { + my $date = parse_datetime($cgi->param('start_date')); + if (!$date) { + $error = "Invalid change date '".$cgi->param('start_date')."'."; + } elsif ( $date < $now ) { + $error = "Change date ".$cgi->param('start_date')." is in the past."; + } else { + # schedule the change + $change{'start_date'} = $date; + $error = $cust_pkg->change_later(\%change); + } + } else { + # special case: if there's a package change scheduled, and it matches + # the parameters the user requested this time, then change to the existing + # future package. + if ( $cust_pkg->change_to_pkgnum ) { + my $change_to = FS::cust_pkg->by_key($cust_pkg->change_to_pkgnum); + if ( + $change_to->pkgpart == $change{'pkgpart'} and + $change_to->locationnum == $change{'locationnum'} and + $change_to->quantity == $change{'quantity'} and + $change_to->contract_end == $change{'contract_end'} + ) { + %change = ( 'cust_pkg' => $change_to ); + } } - } - # do a package change right now - my $pkg_or_error = $cust_pkg->change( \%change ); - $error = ref($pkg_or_error) ? '' : $pkg_or_error; + # do a package change right now + my $pkg_or_error = $cust_pkg->change( \%change ); + $error = ref($pkg_or_error) ? '' : $pkg_or_error; + } } diff --git a/httemplate/misc/change_pkg.cgi b/httemplate/misc/change_pkg.cgi index 1b4a94e81..e74747e82 100755 --- a/httemplate/misc/change_pkg.cgi +++ b/httemplate/misc/change_pkg.cgi @@ -28,6 +28,14 @@ 'curr_value' => $cust_pkg->quantity &> +% if ($use_contract_end) { + <& /elements/tr-input-date-field.html, { + 'name' => 'contract_end', + 'value' => ($cgi->param('contract_end') || $cust_pkg->get('contract_end')), + 'label' => 'Contract End', + } &> +% } +
@@ -124,6 +132,8 @@ my $part_pkg = $cust_pkg->part_pkg; my $title = "Change Package"; +my $use_contract_end = $cust_pkg->get('contract_end') ? 1 : 0; + # if there's already a package change ordered, preload it if ( $cust_pkg->change_to_pkgnum ) { my $change_to = FS::cust_pkg->by_key($cust_pkg->change_to_pkgnum); @@ -131,6 +141,9 @@ if ( $cust_pkg->change_to_pkgnum ) { foreach(qw( start_date pkgpart locationnum quantity )) { $cgi->param($_, $change_to->get($_)); } + if ($use_contract_end) { + $cgi->param('contract_end', $change_to->get('contract_end')); + } $title = "Edit Scheduled Package Change"; } diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html index 047abda0b..3063e3fc8 100644 --- a/httemplate/view/cust_main/packages/status.html +++ b/httemplate/view/cust_main/packages/status.html @@ -417,6 +417,10 @@ sub pkg_status_row_expire { } elsif ( $cust_pkg->change_to_pkg->locationnum != $cust_pkg->locationnum ) { $title = mt('Will change location on'); + } elsif (( $cust_pkg->change_to_pkg->quantity != $cust_pkg->quantity ) || + ( $cust_pkg->change_to_pkg->contract_end != $cust_pkg->contract_end )) + { + $title = mt('Will change on'); } else { # FS::cust_pkg->change_later should have prevented this, but # just so that we can display _something_ -- 2.11.0