1b18720ac53cfb0cfccbb9399215ec8b4e2e098e
[freeside.git] / FS / FS / part_pkg / rt_field.pm
1 package FS::part_pkg::rt_field;
2
3 use strict;
4 use FS::Conf;
5 use FS::TicketSystem;
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;
10
11 our @ISA = qw(FS::part_pkg::recur_Common);
12
13 our $DEBUG = 0;
14
15 use vars qw( $conf $money_char );
16
17 FS::UID->install_callback( sub {
18   $conf = new FS::Conf;
19   $money_char = $conf->config('money_char') || '$';
20 });
21
22 my %custom_field = (
23   'type'        => 'select-rt-customfield',
24   'lookuptype'  => 'RT::Queue-RT::Ticket',
25 );
26
27 our %info = (
28   'name'      =>  'Bill from custom fields in resolved RT tickets',
29   'shortname' =>  'RT custom rate',
30   'weight'    => 65,
31   'inherit_fields' => [ 'global_Mixin' ],
32   'fields'    =>  {
33     'unit_field'     => { 'name' => 'Units field',
34                           %custom_field,
35                           'validate' => sub { return ${$_[1]} ? '' : 'Units field must be specified' },
36                         },
37     'rate_field'     => { 'name' => 'Charge per unit (from RT field)',
38                           %custom_field,
39                           'empty_label' => '',
40                         },
41         'rate_flat'      => { 'name' => 'Charge per unit (flat)',
42                           'validate' => \&FS::part_pkg::global_Mixin::validate_moneyn },
43     'display_fields' => { 'name' => 'Display fields',
44                           %custom_field,
45                           'multiple' => 1,
46                           'parse' => sub { @_ }, # because /edit/process/part_pkg.pm doesn't grok select multiple
47                         },
48     # from global_Mixin, but don't get used by this at all
49     'unused_credit_cancel'  => {'disabled' => 1},
50     'unused_credit_suspend' => {'disabled' => 1},
51     'unused_credit_change'  => {'disabled' => 1},
52   },
53   'validate' => sub {
54     my $options = shift;
55     return 'Rate must be specified'
56       unless $options->{'rate_field'} or $options->{'rate_flat'};
57     return 'Cannot specify both flat rate and rate field'
58       if $options->{'rate_field'} and $options->{'rate_flat'};
59     return '';
60   },
61   'fieldorder' => [ 'unit_field', 'rate_field', 'rate_flat', 'display_fields' ]
62 );
63
64 sub price_info {
65     my $self = shift;
66     my $str = $self->SUPER::price_info;
67     $str .= ' plus ' if $str;
68     FS::TicketSystem->init();
69     my %custom_fields = FS::TicketSystem->custom_fields();
70     my $rate = $self->option('rate_flat',1);
71     my $rate_field = $self->option('rate_field',1);
72     my $unit_field = $self->option('unit_field');
73     $str .= $rate
74             ? $money_char . sprintf("%.2",$rate)
75             : $custom_fields{$rate_field};
76     $str .= ' x ' . $custom_fields{$unit_field};
77     return $str;
78 }
79
80 sub calc_setup {
81   my($self, $cust_pkg ) = @_;
82   $self->option('setup_fee');
83 }
84
85 sub calc_recur {
86   my $self = shift;
87   my($cust_pkg, $sdate, $details, $param ) = @_;
88
89   my $charges = 0;
90
91   $charges += $self->calc_usage(@_);
92   $charges += ($cust_pkg->quantity || 1) * $self->calc_recur_Common(@_);
93
94   $charges;
95
96 }
97
98 sub can_discount { 0; }
99
100 sub calc_usage {
101   my $self = shift;
102   my($cust_pkg, $sdate, $details, $param ) = @_;
103
104   FS::TicketSystem->init();
105
106   # not date delimited--load all resolved tickets
107   # will subtract previous charges below
108   # only way to be sure we've caught everything
109   # limit set to be arbitrarily large (10000)
110   my $tickets = FS::TicketSystem->customer_tickets( $cust_pkg->custnum, 10000, undef, 'resolved');
111
112   my $rate = $self->option('rate_flat',1);
113   my $rate_field = $self->option('rate_field',1);
114   my $unit_field = $self->option('unit_field');
115   my @display_fields = split(', ',$self->option('display_fields',1) || '');
116
117   my %custom_fields = FS::TicketSystem->custom_fields();
118   my $rate_label = $rate
119                    ? ''
120                    : ' ' . $custom_fields{$rate_field};
121   my $unit_label = $custom_fields{$unit_field};
122
123   $rate_field = 'CF.{' . $rate_field . '}' if $rate_field;
124   $unit_field = 'CF.{' . $unit_field . '}';
125
126   my $charges = 0;
127   foreach my $ticket ( @$tickets ) {
128     next unless $ticket->{$unit_field};
129     next unless $rate || $ticket->{$rate_field};
130     my $trate = $rate || $ticket->{$rate_field};
131     my $tunit = $ticket->{$unit_field};
132     my $subcharge = sprintf('%.2f', $trate * $tunit);
133     my $precharge = _previous_charges( $cust_pkg->pkgnum, $ticket->{'id'} );
134     $subcharge -= $precharge;
135
136     # if field values for previous charges increased,
137     # we can make additional charges here and now,
138     # but if field values were decreased, we just ignore--
139     # credits will have to be applied manually later, if that's what's intended
140     next if $subcharge <= 0;
141
142     my $rt_field_charge = new FS::rt_field_charge {
143       'pkgnum' => $cust_pkg->pkgnum,
144       'ticketid' => $ticket->{'id'},
145       'rate' => $trate,
146       'units' => $tunit,
147       'charge' => $subcharge,
148       '_date' => $$sdate,
149     };
150     my $error = $rt_field_charge->insert;
151     die "Error inserting rt_field_charge: $error" if $error;
152     push @$details, $money_char . sprintf('%.2f',$trate) . $rate_label . ' x ' . $tunit . ' ' . $unit_label;
153     push @$details, ' - ' . $money_char . sprintf('%.2f',$precharge) . ' previously charged' if $precharge;
154     foreach my $field (
155       sort { $ticket->{'_cf_sort_order'}{$a} <=> $ticket->{'_cf_sort_order'}{$b} } @display_fields
156     ) {
157       my $label = $custom_fields{$field};
158       my $value = $ticket->{'CF.{' . $field . '}'};
159       push @$details, $label . ': ' . $value if $value;
160     }
161     $charges += $subcharge;
162   }
163   return $charges;
164 }
165
166 sub _previous_charges {
167   my ($pkgnum, $ticketid) = @_;
168   my $prev = 0;
169   foreach my $rt_field_charge (
170     qsearch('rt_field_charge', { pkgnum => $pkgnum, ticketid => $ticketid })
171   ) {
172     $prev += $rt_field_charge->charge;
173   }
174   return $prev;
175 }
176
177 1;