2 use base qw(FS::Record);
6 use FS::Record qw( qsearch qsearchs dbh fields );
13 FS::rate - Object methods for rate records
19 $record = new FS::rate \%hash;
20 $record = new FS::rate { 'column' => 'value' };
22 $error = $record->insert;
24 $error = $new_record->replace($old_record);
26 $error = $record->delete;
28 $error = $record->check;
32 An FS::rate object represents an rate plan. FS::rate inherits from
33 FS::Record. The following fields are currently supported:
37 =item ratenum - primary key
49 Creates a new rate plan. To add the rate plan to the database, see L<"insert">.
51 Note that this stores the hash reference, not a distinct copy of the hash it
52 points to. You can ask the object for a copy with the I<hash> method.
56 # the new method can be inherited from FS::Record, if a table method is defined
60 =item insert [ , OPTION => VALUE ... ]
62 Adds this record to the database. If there is an error, returns the error,
63 otherwise returns false.
65 Currently available options are: I<rate_detail>
67 If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
68 objects will have their ratenum field set and will be inserted after this
77 local $SIG{HUP} = 'IGNORE';
78 local $SIG{INT} = 'IGNORE';
79 local $SIG{QUIT} = 'IGNORE';
80 local $SIG{TERM} = 'IGNORE';
81 local $SIG{TSTP} = 'IGNORE';
82 local $SIG{PIPE} = 'IGNORE';
84 my $oldAutoCommit = $FS::UID::AutoCommit;
85 local $FS::UID::AutoCommit = 0;
88 my $error = $self->check;
89 return $error if $error;
91 $error = $self->SUPER::insert;
93 $dbh->rollback if $oldAutoCommit;
97 if ( $options{'rate_detail'} ) {
99 my( $num, $last, $min_sec ) = (0, time, 5); #progressbar foo
101 foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
103 $rate_detail->ratenum($self->ratenum);
104 $error = $rate_detail->insert;
106 $dbh->rollback if $oldAutoCommit;
110 if ( $options{'job'} ) {
112 if ( time - $min_sec > $last ) {
113 my $error = $options{'job'}->update_statustext(
114 int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
117 $dbh->rollback if $oldAutoCommit;
127 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
136 Delete this record from the database.
140 # the delete method can be inherited from FS::Record
142 =item replace OLD_RECORD [ , OPTION => VALUE ... ]
144 Replaces the OLD_RECORD with this one in the database. If there is an error,
145 returns the error, otherwise returns false.
147 Currently available options are: I<rate_detail>
149 If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
150 objects will have their ratenum field set and will be inserted after this
151 record. Any existing rate_detail records associated with this record will be
157 my ($new, $old) = (shift, shift);
160 local $SIG{HUP} = 'IGNORE';
161 local $SIG{INT} = 'IGNORE';
162 local $SIG{QUIT} = 'IGNORE';
163 local $SIG{TERM} = 'IGNORE';
164 local $SIG{TSTP} = 'IGNORE';
165 local $SIG{PIPE} = 'IGNORE';
167 my $oldAutoCommit = $FS::UID::AutoCommit;
168 local $FS::UID::AutoCommit = 0;
171 # my @old_rate_detail = ();
172 # @old_rate_detail = $old->rate_detail if $options{'rate_detail'};
174 my $error = $new->SUPER::replace($old);
176 $dbh->rollback if $oldAutoCommit;
180 # foreach my $old_rate_detail ( @old_rate_detail ) {
182 # my $error = $old_rate_detail->delete;
184 # $dbh->rollback if $oldAutoCommit;
188 # if ( $options{'job'} ) {
190 # if ( time - $min_sec > $last ) {
191 # my $error = $options{'job'}->update_statustext(
192 # int( 50 * $num / scalar( @old_rate_detail ) )
195 # $dbh->rollback if $oldAutoCommit;
203 if ( $options{'rate_detail'} ) {
204 my $sth = $dbh->prepare('DELETE FROM rate_detail WHERE ratenum = ?') or do {
205 $dbh->rollback if $oldAutoCommit;
209 $sth->execute($old->ratenum) or do {
210 $dbh->rollback if $oldAutoCommit;
214 my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
216 foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
218 $rate_detail->ratenum($new->ratenum);
219 $error = $rate_detail->insert;
221 $dbh->rollback if $oldAutoCommit;
225 if ( $options{'job'} ) {
227 if ( time - $min_sec > $last ) {
228 my $error = $options{'job'}->update_statustext(
229 int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
232 $dbh->rollback if $oldAutoCommit;
243 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
250 Checks all fields to make sure this is a valid rate plan. If there is
251 an error, returns the error, otherwise returns false. Called by the insert
256 # the check method should currently be supplied - FS::Record contains some
257 # data checking routines
263 $self->ut_numbern('ratenum')
264 || $self->ut_text('ratename')
266 return $error if $error;
271 =item dest_detail REGIONNUM | RATE_REGION_OBJECTD | HASHREF
273 Returns the rate detail (see L<FS::rate_detail>) for this rate to the
274 specificed destination, or the empty string if no rate can be found for
275 the given destination.
277 Destination can be specified as an FS::rate_detail object or regionnum
278 (see L<FS::rate_detail>), or as a hashref containing the following keys:
282 =item I<countrycode> - required.
284 =item I<phonenum> - required.
286 =item I<weektime> - optional. Specifies a time in seconds from the start
287 of the week, and will return a timed rate (one with a non-null I<ratetimenum>)
288 if one exists at that time. If not, returns a non-timed rate.
290 =item I<cdrtypenum> - optional. Specifies a value for the cdrtypenum
291 field, and will return a rate matching that, if one exists. If not, returns
292 a rate with null cdrtypenum.
299 my( $regionnum, $weektime, $cdrtypenum );
300 if ( ref($_[0]) eq 'HASH' ) {
302 my $countrycode = $_[0]->{'countrycode'};
303 my $phonenum = $_[0]->{'phonenum'};
304 $weektime = $_[0]->{'weektime'};
305 $cdrtypenum = $_[0]->{'cdrtypenum'} || '';
307 #find a rate prefix, first look at most specific, then fewer digits,
308 # finally trying the country code only
309 my $rate_prefix = '';
310 $rate_prefix = qsearchs({
311 'table' => 'rate_prefix',
312 'addl_from' => ' JOIN rate_region USING (regionnum)',
314 'countrycode' => $countrycode,
317 'extra_sql' => ' AND exact_match = \'Y\''
320 for my $len ( reverse(1..10) ) {
321 $rate_prefix = qsearchs('rate_prefix', {
322 'countrycode' => $countrycode,
323 #'npa' => { op=> 'LIKE', value=> substr($number, 0, $len) }
324 'npa' => substr($phonenum, 0, $len),
327 $rate_prefix ||= qsearchs('rate_prefix', {
328 'countrycode' => $countrycode,
333 return '' unless $rate_prefix;
335 $regionnum = $rate_prefix->regionnum;
338 $regionnum = ref($_[0]) ? shift->regionnum : shift;
342 'ratenum' => $self->ratenum,
343 'dest_regionnum' => $regionnum,
346 # find all rates matching ratenum, regionnum, cdrtypenum
347 my @details = qsearch( 'rate_detail', {
349 'cdrtypenum' => $cdrtypenum
351 # find all rates maching ratenum, regionnum and null cdrtypenum
352 if ( !@details and $cdrtypenum ) {
353 @details = qsearch( 'rate_detail', {
358 # find one of those matching weektime
359 if ( defined($weektime) ) {
361 my $rate_time = $_->rate_time;
362 $rate_time && $rate_time->contains($weektime)
367 elsif ( @exact > 1 ) {
368 die "overlapping rate_detail times (region $regionnum, time $weektime)\n"
372 # if not found or there is no weektime, find one matching null weektime
374 return $_ if $_->ratetimenum eq '';
382 Returns all region-specific details (see L<FS::rate_detail>) for this rate.
392 Job-queue processor for web interface adds/edits
396 use Storable qw(thaw);
402 my $param = thaw(decode_base64(shift));
403 warn Dumper($param) if $DEBUG;
405 my $old = qsearchs('rate', { 'ratenum' => $param->{'ratenum'} } )
406 if $param->{'ratenum'};
408 my @rate_detail = map {
410 my $regionnum = $_->regionnum;
411 if ( $param->{"sec_granularity$regionnum"} ) {
413 new FS::rate_detail {
414 'dest_regionnum' => $regionnum,
415 map { $_ => $param->{"$_$regionnum"} }
416 qw( min_included min_charge sec_granularity )
417 #qw( min_included conn_charge conn_sec min_charge sec_granularity )
422 new FS::rate_detail {
423 'dest_regionnum' => $regionnum,
429 'sec_granularity' => '60'
434 } qsearch('rate_region', {} );
436 my $rate = new FS::rate {
437 map { $_ => $param->{$_} }
442 if ( $param->{'ratenum'} ) {
443 warn "$rate replacing $old (". $param->{'ratenum'}. ")\n" if $DEBUG;
445 my @param = ( 'job'=>$job );
446 push @param, 'rate_detail'=>\@rate_detail
447 unless $param->{'preserve_rate_detail'};
449 $error = $rate->replace( $old, @param );
452 warn "inserting $rate\n" if $DEBUG;
453 $error = $rate->insert( 'rate_detail' => \@rate_detail,
456 #$ratenum = $rate->getfield('ratenum');
459 die "$error\n" if $error;
467 L<FS::Record>, schema.html from the base documentation.