37e7d9d3fa184529147725b5dab72528661a0057
[freeside.git] / FS / FS / part_pkg / agent.pm
1 package FS::part_pkg::agent;
2 #use base qw(FS::part_pkg::recur_Common);
3 use base qw(FS::part_pkg::prorate);
4
5 use strict;
6 use vars qw($DEBUG $me %info);
7 use Date::Format;
8 use FS::Record qw( qsearch );
9 use FS::agent;
10 use FS::cust_main;
11
12 $DEBUG = 0;
13
14 $me = '[FS::part_pkg::agent]';
15
16 %info = (
17   'name'      => 'Wholesale bulk billing, for master customers of an agent.',
18   'shortname' => 'Wholesale bulk billing for agent.',
19   'inherit_fields' => [qw( prorate global_Mixin)],
20   'fields' => {
21     #'recur_method'  => { 'name' => 'Recurring fee method',
22     #                     #'type' => 'radio',
23     #                     #'options' => \%recur_method,
24     #                     'type' => 'select',
25     #                     'select_options' => \%recur_Common::recur_method,
26     #                   },
27     'cutoff_day'    => { 'name' => 'Billing Day (1 - 28)',
28                          'default' => '1',
29                        },
30     'add_full_period'=> { 'name' => 'When prorating first month, also bill '.
31                                     'for one full period after that',
32                           'type' => 'checkbox',
33                         },
34
35     'no_pkg_prorate'   => { 'name' => 'Disable prorating bulk packages (charge full price for packages active only a portion of the month)',
36                             'type' => 'checkbox',
37                           },
38
39     'display_separate_cust'=> { 'name' => 'Separate customer from package display on invoices',
40                                 'type' => 'checkbox',
41                               },
42
43   },
44
45   'fieldorder' => [qw( cutoff_day add_full_period no_pkg_prorate ) ],
46
47   'weight' => 52,
48
49 );
50
51 #some false laziness-ish w/bulk.pm...  not a lot
52 sub calc_recur {
53   my $self = shift;
54   my($cust_pkg, $sdate, $details, $param ) = @_;
55
56   my $last_bill = $cust_pkg->last_bill;
57
58   return sprintf("%.2f", $self->SUPER::calc_recur(@_) )
59     unless $$sdate > $last_bill;
60
61   my $conf = new FS::Conf;
62   my $money_char = $conf->config('money_char') || '$';
63   my $date_format = $conf->config('date_format') || '%m/%d/%Y';
64
65   my $total_agent_charge = 0;
66
67   warn "$me billing for agent packages from ". time2str('%x', $last_bill).
68                                        " to ". time2str('%x', $$sdate). "\n"
69     if $DEBUG;
70
71   my $prorate_ratio =   ( $$sdate                     - $last_bill )
72                       / ( $self->add_freq($last_bill) - $last_bill );
73
74   #almost always just one,
75   #unless you have multiple agents with same master customer0
76   my @agents = qsearch('agent', { 'agent_custnum' => $cust_pkg->custnum } );
77
78   foreach my $agent (@agents) {
79
80     warn "$me billing for agent ". $agent->agent. "\n"
81       if $DEBUG;
82
83     #not the most efficient to load them all into memory,
84     #but good enough for our current needs
85     my @cust_main = qsearch('cust_main', { 'agentnum' => $agent->agentnum } );
86
87     foreach my $cust_main (@cust_main) {
88
89       warn "$me billing agent charges for ". $cust_main->name_short. "\n"
90         if $DEBUG;
91
92       #make sure setup dates are filled in
93       my $error = $cust_main->bill; #options don't propogate from freeside-daily
94       die "Error pre-billing agent customer: $error" if $error;
95
96       my @cust_pkg = grep { my $setup  = $_->get('setup');
97                             my $cancel = $_->get('cancel');
98
99                             $setup < $$sdate  # END
100                             && ( ! $cancel || $cancel > $last_bill ) #START
101                           }
102                      $cust_main->all_pkgs;
103
104       my $cust_details = 0;
105
106       foreach my $cust_pkg ( @cust_pkg ) {
107
108         warn "$me billing agent charges for pkgnum ". $cust_pkg->pkgnum. "\n"
109           if $DEBUG;
110
111         my $pkg_details = '';
112
113         my $cust_location = $cust_pkg->cust_location;
114         $pkg_details .= $cust_location->locationname. ': '
115           if $cust_location->locationname;
116
117         my $part_pkg = $cust_pkg->part_pkg;
118
119         # + something to identify package... primary service probably
120         # no... package def for now
121         $pkg_details .= $part_pkg->pkg. ': ';
122
123         my $pkg_charge = 0;
124
125         my $quantity = $cust_pkg->quantity || 1;
126
127         #option to not fallback? via options above
128         my $pkg_setup_fee  =
129           $part_pkg->setup_cost || $part_pkg->option('setup_fee');
130         my $pkg_base_recur =
131           $part_pkg->recur_cost || $part_pkg->base_recur_permonth($cust_pkg);
132
133         my $pkg_start = $cust_pkg->get('setup');
134         if ( $pkg_start < $last_bill ) {
135           $pkg_start = $last_bill;
136         } elsif ( $pkg_setup_fee ) {
137           $pkg_charge += $quantity * $pkg_setup_fee;
138           $pkg_details .= $money_char.
139                           sprintf('%.2f setup', $quantity * $pkg_setup_fee );
140           $pkg_details .= sprintf(" ($quantity \@ $money_char". '%.2f)',
141                                   $pkg_setup_fee )
142             if $quantity > 1;
143           $pkg_details .= ', ';
144         }
145
146         my $pkg_end = $cust_pkg->get('cancel');
147         $pkg_end = ( !$pkg_end || $pkg_end > $$sdate ) ? $$sdate : $pkg_end;
148
149         my $pkg_recur_charge = $prorate_ratio * $pkg_base_recur;
150         $pkg_recur_charge *= ( $pkg_end - $pkg_start )
151                            / ( $$sdate  - $last_bill )
152           unless $self->option('no_pkg_prorate');
153
154         my $recur_charge += $pkg_recur_charge;
155
156         if ( $recur_charge ) {
157           $pkg_details .= $money_char.
158                           sprintf('%.2f', $quantity * $recur_charge );
159           $pkg_details .= sprintf(" ($quantity \@ $money_char". '%.2f)',
160                                   $recur_charge )
161             if $quantity > 1;
162           $pkg_details .= ' ('.  time2str($date_format, $pkg_start).
163                           ' - '. time2str($date_format, $pkg_end  ). ')';
164         }
165
166         $pkg_charge += $quantity * $recur_charge;
167
168         if ( $pkg_charge ) {
169           if ( $self->option('display_separate_cust') ) {
170             push @$details, $cust_main->name.':' unless $cust_details++;
171             push @$details, '    '.$pkg_details;
172           } else {
173             push @$details, $cust_main->name_short.': '. $pkg_details;
174           }
175         };
176
177         $total_agent_charge += $pkg_charge;
178
179       } #foreach $cust_pkg
180
181       push @$details, ' ' if $cust_details;
182
183     } #foreach $cust_main
184
185   } #foreach $agent;
186
187   my $charges = $total_agent_charge + $self->SUPER::calc_recur(@_); #prorate
188
189   sprintf('%.2f', $charges );
190
191 }
192
193 sub can_discount { 0; }
194
195 sub hide_svc_detail { 1; }
196
197 sub is_free { 0; }
198
199 sub can_usageprice { 0; }
200
201 1;
202