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 containing the following keys:
283 =item I<countrycode> - required.
285 =item I<phonenum> - required.
287 =item I<weektime> - optional. Specifies a time in seconds from the start
288 of the week, and will return a timed rate (one with a non-null I<ratetimenum>)
289 if one exists at that time. If not, returns a non-timed rate.
291 =item I<cdrtypenum> - optional. Specifies a value for the cdrtypenum
292 field, and will return a rate matching that, if one exists. If not, returns
293 a rate with null cdrtypenum.
300 my( $regionnum, $weektime, $cdrtypenum );
301 if ( ref($_[0]) eq 'HASH' ) {
303 my $countrycode = $_[0]->{'countrycode'};
304 my $phonenum = $_[0]->{'phonenum'};
305 $weektime = $_[0]->{'weektime'};
306 $cdrtypenum = $_[0]->{'cdrtypenum'} || '';
308 #find a rate prefix, first look at most specific, then fewer digits,
309 # finally trying the country code only
310 my $rate_prefix = '';
311 for my $len ( reverse(1..10) ) {
312 $rate_prefix = qsearchs('rate_prefix', {
313 'countrycode' => $countrycode,
314 #'npa' => { op=> 'LIKE', value=> substr($number, 0, $len) }
315 'npa' => substr($phonenum, 0, $len),
318 $rate_prefix ||= qsearchs('rate_prefix', {
319 'countrycode' => $countrycode,
323 return '' unless $rate_prefix;
325 $regionnum = $rate_prefix->regionnum;
328 $regionnum = ref($_[0]) ? shift->regionnum : shift;
332 'ratenum' => $self->ratenum,
333 'dest_regionnum' => $regionnum,
336 # find all rates matching ratenum, regionnum, cdrtypenum
337 my @details = qsearch( 'rate_detail', {
339 'cdrtypenum' => $cdrtypenum
341 # find all rates maching ratenum, regionnum and null cdrtypenum
342 if ( !@details and $cdrtypenum ) {
343 @details = qsearch( 'rate_detail', {
348 # find one of those matching weektime
349 if ( defined($weektime) ) {
351 my $rate_time = $_->rate_time;
352 $rate_time && $rate_time->contains($weektime)
357 elsif ( @exact > 1 ) {
358 die "overlapping rate_detail times (region $regionnum, time $weektime)\n"
362 # if not found or there is no weektime, find one matching null weektime
364 return $_ if $_->ratetimenum eq '';
372 Returns all region-specific details (see L<FS::rate_detail>) for this rate.
378 qsearch( 'rate_detail', { 'ratenum' => $self->ratenum } );
390 Experimental job-queue processor for web interface adds/edits
394 use Storable qw(thaw);
400 my $param = thaw(decode_base64(shift));
401 warn Dumper($param) if $DEBUG;
403 my $old = qsearchs('rate', { 'ratenum' => $param->{'ratenum'} } )
404 if $param->{'ratenum'};
406 my @rate_detail = map {
408 my $regionnum = $_->regionnum;
409 if ( $param->{"sec_granularity$regionnum"} ) {
411 new FS::rate_detail {
412 'dest_regionnum' => $regionnum,
413 map { $_ => $param->{"$_$regionnum"} }
414 qw( min_included min_charge sec_granularity )
415 #qw( min_included conn_charge conn_sec min_charge sec_granularity )
420 new FS::rate_detail {
421 'dest_regionnum' => $regionnum,
427 'sec_granularity' => '60'
432 } qsearch('rate_region', {} );
434 my $rate = new FS::rate {
435 map { $_ => $param->{$_} }
440 if ( $param->{'ratenum'} ) {
441 warn "$rate replacing $old (". $param->{'ratenum'}. ")\n" if $DEBUG;
443 my @param = ( 'job'=>$job );
444 push @param, 'rate_detail'=>\@rate_detail
445 unless $param->{'preserve_rate_detail'};
447 $error = $rate->replace( $old, @param );
450 warn "inserting $rate\n" if $DEBUG;
451 $error = $rate->insert( 'rate_detail' => \@rate_detail,
454 #$ratenum = $rate->getfield('ratenum');
457 die "$error\n" if $error;
465 L<FS::Record>, schema.html from the base documentation.