summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authorJonathan Prykop <jonathan@freeside.biz>2016-07-18 19:29:59 -0500
committerJonathan Prykop <jonathan@freeside.biz>2016-08-14 19:17:24 -0500
commit4ec2818fa42bf6b3ed00f6888c744456855ec0f1 (patch)
tree0068cbc6a8f706476323bf1eb3e3eda2fcee3bd8 /FS
parent0edd5b9d5c03f02341b1004888f0f712c8defb47 (diff)
RT#38973: Bill for time worked on ticket resolution [fully functional]
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/TicketSystem/RT_Internal.pm37
-rw-r--r--FS/FS/cust_pkg.pm14
-rw-r--r--FS/FS/part_pkg/rt_field.pm66
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};