diff options
author | Jonathan Prykop <jonathan@freeside.biz> | 2016-07-18 19:29:59 -0500 |
---|---|---|
committer | Jonathan Prykop <jonathan@freeside.biz> | 2016-08-14 19:17:24 -0500 |
commit | 4ec2818fa42bf6b3ed00f6888c744456855ec0f1 (patch) | |
tree | 0068cbc6a8f706476323bf1eb3e3eda2fcee3bd8 /FS | |
parent | 0edd5b9d5c03f02341b1004888f0f712c8defb47 (diff) |
RT#38973: Bill for time worked on ticket resolution [fully functional]
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/TicketSystem/RT_Internal.pm | 37 | ||||
-rw-r--r-- | FS/FS/cust_pkg.pm | 14 | ||||
-rw-r--r-- | FS/FS/part_pkg/rt_field.pm | 66 |
3 files changed, 96 insertions, 21 deletions
diff --git a/FS/FS/TicketSystem/RT_Internal.pm b/FS/FS/TicketSystem/RT_Internal.pm index 01806c1..99e7044 100644 --- a/FS/FS/TicketSystem/RT_Internal.pm +++ b/FS/FS/TicketSystem/RT_Internal.pm @@ -3,6 +3,7 @@ package FS::TicketSystem::RT_Internal; use strict; use vars qw( @ISA $DEBUG $me ); use Data::Dumper; +use Date::Format qw( time2str ); use MIME::Entity; use FS::UID qw(dbh); use FS::CGI qw(popurl); @@ -101,17 +102,43 @@ sub init { warn "$me init: complete" if $DEBUG; } -=item customer_tickets CUSTNUM [ LIMIT ] [ PRIORITYVALUE ] +=item customer_tickets CUSTNUM [ PARAMS ] Replacement for the one in RT_External so that we can access custom fields -properly. +properly. Accepts a hashref with the following parameters: + +number - custnum/svcnum + +limit + +priority + +status + +queueid + +resolved - only return tickets resolved after this timestamp =cut # create an RT::Tickets object for a specified custnum or svcnum sub _tickets_search { - my( $self, $type, $number, $limit, $priority, $status, $queueid ) = @_; + my $self = shift; + my $type = shift; + + my( $number, $limit, $priority, $status, $queueid, $opt ); + if ( ref($_[0]) eq 'HASH' ) { + $opt = shift; + $number = $$opt{'number'}; + $limit = $$opt{'limit'}; + $priority = $$opt{'priority'}; + $status = $$opt{'status'}; + $queueid = $$opt{'queueid'}; + } else { + ( $number, $limit, $priority, $status, $queueid ) = @_; + $opt = {}; + } $type =~ /^Customer|Service$/ or die "invalid type: $type"; $number =~ /^\d+$/ or die "invalid custnum/svcnum: $number"; @@ -161,6 +188,10 @@ sub _tickets_search { $rtql .= " AND Queue = $queueid " if $queueid; + if ($$opt{'resolved'}) { + $rtql .= " AND Resolved >= " . dbh->quote(time2str('%Y-%m-%d %H:%M:%S',$$opt{'resolved'})); + } + warn "$me _customer_tickets_search:\n$rtql\n" if $DEBUG; $Tickets->FromSQL($rtql); diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index de88cc0..1ee8552 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -533,6 +533,7 @@ sub delete { # cust_bill_pay.pkgnum (wtf, shouldn't reference pkgnum) # cust_pkg_usage.pkgnum # cust_pkg.uncancel_pkgnum, change_pkgnum, main_pkgnum, and change_to_pkgnum + # rt_field_charge.pkgnum # cust_svc is handled by canceling the package before deleting it # cust_pkg_option is handled via option_Common @@ -2631,6 +2632,19 @@ sub change { return "canceling old package: $error"; } + # transfer rt_field_charge, if we're not changing pkgpart + # after billing of old package, before billing of new package + if ( $same_pkgpart ) { + foreach my $rt_field_charge ($self->rt_field_charge) { + $rt_field_charge->set('pkgnum', $cust_pkg->pkgnum); + $error = $rt_field_charge->replace; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "transferring rt_field_charge: $error"; + } + } + } + if ( $conf->exists('cust_pkg-change_pkgpart-bill_now') ) { #$self->cust_main my $error = $cust_pkg->cust_main->bill( diff --git a/FS/FS/part_pkg/rt_field.pm b/FS/FS/part_pkg/rt_field.pm index 1b18720..657a8d7 100644 --- a/FS/FS/part_pkg/rt_field.pm +++ b/FS/FS/part_pkg/rt_field.pm @@ -24,12 +24,21 @@ my %custom_field = ( 'lookuptype' => 'RT::Queue-RT::Ticket', ); +my %multiple = ( + 'multiple' => 1, + 'parse' => sub { @_ }, # because /edit/process/part_pkg.pm doesn't grok select multiple +); + our %info = ( 'name' => 'Bill from custom fields in resolved RT tickets', 'shortname' => 'RT custom rate', 'weight' => 65, 'inherit_fields' => [ 'global_Mixin' ], 'fields' => { + 'queueids' => { 'name' => 'Queues', + 'type' => 'select-rt-queue', + %multiple, + }, 'unit_field' => { 'name' => 'Units field', %custom_field, 'validate' => sub { return ${$_[1]} ? '' : 'Units field must be specified' }, @@ -42,8 +51,7 @@ our %info = ( 'validate' => \&FS::part_pkg::global_Mixin::validate_moneyn }, 'display_fields' => { 'name' => 'Display fields', %custom_field, - 'multiple' => 1, - 'parse' => sub { @_ }, # because /edit/process/part_pkg.pm doesn't grok select multiple + %multiple, }, # from global_Mixin, but don't get used by this at all 'unused_credit_cancel' => {'disabled' => 1}, @@ -58,22 +66,24 @@ our %info = ( if $options->{'rate_field'} and $options->{'rate_flat'}; return ''; }, - 'fieldorder' => [ 'unit_field', 'rate_field', 'rate_flat', 'display_fields' ] + 'fieldorder' => [ 'queueids', 'unit_field', 'rate_field', 'rate_flat', 'display_fields' ] ); sub price_info { my $self = shift; my $str = $self->SUPER::price_info; $str .= ' plus ' if $str; - FS::TicketSystem->init(); - my %custom_fields = FS::TicketSystem->custom_fields(); - my $rate = $self->option('rate_flat',1); - my $rate_field = $self->option('rate_field',1); - my $unit_field = $self->option('unit_field'); - $str .= $rate - ? $money_char . sprintf("%.2",$rate) - : $custom_fields{$rate_field}; - $str .= ' x ' . $custom_fields{$unit_field}; + $str .= 'charge from RT'; +# takes way too long just to get a package label +# FS::TicketSystem->init(); +# my %custom_fields = FS::TicketSystem->custom_fields(); +# my $rate = $self->option('rate_flat',1); +# my $rate_field = $self->option('rate_field',1); +# my $unit_field = $self->option('unit_field'); +# $str .= $rate +# ? $money_char . sprintf("%.2",$rate) +# : $custom_fields{$rate_field}; +# $str .= ' x ' . $custom_fields{$unit_field}; return $str; } @@ -103,11 +113,31 @@ sub calc_usage { FS::TicketSystem->init(); - # not date delimited--load all resolved tickets - # will subtract previous charges below - # only way to be sure we've caught everything - # limit set to be arbitrarily large (10000) - my $tickets = FS::TicketSystem->customer_tickets( $cust_pkg->custnum, 10000, undef, 'resolved'); + my %queues = FS::TicketSystem->queues(undef,'SeeCustomField'); + + my @tickets; + foreach my $queueid ( + split(', ',$self->option('queueids',1) || '') + ) { + + die "Insufficient permission to invoice package" + unless exists $queues{$queueid}; + + # load all resolved tickets since pkg was ordered + # will subtract previous charges below + # only way to be sure we've caught everything + my $tickets = FS::TicketSystem->customer_tickets({ + number => $cust_pkg->custnum, + limit => 10000, # arbitrarily large + status => 'resolved', + queueid => $queueid, + resolved => $cust_pkg->order_date, # or setup? but this is mainly for installations, + # and workflow might resolve tickets before first bill... + # for now, expect pkg to be ordered before tickets get resolved, + # easy enough to make a pkg option to use setup/sdate instead + }); + push @tickets, @$tickets; + }; my $rate = $self->option('rate_flat',1); my $rate_field = $self->option('rate_field',1); @@ -124,7 +154,7 @@ sub calc_usage { $unit_field = 'CF.{' . $unit_field . '}'; my $charges = 0; - foreach my $ticket ( @$tickets ) { + foreach my $ticket ( @tickets ) { next unless $ticket->{$unit_field}; next unless $rate || $ticket->{$rate_field}; my $trate = $rate || $ticket->{$rate_field}; |