per-agent disable_previous_balance, #15863
[freeside.git] / FS / FS / phone_avail.pm
1 package FS::phone_avail;
2
3 use strict;
4 use vars qw( @ISA $DEBUG $me );
5 use FS::Record qw( qsearch qsearchs dbh );
6 use FS::cust_svc;
7 use FS::Misc::DateTime qw( parse_datetime );
8 use FS::msa;
9 use Data::Dumper;
10
11 @ISA = qw(FS::cust_main_Mixin FS::Record);
12
13 $me = '[FS::phone_avail]';
14 $DEBUG = 0;
15
16 =head1 NAME
17
18 FS::phone_avail - Phone number availability cache
19
20 =head1 SYNOPSIS
21
22   use FS::phone_avail;
23
24   $record = new FS::phone_avail \%hash;
25   $record = new FS::phone_avail { 'column' => 'value' };
26
27   $error = $record->insert;
28
29   $error = $new_record->replace($old_record);
30
31   $error = $record->delete;
32
33   $error = $record->check;
34
35 =head1 DESCRIPTION
36
37 An FS::phone_avail object represents availability of phone service.
38 FS::phone_avail inherits from FS::Record.  The following fields are currently
39 supported:
40
41 =over 4
42
43 =item availnum
44
45 primary key
46
47 =item exportnum
48
49 exportnum
50
51 =item countrycode
52
53 countrycode
54
55 =item state
56
57 state
58
59 =item npa
60
61 npa
62
63 =item nxx
64
65 nxx
66
67 =item station
68
69 station
70
71 =item name
72
73 Optional name
74
75 =item rate_center_abbrev - abbreviated rate center
76
77 =item latanum - LATA #
78
79 =item msanum - MSA #
80
81 =item ordernum - bulk DID order #
82
83 =item svcnum
84
85 =item availbatch
86
87 =back
88
89 =head1 METHODS
90
91 =over 4
92
93 =item new HASHREF
94
95 Creates a new record.  To add the record to the database, see L<"insert">.
96
97 Note that this stores the hash reference, not a distinct copy of the hash it
98 points to.  You can ask the object for a copy with the I<hash> method.
99
100 =cut
101
102 # the new method can be inherited from FS::Record, if a table method is defined
103
104 sub table { 'phone_avail'; }
105
106 =item insert
107
108 Adds this record to the database.  If there is an error, returns the error,
109 otherwise returns false.
110
111 =cut
112
113 # the insert method can be inherited from FS::Record
114
115 =item delete
116
117 Delete this record from the database.
118
119 =cut
120
121 # the delete method can be inherited from FS::Record
122
123 =item replace OLD_RECORD
124
125 Replaces the OLD_RECORD with this one in the database.  If there is an error,
126 returns the error, otherwise returns false.
127
128 =cut
129
130 # the replace method can be inherited from FS::Record
131
132 =item check
133
134 Checks all fields to make sure this is a valid record.  If there is
135 an error, returns the error, otherwise returns false.  Called by the insert
136 and replace methods.
137
138 =cut
139
140 # the check method should currently be supplied - FS::Record contains some
141 # data checking routines
142
143 sub check {
144   my $self = shift;
145
146   my $error = 
147     $self->ut_numbern('availnum')
148     || $self->ut_foreign_key('exportnum', 'part_export', 'exportnum' )
149     || $self->ut_number('countrycode')
150     || $self->ut_alphan('state')
151     || $self->ut_number('npa')
152     || $self->ut_numbern('nxx')
153     || $self->ut_numbern('station')
154     || $self->ut_textn('name')
155     || $self->ut_textn('rate_center_abbrev')
156     || $self->ut_foreign_keyn('latanum', 'lata', 'latanum' )
157     || $self->ut_foreign_keyn('msanum', 'msa', 'msanum' )
158     || $self->ut_foreign_keyn('ordernum', 'did_order', 'ordernum' )
159     || $self->ut_foreign_keyn('svcnum', 'cust_svc', 'svcnum' )
160     || $self->ut_textn('availbatch')
161   ;
162   return $error if $error;
163
164   $self->SUPER::check;
165 }
166
167 =item cust_svc
168
169 =cut
170
171 sub cust_svc {
172   my $self = shift;
173   return '' unless $self->svcnum;
174   qsearchs('cust_svc', { 'svcnum' => $self->svcnum });
175 }
176
177 =item part_export
178
179 =cut
180
181 sub part_export {
182   my $self = shift;
183   return '' unless $self->exportnum;
184   qsearchs('part_export', { 'exportnum' => $self->exportnum });
185 }
186
187 =item lata 
188
189 =cut
190
191 sub lata {
192   my $self = shift;
193   return '' unless $self->latanum;
194   qsearchs('lata', { 'latanum' => $self->latanum });
195 }
196
197 =item msa2msanum
198
199 Translate free-form MSA name to a msa.msanum
200
201 =cut
202
203 sub msa2msanum {
204     my $self = shift;
205     my $msa = shift;
206
207     if ( $msa =~ /(.+[^,])\s+(\w{2}(-\w{2})*)$/ ) {
208       $msa = "$1, $2";
209     }
210
211     my @msas = qsearch('msa', { 'description' => { 'op' => 'ILIKE',
212                                                    'value' => "%$msa%", }
213                               });
214     return 0 unless scalar(@msas);
215     my @msa = grep { $self->msatest($msa,$_->description) } @msas;
216     return 0 unless scalar(@msa) == 1;
217     $msa[0]->msanum;
218 }
219
220 sub msatest {
221     my $self = shift;
222     my ($their,$our) = (shift,shift);
223
224     $their =~ s/^\s+//;
225     $their =~ s/\s+$//;
226     $their =~ s/\s+/ /g;
227     return 1 if $our eq $their;
228
229     my $a = $our;
230     $a =~ s/,.*?$//;
231     return 1 if $a eq $their;
232     return 1 if ($our =~ /^([\w\s]+)-/ && $1 eq $their);
233     0;
234 }
235
236 sub process_batch_import {
237   my $job = shift;
238
239   my $numsub = sub {
240     my( $phone_avail, $value ) = @_;
241     $value =~ s/\D//g;
242     $value =~ /^(\d{3})(\d{3})(\d+)$/ or die "unparsable number $value\n";
243     #( $hash->{npa}, $hash->{nxx}, $hash->{station} ) = ( $1, $2, $3 );
244     $phone_avail->npa($1);
245     $phone_avail->nxx($2);
246     $phone_avail->station($3);
247   };
248
249   my $msasub = sub {
250     my( $phone_avail, $value ) = @_;
251     return '' if !$value;
252     my $msanum = $phone_avail->msa2msanum($value);
253     die "cannot translate MSA ($value) to msanum" unless $msanum;
254     $phone_avail->msanum($msanum);
255   };
256
257   my $opt = { 'table'   => 'phone_avail',
258               'params'  => [ 'availbatch', 'exportnum', 'countrycode', 'ordernum', 'vendor_order_id', 'confirmed' ],
259               'formats' => { 'default' => [ 'state', $numsub, 'name' ],
260                  'bulk' => [ 'state', $numsub, 'name', 'rate_center_abbrev', $msasub, 'latanum' ],
261                },
262                'postinsert_callback' => sub {  
263                     my $record = shift;
264                     if($record->ordernum) {
265                         my $did_order = qsearchs('did_order', 
266                                             { 'ordernum' => $record->ordernum } );
267                         if($did_order && !$did_order->received) {
268                             $did_order->received(time);
269                             $did_order->confirmed(parse_datetime($record->confirmed));
270                             $did_order->vendor_order_id($record->vendor_order_id);
271                             $did_order->replace;
272                         }
273                     }
274                }, 
275             };
276
277   FS::Record::process_batch_import( $job, $opt, @_ );
278 }
279
280 sub flush { # evil direct SQL
281     my $opt = shift;
282
283     if ( $opt->{'ratecenter'} =~ /^[\w\s]+$/
284         && $opt->{'state'} =~ /^[A-Z][A-Z]$/ 
285         && $opt->{'exportnum'} =~ /^\d+$/) {
286     my $sth = dbh->prepare('delete from phone_avail where exportnum = ? '.
287             ' and state = ? and name = ?');
288     $sth->execute($opt->{'exportnum'},$opt->{'state'},$opt->{'ratecenter'})
289         or die $sth->errstr;
290     }
291
292     '';
293 }
294
295 # Used by FS::Upgrade to migrate to a new database.
296 sub _upgrade_data {
297   my ($class, %opts) = @_;
298
299   warn "$me upgrading $class\n" if $DEBUG;
300
301   my $sth = dbh->prepare(
302     'UPDATE phone_avail SET svcnum = NULL
303        WHERE svcnum IS NOT NULL
304          AND 0 = ( SELECT COUNT(*) FROM svc_phone
305                      WHERE phone_avail.svcnum = svc_phone.svcnum )'
306   ) or die dbh->errstr;
307
308   $sth->execute or die $sth->errstr;
309
310 }
311
312 =back
313
314 =head1 BUGS
315
316 Sparse documentation.
317
318 =head1 SEE ALSO
319
320 L<FS::Record>, schema.html from the base documentation.
321
322 =cut
323
324 1;
325