added svc_broadband description which is appended to Site Name in prizm export
[freeside.git] / FS / FS / svc_broadband.pm
1 package FS::svc_broadband;
2
3 use strict;
4 use vars qw(@ISA $conf);
5 use FS::Record qw( qsearchs qsearch dbh );
6 use FS::svc_Common;
7 use FS::cust_svc;
8 use FS::addr_block;
9 use NetAddr::IP;
10
11 @ISA = qw( FS::svc_Common );
12
13 $FS::UID::callback{'FS::svc_broadband'} = sub { 
14   $conf = new FS::Conf;
15 };
16
17 =head1 NAME
18
19 FS::svc_broadband - Object methods for svc_broadband records
20
21 =head1 SYNOPSIS
22
23   use FS::svc_broadband;
24
25   $record = new FS::svc_broadband \%hash;
26   $record = new FS::svc_broadband { 'column' => 'value' };
27
28   $error = $record->insert;
29
30   $error = $new_record->replace($old_record);
31
32   $error = $record->delete;
33
34   $error = $record->check;
35
36   $error = $record->suspend;
37
38   $error = $record->unsuspend;
39
40   $error = $record->cancel;
41
42 =head1 DESCRIPTION
43
44 An FS::svc_broadband object represents a 'broadband' Internet connection, such
45 as a DSL, cable modem, or fixed wireless link.  These services are assumed to
46 have the following properties:
47
48 FS::svc_broadband inherits from FS::svc_Common.  The following fields are
49 currently supported:
50
51 =over 4
52
53 =item svcnum - primary key
54
55 =item blocknum - see FS::addr_block
56
57 =item
58 speed_up - maximum upload speed, in bits per second.  If set to zero, upload
59 speed will be unlimited.  Exports that do traffic shaping should handle this
60 correctly, and not blindly set the upload speed to zero and kill the customer's
61 connection.
62
63 =item
64 speed_down - maximum download speed, as above
65
66 =item ip_addr - the customer's IP address.  If the customer needs more than one
67 IP address, set this to the address of the customer's router.  As a result, the
68 customer's router will have the same address for both its internal and external
69 interfaces thus saving address space.  This has been found to work on most NAT
70 routers available.
71
72 =back
73
74 =head1 METHODS
75
76 =over 4
77
78 =item new HASHREF
79
80 Creates a new svc_broadband.  To add the record to the database, see
81 "insert".
82
83 Note that this stores the hash reference, not a distinct copy of the hash it
84 points to.  You can ask the object for a copy with the I<hash> method.
85
86 =cut
87
88 sub table_info {
89   {
90     'name' => 'Broadband',
91     'name_plural' => 'Broadband services',
92     'longname_plural' => 'Fixed (username-less) broadband services',
93     'display_weight' => 50,
94     'cancel_weight'  => 70,
95     'fields' => {
96       'description' => 'Descriptive label for this particular device.',
97       'speed_down'  => 'Maximum download speed for this service in Kbps.  0 denotes unlimited.',
98       'speed_up'    => 'Maximum upload speed for this service in Kbps.  0 denotes unlimited.',
99       'ip_addr'     => 'IP address.  Leave blank for automatic assignment.',
100       'blocknum'    => 'Address block.',
101     },
102   };
103 }
104
105 sub table { 'svc_broadband'; }
106
107 =item search_sql STRING
108
109 Class method which returns an SQL fragment to search for the given string.
110
111 =cut
112
113 sub search_sql {
114   my( $class, $string ) = @_;
115   if ( $string =~ /^(\d{1,3}\.){3}\d{1,3}$/ ) {
116     $class->search_sql_field('ip_addr', $string );
117   } else {
118     '1 = 0'; #false
119   }
120 }
121
122 =item label
123
124 Returns the IP address.
125
126 =cut
127
128 sub label {
129   my $self = shift;
130   $self->ip_addr;
131 }
132
133 =item insert [ , OPTION => VALUE ... ]
134
135 Adds this record to the database.  If there is an error, returns the error,
136 otherwise returns false.
137
138 The additional fields pkgnum and svcpart (see FS::cust_svc) should be 
139 defined.  An FS::cust_svc record will be created and inserted.
140
141 Currently available options are: I<depend_jobnum>
142
143 If I<depend_jobnum> is set (to a scalar jobnum or an array reference of
144 jobnums), all provisioning jobs will have a dependancy on the supplied
145 jobnum(s) (they will not run until the specific job(s) complete(s)).
146
147 =cut
148
149 # Standard FS::svc_Common::insert
150
151 =item delete
152
153 Delete this record from the database.
154
155 =cut
156
157 # Standard FS::svc_Common::delete
158
159 =item replace OLD_RECORD
160
161 Replaces the OLD_RECORD with this one in the database.  If there is an error,
162 returns the error, otherwise returns false.
163
164 =cut
165
166 # Standard FS::svc_Common::replace
167
168 =item suspend
169
170 Called by the suspend method of FS::cust_pkg (see FS::cust_pkg).
171
172 =item unsuspend
173
174 Called by the unsuspend method of FS::cust_pkg (see FS::cust_pkg).
175
176 =item cancel
177
178 Called by the cancel method of FS::cust_pkg (see FS::cust_pkg).
179
180 =item check
181
182 Checks all fields to make sure this is a valid broadband service.  If there is
183 an error, returns the error, otherwise returns false.  Called by the insert
184 and replace methods.
185
186 =cut
187
188 sub check {
189   my $self = shift;
190   my $x = $self->setfixed;
191
192   return $x unless ref($x);
193
194   my $error =
195     $self->ut_numbern('svcnum')
196     || $self->ut_foreign_key('blocknum', 'addr_block', 'blocknum')
197     || $self->ut_textn('description')
198     || $self->ut_number('speed_up')
199     || $self->ut_number('speed_down')
200     || $self->ut_ipn('ip_addr')
201     || $self->ut_hexn('mac_addr')
202     || $self->ut_hexn('auth_key')
203     || $self->ut_floatn('latitude')
204     || $self->ut_floatn('longitude')
205     || $self->ut_floatn('altitude')
206     || $self->ut_textn('vlan_profile')
207   ;
208   return $error if $error;
209
210   if($self->speed_up < 0) { return 'speed_up must be positive'; }
211   if($self->speed_down < 0) { return 'speed_down must be positive'; }
212
213   if($self->latitude < -90 || $self->latitude > 90) {
214     return 'latitude must be between -90 and 90';
215   }
216   if($self->longitude < -180 || $self->longitude > 180) {
217     return 'longitude must be between -180 and 180';
218   }
219
220   if (not($self->ip_addr) or $self->ip_addr eq '0.0.0.0') {
221     my $next_addr = $self->addr_block->next_free_addr;
222     if ($next_addr) {
223       $self->ip_addr($next_addr->addr);
224     } else {
225       return "No free addresses in addr_block (blocknum: ".$self->blocknum.")";
226     }
227   }
228
229   # This should catch errors in the ip_addr.  If it doesn't,
230   # they'll almost certainly not map into the block anyway.
231   my $self_addr = $self->NetAddr; #netmask is /32
232   return ('Cannot parse address: ' . $self->ip_addr) unless $self_addr;
233
234   my $block_addr = $self->addr_block->NetAddr;
235   unless ($block_addr->contains($self_addr)) {
236     return 'blocknum '.$self->blocknum.' does not contain address '.$self->ip_addr;
237   }
238
239   my $router = $self->addr_block->router 
240     or return 'Cannot assign address from unallocated block:'.$self->addr_block->blocknum;
241   if(grep { $_->routernum == $router->routernum} $self->allowed_routers) {
242   } # do nothing
243   else {
244     return 'Router '.$router->routernum.' cannot provide svcpart '.$self->svcpart;
245   }
246
247   $self->SUPER::check;
248 }
249
250 =item NetAddr
251
252 Returns a NetAddr::IP object containing the IP address of this service.  The netmask 
253 is /32.
254
255 =cut
256
257 sub NetAddr {
258   my $self = shift;
259   return new NetAddr::IP ($self->ip_addr);
260 }
261
262 =item addr_block
263
264 Returns the FS::addr_block record (i.e. the address block) for this broadband service.
265
266 =cut
267
268 sub addr_block {
269   my $self = shift;
270
271   return qsearchs('addr_block', { blocknum => $self->blocknum });
272 }
273
274 =back
275
276 =item allowed_routers
277
278 Returns a list of allowed FS::router objects.
279
280 =cut
281
282 sub allowed_routers {
283   my $self = shift;
284
285   return map { $_->router } qsearch('part_svc_router', { svcpart => $self->svcpart });
286 }
287
288 =head1 BUGS
289
290 The business with sb_field has been 'fixed', in a manner of speaking.
291
292 =head1 SEE ALSO
293
294 FS::svc_Common, FS::Record, FS::addr_block,
295 FS::part_svc, schema.html from the base documentation.
296
297 =cut
298
299 1;
300