RT#38973: Bill for time worked on ticket resolution [checkpoint, not ready for backport]
[freeside.git] / FS / FS / part_pkg / rt_field.pm
diff --git a/FS/FS/part_pkg/rt_field.pm b/FS/FS/part_pkg/rt_field.pm
new file mode 100644 (file)
index 0000000..1b18720
--- /dev/null
@@ -0,0 +1,177 @@
+package FS::part_pkg::rt_field;
+
+use strict;
+use FS::Conf;
+use FS::TicketSystem;
+use FS::Record qw(qsearchs qsearch);
+use FS::part_pkg::recur_Common;
+use FS::part_pkg::global_Mixin;
+use FS::rt_field_charge;
+
+our @ISA = qw(FS::part_pkg::recur_Common);
+
+our $DEBUG = 0;
+
+use vars qw( $conf $money_char );
+
+FS::UID->install_callback( sub {
+  $conf = new FS::Conf;
+  $money_char = $conf->config('money_char') || '$';
+});
+
+my %custom_field = (
+  'type'        => 'select-rt-customfield',
+  'lookuptype'  => 'RT::Queue-RT::Ticket',
+);
+
+our %info = (
+  'name'      =>  'Bill from custom fields in resolved RT tickets',
+  'shortname' =>  'RT custom rate',
+  'weight'    => 65,
+  'inherit_fields' => [ 'global_Mixin' ],
+  'fields'    =>  {
+    'unit_field'     => { 'name' => 'Units field',
+                          %custom_field,
+                          'validate' => sub { return ${$_[1]} ? '' : 'Units field must be specified' },
+                        },
+    'rate_field'     => { 'name' => 'Charge per unit (from RT field)',
+                          %custom_field,
+                          'empty_label' => '',
+                        },
+       'rate_flat'      => { 'name' => 'Charge per unit (flat)',
+                          '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
+                        },
+    # from global_Mixin, but don't get used by this at all
+    'unused_credit_cancel'  => {'disabled' => 1},
+    'unused_credit_suspend' => {'disabled' => 1},
+    'unused_credit_change'  => {'disabled' => 1},
+  },
+  'validate' => sub {
+    my $options = shift;
+    return 'Rate must be specified'
+      unless $options->{'rate_field'} or $options->{'rate_flat'};
+    return 'Cannot specify both flat rate and rate field'
+      if $options->{'rate_field'} and $options->{'rate_flat'};
+    return '';
+  },
+  'fieldorder' => [ '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};
+    return $str;
+}
+
+sub calc_setup {
+  my($self, $cust_pkg ) = @_;
+  $self->option('setup_fee');
+}
+
+sub calc_recur {
+  my $self = shift;
+  my($cust_pkg, $sdate, $details, $param ) = @_;
+
+  my $charges = 0;
+
+  $charges += $self->calc_usage(@_);
+  $charges += ($cust_pkg->quantity || 1) * $self->calc_recur_Common(@_);
+
+  $charges;
+
+}
+
+sub can_discount { 0; }
+
+sub calc_usage {
+  my $self = shift;
+  my($cust_pkg, $sdate, $details, $param ) = @_;
+
+  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 $rate = $self->option('rate_flat',1);
+  my $rate_field = $self->option('rate_field',1);
+  my $unit_field = $self->option('unit_field');
+  my @display_fields = split(', ',$self->option('display_fields',1) || '');
+
+  my %custom_fields = FS::TicketSystem->custom_fields();
+  my $rate_label = $rate
+                   ? ''
+                   : ' ' . $custom_fields{$rate_field};
+  my $unit_label = $custom_fields{$unit_field};
+
+  $rate_field = 'CF.{' . $rate_field . '}' if $rate_field;
+  $unit_field = 'CF.{' . $unit_field . '}';
+
+  my $charges = 0;
+  foreach my $ticket ( @$tickets ) {
+    next unless $ticket->{$unit_field};
+    next unless $rate || $ticket->{$rate_field};
+    my $trate = $rate || $ticket->{$rate_field};
+    my $tunit = $ticket->{$unit_field};
+    my $subcharge = sprintf('%.2f', $trate * $tunit);
+    my $precharge = _previous_charges( $cust_pkg->pkgnum, $ticket->{'id'} );
+    $subcharge -= $precharge;
+
+    # if field values for previous charges increased,
+    # we can make additional charges here and now,
+    # but if field values were decreased, we just ignore--
+    # credits will have to be applied manually later, if that's what's intended
+    next if $subcharge <= 0;
+
+    my $rt_field_charge = new FS::rt_field_charge {
+      'pkgnum' => $cust_pkg->pkgnum,
+      'ticketid' => $ticket->{'id'},
+      'rate' => $trate,
+      'units' => $tunit,
+      'charge' => $subcharge,
+      '_date' => $$sdate,
+    };
+    my $error = $rt_field_charge->insert;
+    die "Error inserting rt_field_charge: $error" if $error;
+    push @$details, $money_char . sprintf('%.2f',$trate) . $rate_label . ' x ' . $tunit . ' ' . $unit_label;
+    push @$details, ' - ' . $money_char . sprintf('%.2f',$precharge) . ' previously charged' if $precharge;
+    foreach my $field (
+      sort { $ticket->{'_cf_sort_order'}{$a} <=> $ticket->{'_cf_sort_order'}{$b} } @display_fields
+    ) {
+      my $label = $custom_fields{$field};
+      my $value = $ticket->{'CF.{' . $field . '}'};
+      push @$details, $label . ': ' . $value if $value;
+    }
+    $charges += $subcharge;
+  }
+  return $charges;
+}
+
+sub _previous_charges {
+  my ($pkgnum, $ticketid) = @_;
+  my $prev = 0;
+  foreach my $rt_field_charge (
+    qsearch('rt_field_charge', { pkgnum => $pkgnum, ticketid => $ticketid })
+  ) {
+    $prev += $rt_field_charge->charge;
+  }
+  return $prev;
+}
+
+1;