4 use vars qw( @ISA $DEBUG );
5 use FS::Record qw( qsearch qsearchs dbh fields );
14 FS::rate - Object methods for rate records
20 $record = new FS::rate \%hash;
21 $record = new FS::rate { 'column' => 'value' };
23 $error = $record->insert;
25 $error = $new_record->replace($old_record);
27 $error = $record->delete;
29 $error = $record->check;
33 An FS::rate object represents an rate plan. FS::rate inherits from
34 FS::Record. The following fields are currently supported:
38 =item ratenum - primary key
50 Creates a new rate plan. To add the rate plan to the database, see L<"insert">.
52 Note that this stores the hash reference, not a distinct copy of the hash it
53 points to. You can ask the object for a copy with the I<hash> method.
57 # the new method can be inherited from FS::Record, if a table method is defined
61 =item insert [ , OPTION => VALUE ... ]
63 Adds this record to the database. If there is an error, returns the error,
64 otherwise returns false.
66 Currently available options are: I<rate_detail>
68 If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
69 objects will have their ratenum field set and will be inserted after this
78 local $SIG{HUP} = 'IGNORE';
79 local $SIG{INT} = 'IGNORE';
80 local $SIG{QUIT} = 'IGNORE';
81 local $SIG{TERM} = 'IGNORE';
82 local $SIG{TSTP} = 'IGNORE';
83 local $SIG{PIPE} = 'IGNORE';
85 my $oldAutoCommit = $FS::UID::AutoCommit;
86 local $FS::UID::AutoCommit = 0;
89 my $error = $self->check;
90 return $error if $error;
92 $error = $self->SUPER::insert;
94 $dbh->rollback if $oldAutoCommit;
98 if ( $options{'rate_detail'} ) {
100 my( $num, $last, $min_sec ) = (0, time, 5); #progressbar foo
102 foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
104 $rate_detail->ratenum($self->ratenum);
105 $error = $rate_detail->insert;
107 $dbh->rollback if $oldAutoCommit;
111 if ( $options{'job'} ) {
113 if ( time - $min_sec > $last ) {
114 my $error = $options{'job'}->update_statustext(
115 int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
118 $dbh->rollback if $oldAutoCommit;
128 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
137 Delete this record from the database.
141 # the delete method can be inherited from FS::Record
143 =item replace OLD_RECORD [ , OPTION => VALUE ... ]
145 Replaces the OLD_RECORD with this one in the database. If there is an error,
146 returns the error, otherwise returns false.
148 Currently available options are: I<rate_detail>
150 If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
151 objects will have their ratenum field set and will be inserted after this
152 record. Any existing rate_detail records associated with this record will be
158 my ($new, $old) = (shift, shift);
161 local $SIG{HUP} = 'IGNORE';
162 local $SIG{INT} = 'IGNORE';
163 local $SIG{QUIT} = 'IGNORE';
164 local $SIG{TERM} = 'IGNORE';
165 local $SIG{TSTP} = 'IGNORE';
166 local $SIG{PIPE} = 'IGNORE';
168 my $oldAutoCommit = $FS::UID::AutoCommit;
169 local $FS::UID::AutoCommit = 0;
172 # my @old_rate_detail = ();
173 # @old_rate_detail = $old->rate_detail if $options{'rate_detail'};
175 my $error = $new->SUPER::replace($old);
177 $dbh->rollback if $oldAutoCommit;
181 # foreach my $old_rate_detail ( @old_rate_detail ) {
183 # my $error = $old_rate_detail->delete;
185 # $dbh->rollback if $oldAutoCommit;
189 # if ( $options{'job'} ) {
191 # if ( time - $min_sec > $last ) {
192 # my $error = $options{'job'}->update_statustext(
193 # int( 50 * $num / scalar( @old_rate_detail ) )
196 # $dbh->rollback if $oldAutoCommit;
204 if ( $options{'rate_detail'} ) {
205 my $sth = $dbh->prepare('DELETE FROM rate_detail WHERE ratenum = ?') or do {
206 $dbh->rollback if $oldAutoCommit;
210 $sth->execute($old->ratenum) or do {
211 $dbh->rollback if $oldAutoCommit;
215 my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
217 foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
219 $rate_detail->ratenum($new->ratenum);
220 $error = $rate_detail->insert;
222 $dbh->rollback if $oldAutoCommit;
226 if ( $options{'job'} ) {
228 if ( time - $min_sec > $last ) {
229 my $error = $options{'job'}->update_statustext(
230 int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
233 $dbh->rollback if $oldAutoCommit;
244 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
251 Checks all fields to make sure this is a valid rate plan. If there is
252 an error, returns the error, otherwise returns false. Called by the insert
257 # the check method should currently be supplied - FS::Record contains some
258 # data checking routines
264 $self->ut_numbern('ratenum')
265 || $self->ut_text('ratename')
267 return $error if $error;
272 =item dest_detail REGIONNUM | RATE_REGION_OBJECTD | HASHREF
274 Returns the rate detail (see L<FS::rate_detail>) for this rate to the
275 specificed destination, or the empty string if no rate can be found for
276 the given destination.
278 Destination can be specified as an FS::rate_detail object or regionnum
279 (see L<FS::rate_detail>), or as a hashref with two keys: I<countrycode>
288 if ( ref($_[0]) eq 'HASH' ) {
290 my $countrycode = $_[0]->{'countrycode'};
291 my $phonenum = $_[0]->{'phonenum'};
293 #find a rate prefix, first look at most specific, then fewer digits,
294 # finally trying the country code only
295 my $rate_prefix = '';
296 for my $len ( reverse(1..10) ) {
297 $rate_prefix = qsearchs('rate_prefix', {
298 'countrycode' => $countrycode,
299 #'npa' => { op=> 'LIKE', value=> substr($number, 0, $len) }
300 'npa' => substr($phonenum, 0, $len),
303 $rate_prefix ||= qsearchs('rate_prefix', {
304 'countrycode' => $countrycode,
308 return '' unless $rate_prefix;
310 $regionnum = $rate_prefix->regionnum;
312 #$rate_region = $rate_prefix->rate_region;
315 $regionnum = ref($_[0]) ? shift->regionnum : shift;
318 qsearchs( 'rate_detail', { 'ratenum' => $self->ratenum,
319 'dest_regionnum' => $regionnum, } );
324 Returns all region-specific details (see L<FS::rate_detail>) for this rate.
330 qsearch( 'rate_detail', { 'ratenum' => $self->ratenum } );
342 Experimental job-queue processor for web interface adds/edits
346 use Storable qw(thaw);
352 my $param = thaw(decode_base64(shift));
353 warn Dumper($param) if $DEBUG;
355 my $old = qsearchs('rate', { 'ratenum' => $param->{'ratenum'} } )
356 if $param->{'ratenum'};
358 my @rate_detail = map {
360 my $regionnum = $_->regionnum;
361 if ( $param->{"sec_granularity$regionnum"} ) {
363 new FS::rate_detail {
364 'dest_regionnum' => $regionnum,
365 map { $_ => $param->{"$_$regionnum"} }
366 qw( min_included min_charge sec_granularity )
367 #qw( min_included conn_charge conn_sec min_charge sec_granularity )
372 new FS::rate_detail {
373 'dest_regionnum' => $regionnum,
379 'sec_granularity' => '60'
384 } qsearch('rate_region', {} );
386 my $rate = new FS::rate {
387 map { $_ => $param->{$_} }
392 if ( $param->{'ratenum'} ) {
393 warn "$rate replacing $old (". $param->{'ratenum'}. ")\n" if $DEBUG;
395 my @param = ( 'job'=>$job );
396 push @param, 'rate_detail'=>\@rate_detail
397 unless $param->{'preserve_rate_detail'};
399 $error = $rate->replace( $old, @param );
402 warn "inserting $rate\n" if $DEBUG;
403 $error = $rate->insert( 'rate_detail' => \@rate_detail,
406 #$ratenum = $rate->getfield('ratenum');
409 die "$error\n" if $error;
417 L<FS::Record>, schema.html from the base documentation.