Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / FS / FS / deploy_zone.pm
1 package FS::deploy_zone;
2
3 use strict;
4 use base qw( FS::o2m_Common FS::Record );
5 use FS::Record qw( qsearch qsearchs dbh );
6 use Storable qw(thaw);
7 use MIME::Base64;
8
9 =head1 NAME
10
11 FS::deploy_zone - Object methods for deploy_zone records
12
13 =head1 SYNOPSIS
14
15   use FS::deploy_zone;
16
17   $record = new FS::deploy_zone \%hash;
18   $record = new FS::deploy_zone { 'column' => 'value' };
19
20   $error = $record->insert;
21
22   $error = $new_record->replace($old_record);
23
24   $error = $record->delete;
25
26   $error = $record->check;
27
28 =head1 DESCRIPTION
29
30 An FS::deploy_zone object represents a geographic zone where a certain kind
31 of service is available.  Currently we store this information to generate
32 the FCC Form 477 deployment reports, but it may find other uses later.
33
34 FS::deploy_zone inherits from FS::Record.  The following fields are currently
35 supported:
36
37 =over 4
38
39 =item zonenum
40
41 primary key
42
43 =item description
44
45 Optional text describing the zone.
46
47 =item agentnum
48
49 The agent that serves this zone.
50
51 =item dbaname
52
53 The name under which service is marketed in this zone.  If null, will 
54 default to the agent name.
55
56 =item zonetype
57
58 The way the zone geography is defined: "B" for a list of census blocks
59 (used by the FCC for fixed broadband service), "P" for a polygon (for 
60 mobile services).  See L<FS::deploy_zone_block> and L<FS::deploy_zone_vertex>.
61
62 =item technology
63
64 The FCC technology code for the type of service available.
65
66 =item spectrum
67
68 For mobile service zones, the FCC code for the RF band.
69
70 =item adv_speed_up
71
72 For broadband, the advertised upstream bandwidth in the zone.  If multiple
73 speed tiers are advertised, use the highest.
74
75 =item adv_speed_down
76
77 For broadband, the advertised downstream bandwidth in the zone.
78
79 =item cir_speed_up
80
81 For broadband, the contractually guaranteed upstream bandwidth, if that type
82 of service is sold.
83
84 =item cir_speed_down
85
86 For broadband, the contractually guaranteed downstream bandwidth, if that 
87 type of service is sold.
88
89 =item is_consumer
90
91 'Y' if this service is sold for consumer/household use.
92
93 =item is_business
94
95 'Y' if this service is sold to business or institutional use.  Not mutually
96 exclusive with is_consumer.
97
98 =item is_broadband
99
100 'Y' if this service includes broadband Internet.
101
102 =item is_voice
103
104 'Y' if this service includes voice communication.
105
106 =item active_date
107
108 The date this zone became active.
109
110 =item expire_date
111
112 The date this zone became inactive, if any.
113
114 =back
115
116 =head1 METHODS
117
118 =over 4
119
120 =item new HASHREF
121
122 Creates a new zone.  To add the zone to the database, see L<"insert">.
123
124 =cut
125
126 # the new method can be inherited from FS::Record, if a table method is defined
127
128 sub table { 'deploy_zone'; }
129
130 =item insert ELEMENTS
131
132 Adds this record to the database.  If there is an error, returns the error,
133 otherwise returns false.
134
135 =cut
136
137 # the insert method can be inherited from FS::Record
138
139 =item delete
140
141 Delete this record from the database.
142
143 =cut
144
145 sub delete {
146   my $oldAutoCommit = $FS::UID::AutoCommit;
147   local $FS::UID::AutoCommit = 0;
148   # clean up linked records
149   my $self = shift;
150   my $error = $self->process_o2m(
151     'table'   => $self->element_table,
152     'num_col' => 'zonenum',
153     'fields'  => 'zonenum',
154     'params'  => {},
155   ) || $self->SUPER::delete(@_);
156   
157   if ($error) {
158     dbh->rollback if $oldAutoCommit;
159     return $error;
160   }
161   '';
162 }
163
164 =item replace OLD_RECORD
165
166 Replaces the OLD_RECORD with this one in the database.  If there is an error,
167 returns the error, otherwise returns false.
168
169 =cut
170
171 # the replace method can be inherited from FS::Record
172
173 =item check
174
175 Checks all fields to make sure this is a valid zone record.  If there is
176 an error, returns the error, otherwise returns false.  Called by the insert
177 and replace methods.
178
179 =cut
180
181 sub check {
182   my $self = shift;
183
184   my $error = 
185     $self->ut_numbern('zonenum')
186     || $self->ut_text('description')
187     || $self->ut_number('agentnum')
188     || $self->ut_foreign_key('agentnum', 'agent', 'agentnum')
189     || $self->ut_textn('dbaname')
190     || $self->ut_enum('zonetype', [ 'B', 'P' ])
191     || $self->ut_number('technology')
192     || $self->ut_numbern('spectrum')
193     || $self->ut_decimaln('adv_speed_up', 3)
194     || $self->ut_decimaln('adv_speed_down', 3)
195     || $self->ut_decimaln('cir_speed_up', 3)
196     || $self->ut_decimaln('cir_speed_down', 3)
197     || $self->ut_flag('is_consumer')
198     || $self->ut_flag('is_business')
199     || $self->ut_flag('is_broadband')
200     || $self->ut_flag('is_voice')
201     || $self->ut_numbern('active_date')
202     || $self->ut_numbern('expire_date')
203   ;
204   return $error if $error;
205
206   foreach(qw(adv_speed_down adv_speed_up cir_speed_down cir_speed_up)) {
207     if ($self->get('is_broadband')) {
208       if (!$self->get($_)) {
209         $self->set($_, 0);
210       }
211     } else {
212       $self->set($_, '');
213     }
214   }
215   if (!$self->get('active_date')) {
216     $self->set('active_date', time);
217   }
218
219   $self->SUPER::check;
220 }
221
222 =item element_table
223
224 Returns the name of the table that contains the zone's elements (blocks or
225 vertices).
226
227 =cut
228
229 sub element_table {
230   my $self = shift;
231   if ($self->zonetype eq 'B') {
232     return 'deploy_zone_block';
233   } elsif ( $self->zonetype eq 'P') {
234     return 'deploy_zone_vertex';
235   } else {
236     die 'unknown zonetype';
237   }
238 }
239
240 =item deploy_zone_block
241
242 Returns the census block records in this zone, in order by census block
243 number.  Only appropriate to block-type zones.
244
245 =item deploy_zone_vertex
246
247 Returns the vertex records for this zone, in order by sequence number.  Only
248 appropriate to polygon-type zones.
249
250 =cut
251
252 sub deploy_zone_block {
253   my $self = shift;
254   qsearch({
255       table     => 'deploy_zone_block',
256       hashref   => { zonenum => $self->zonenum },
257       order_by  => ' ORDER BY censusblock',
258   });
259 }
260
261 sub deploy_zone_vertex {
262   my $self = shift;
263   qsearch({
264       table     => 'deploy_zone_vertex',
265       hashref   => { zonenum => $self->zonenum },
266       order_by  => ' ORDER BY vertexnum',
267   });
268 }
269
270 =back
271
272 =head2 SUBROUTINES
273
274 =over 4
275
276 =item process_batch_import JOB, PARAMS
277
278 =cut
279
280 sub process_batch_import {
281   eval {
282     use FS::deploy_zone_block;
283     use FS::deploy_zone_vertex;
284   };
285   my $job = shift;
286   my $param = shift;
287   if (!ref($param)) {
288     $param = thaw(decode_base64($param));
289   }
290
291   # even if creating a new zone, the deploy_zone object should already
292   # be inserted by this point
293   my $zonenum = $param->{zonenum}
294     or die "zonenum required";
295   my $zone = FS::deploy_zone->by_key($zonenum)
296     or die "deploy_zone #$zonenum not found";
297   my $opt;
298   if ( $zone->zonetype eq 'B' ) {
299     $opt = { 'table'    => 'deploy_zone_block',
300              'params'   => [ 'zonenum', 'censusyear' ],
301              'formats'  => { 'plain' => [ 'censusblock' ] },
302              'default_csv' => 1,
303            };
304     $job->update_statustext('1,Inserting census blocks');
305   } elsif ( $zone->zonetype eq 'P' ) {
306     $opt = { 'table'    => 'deploy_zone_vertex',
307              'params'   => [ 'zonenum' ],
308              'formats'  => { 'plain' => [ 'latitude', 'longitude' ] },
309              'default_csv' => 1,
310            };
311   } else {
312     die "don't know how to import to zonetype ".$zone->zonetype;
313   }
314
315   FS::Record::process_batch_import( $job, $opt, $param );
316
317 }
318         
319 =head1 BUGS
320
321 =head1 SEE ALSO
322
323 L<FS::Record>
324
325 =cut
326
327 1;
328