1 package FS::part_pkg::rt_field;
6 use FS::Record qw(qsearchs qsearch);
7 use FS::part_pkg::recur_Common;
8 use FS::part_pkg::global_Mixin;
9 use FS::rt_field_charge;
11 our @ISA = qw(FS::part_pkg::recur_Common);
15 use vars qw( $conf $money_char );
17 FS::UID->install_callback( sub {
19 $money_char = $conf->config('money_char') || '$';
23 'type' => 'select-rt-customfield',
24 'lookuptype' => 'RT::Queue-RT::Ticket',
29 'parse' => sub { @_ }, # because /edit/process/part_pkg.pm doesn't grok select multiple
33 'name' => 'Bill from custom fields in resolved RT tickets',
34 'shortname' => 'RT custom rate',
36 'inherit_fields' => [ 'global_Mixin' ],
38 'queueids' => { 'name' => 'Queues',
39 'type' => 'select-rt-queue',
41 'validate' => sub { return ${$_[1]} ? '' : 'Queue must be specified' },
43 'unit_field' => { 'name' => 'Units field',
45 'validate' => sub { return ${$_[1]} ? '' : 'Units field must be specified' },
47 'rate_field' => { 'name' => 'Charge per unit (from RT field)',
51 'rate_flat' => { 'name' => 'Charge per unit (flat)',
52 'validate' => \&FS::part_pkg::global_Mixin::validate_moneyn },
53 'display_fields' => { 'name' => 'Display fields',
57 # from global_Mixin, but don't get used by this at all
58 'unused_credit_cancel' => {'disabled' => 1},
59 'unused_credit_suspend' => {'disabled' => 1},
60 'unused_credit_change' => {'disabled' => 1},
64 return 'Rate must be specified'
65 unless $options->{'rate_field'} or $options->{'rate_flat'};
66 return 'Cannot specify both flat rate and rate field'
67 if $options->{'rate_field'} and $options->{'rate_flat'};
70 'fieldorder' => [ 'queueids', 'unit_field', 'rate_field', 'rate_flat', 'display_fields' ]
75 my $str = $self->SUPER::price_info;
76 $str .= ' plus ' if $str;
77 $str .= 'charge from RT';
78 # takes way too long just to get a package label
79 # FS::TicketSystem->init();
80 # my %custom_fields = FS::TicketSystem->custom_fields();
81 # my $rate = $self->option('rate_flat',1);
82 # my $rate_field = $self->option('rate_field',1);
83 # my $unit_field = $self->option('unit_field');
85 # ? $money_char . sprintf("%.2",$rate)
86 # : $custom_fields{$rate_field};
87 # $str .= ' x ' . $custom_fields{$unit_field};
92 my($self, $cust_pkg ) = @_;
93 $self->option('setup_fee');
98 my($cust_pkg, $sdate, $details, $param ) = @_;
102 $charges += $self->calc_usage(@_);
103 $charges += ($cust_pkg->quantity || 1) * $self->calc_recur_Common(@_);
109 sub can_discount { 0; }
113 my($cust_pkg, $sdate, $details, $param ) = @_;
115 FS::TicketSystem->init();
117 my %queues = FS::TicketSystem->queues(undef,'SeeCustomField');
120 foreach my $queueid (
121 split(', ',$self->option('queueids',1) || '')
124 die "Insufficient permission to invoice package"
125 unless exists $queues{$queueid};
127 # load all resolved tickets since pkg was ordered
128 # will subtract previous charges below
129 # only way to be sure we've caught everything
130 my $tickets = FS::TicketSystem->customer_tickets({
131 number => $cust_pkg->custnum,
132 limit => 10000, # arbitrarily large
133 status => 'resolved',
135 resolved => $cust_pkg->order_date, # or setup? but this is mainly for installations,
136 # and workflow might resolve tickets before first bill...
137 # for now, expect pkg to be ordered before tickets get resolved,
138 # easy enough to make a pkg option to use setup/sdate instead
140 push @tickets, @$tickets;
143 my $rate = $self->option('rate_flat',1);
144 my $rate_field = $self->option('rate_field',1);
145 my $unit_field = $self->option('unit_field');
146 my @display_fields = split(', ',$self->option('display_fields',1) || '');
148 my %custom_fields = FS::TicketSystem->custom_fields();
149 my $rate_label = $rate
151 : ' ' . $custom_fields{$rate_field};
152 my $unit_label = $custom_fields{$unit_field};
154 $rate_field = 'CF.{' . $rate_field . '}' if $rate_field;
155 $unit_field = 'CF.{' . $unit_field . '}';
158 foreach my $ticket ( @tickets ) {
159 next unless $ticket->{$unit_field};
160 next unless $rate || $ticket->{$rate_field};
161 my $trate = $rate || $ticket->{$rate_field};
162 my $tunit = $ticket->{$unit_field};
163 my $subcharge = sprintf('%.2f', $trate * $tunit);
164 my $precharge = _previous_charges( $cust_pkg->pkgnum, $ticket->{'id'} );
165 $subcharge -= $precharge;
167 # if field values for previous charges increased,
168 # we can make additional charges here and now,
169 # but if field values were decreased, we just ignore--
170 # credits will have to be applied manually later, if that's what's intended
171 next if $subcharge <= 0;
173 my $rt_field_charge = new FS::rt_field_charge {
174 'pkgnum' => $cust_pkg->pkgnum,
175 'ticketid' => $ticket->{'id'},
178 'charge' => $subcharge,
181 my $error = $rt_field_charge->insert;
182 die "Error inserting rt_field_charge: $error" if $error;
183 push @$details, $money_char . sprintf('%.2f',$trate) . $rate_label . ' x ' . $tunit . ' ' . $unit_label;
184 push @$details, ' - ' . $money_char . sprintf('%.2f',$precharge) . ' previously charged' if $precharge;
186 sort { $ticket->{'_cf_sort_order'}{$a} <=> $ticket->{'_cf_sort_order'}{$b} } @display_fields
188 my $label = $custom_fields{$field};
189 my $value = $ticket->{'CF.{' . $field . '}'};
190 push @$details, $label . ': ' . $value if $value;
192 $charges += $subcharge;
197 sub _previous_charges {
198 my ($pkgnum, $ticketid) = @_;
200 foreach my $rt_field_charge (
201 qsearch('rt_field_charge', { pkgnum => $pkgnum, ticketid => $ticketid })
203 $prev += $rt_field_charge->charge;