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:
47 Optional agent (see L<FS::agent>) for agent-virtualized rates.
57 Creates a new rate plan. To add the rate plan to the database, see L<"insert">.
59 Note that this stores the hash reference, not a distinct copy of the hash it
60 points to. You can ask the object for a copy with the I<hash> method.
64 # the new method can be inherited from FS::Record, if a table method is defined
68 =item insert [ , OPTION => VALUE ... ]
70 Adds this record to the database. If there is an error, returns the error,
71 otherwise returns false.
73 Currently available options are: I<rate_detail>
75 If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
76 objects will have their ratenum field set and will be inserted after this
85 local $SIG{HUP} = 'IGNORE';
86 local $SIG{INT} = 'IGNORE';
87 local $SIG{QUIT} = 'IGNORE';
88 local $SIG{TERM} = 'IGNORE';
89 local $SIG{TSTP} = 'IGNORE';
90 local $SIG{PIPE} = 'IGNORE';
92 my $oldAutoCommit = $FS::UID::AutoCommit;
93 local $FS::UID::AutoCommit = 0;
96 my $error = $self->check;
97 return $error if $error;
99 $error = $self->SUPER::insert;
101 $dbh->rollback if $oldAutoCommit;
105 if ( $options{'rate_detail'} ) {
107 my( $num, $last, $min_sec ) = (0, time, 5); #progressbar foo
109 foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
111 $rate_detail->ratenum($self->ratenum);
112 $error = $rate_detail->insert;
114 $dbh->rollback if $oldAutoCommit;
118 if ( $options{'job'} ) {
120 if ( time - $min_sec > $last ) {
121 my $error = $options{'job'}->update_statustext(
122 int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
125 $dbh->rollback if $oldAutoCommit;
135 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
144 Delete this record from the database.
148 # the delete method can be inherited from FS::Record
150 =item replace OLD_RECORD [ , OPTION => VALUE ... ]
152 Replaces the OLD_RECORD with this one in the database. If there is an error,
153 returns the error, otherwise returns false.
155 Currently available options are: I<rate_detail>
157 If I<rate_detail> is set to an array reference of FS::rate_detail objects, the
158 objects will have their ratenum field set and will be inserted after this
159 record. Any existing rate_detail records associated with this record will be
165 my ($new, $old) = (shift, shift);
168 local $SIG{HUP} = 'IGNORE';
169 local $SIG{INT} = 'IGNORE';
170 local $SIG{QUIT} = 'IGNORE';
171 local $SIG{TERM} = 'IGNORE';
172 local $SIG{TSTP} = 'IGNORE';
173 local $SIG{PIPE} = 'IGNORE';
175 my $oldAutoCommit = $FS::UID::AutoCommit;
176 local $FS::UID::AutoCommit = 0;
179 # my @old_rate_detail = ();
180 # @old_rate_detail = $old->rate_detail if $options{'rate_detail'};
182 my $error = $new->SUPER::replace($old);
184 $dbh->rollback if $oldAutoCommit;
188 # foreach my $old_rate_detail ( @old_rate_detail ) {
190 # my $error = $old_rate_detail->delete;
192 # $dbh->rollback if $oldAutoCommit;
196 # if ( $options{'job'} ) {
198 # if ( time - $min_sec > $last ) {
199 # my $error = $options{'job'}->update_statustext(
200 # int( 50 * $num / scalar( @old_rate_detail ) )
203 # $dbh->rollback if $oldAutoCommit;
211 if ( $options{'rate_detail'} ) {
212 my $sth = $dbh->prepare('DELETE FROM rate_detail WHERE ratenum = ?') or do {
213 $dbh->rollback if $oldAutoCommit;
217 $sth->execute($old->ratenum) or do {
218 $dbh->rollback if $oldAutoCommit;
222 my( $num, $last, $min_sec ) = (0, time, 5); #progresbar foo
224 foreach my $rate_detail ( @{$options{'rate_detail'}} ) {
226 $rate_detail->ratenum($new->ratenum);
227 $error = $rate_detail->insert;
229 $dbh->rollback if $oldAutoCommit;
233 if ( $options{'job'} ) {
235 if ( time - $min_sec > $last ) {
236 my $error = $options{'job'}->update_statustext(
237 int( 100 * $num / scalar( @{$options{'rate_detail'}} ) )
240 $dbh->rollback if $oldAutoCommit;
251 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
258 Checks all fields to make sure this is a valid rate plan. If there is
259 an error, returns the error, otherwise returns false. Called by the insert
268 $self->ut_numbern('ratenum')
269 || $self->ut_text('ratename')
270 #|| $self->ut_foreign_keyn('agentnum', 'agent', 'agentnum')
272 return $error if $error;
277 =item dest_detail REGIONNUM | RATE_REGION_OBJECTD | HASHREF
279 Returns the rate detail (see L<FS::rate_detail>) for this rate to the
280 specificed destination, or the empty string if no rate can be found for
281 the given destination.
283 Destination can be specified as an FS::rate_detail object or regionnum
284 (see L<FS::rate_detail>), or as a hashref containing the following keys:
288 =item I<countrycode> - required.
290 =item I<phonenum> - required.
292 =item I<weektime> - optional. Specifies a time in seconds from the start
293 of the week, and will return a timed rate (one with a non-null I<ratetimenum>)
294 if one exists at that time. If not, returns a non-timed rate.
296 =item I<cdrtypenum> - optional. Specifies a value for the cdrtypenum
297 field, and will return a rate matching that, if one exists. If not, returns
298 a rate with null cdrtypenum.
305 my( $regionnum, $weektime, $cdrtypenum );
306 if ( ref($_[0]) eq 'HASH' ) {
308 my $countrycode = $_[0]->{'countrycode'};
309 my $phonenum = $_[0]->{'phonenum'};
310 $weektime = $_[0]->{'weektime'};
311 $cdrtypenum = $_[0]->{'cdrtypenum'} || '';
313 #find a rate prefix, first look at most specific, then fewer digits,
314 # finally trying the country code only
315 my $rate_prefix = '';
316 $rate_prefix = qsearchs({
317 'table' => 'rate_prefix',
318 'addl_from' => ' JOIN rate_region USING (regionnum)',
320 'countrycode' => $countrycode,
323 'extra_sql' => ' AND exact_match = \'Y\''
326 for my $len ( reverse(1..10) ) {
327 $rate_prefix = qsearchs('rate_prefix', {
328 'countrycode' => $countrycode,
329 #'npa' => { op=> 'LIKE', value=> substr($number, 0, $len) }
330 'npa' => substr($phonenum, 0, $len),
333 $rate_prefix ||= qsearchs('rate_prefix', {
334 'countrycode' => $countrycode,
339 return '' unless $rate_prefix;
341 $regionnum = $rate_prefix->regionnum;
344 $regionnum = ref($_[0]) ? shift->regionnum : shift;
348 'ratenum' => $self->ratenum,
349 'dest_regionnum' => $regionnum,
352 # find all rates matching ratenum, regionnum, cdrtypenum
353 my @details = qsearch( 'rate_detail', {
355 'cdrtypenum' => $cdrtypenum
357 # find all rates maching ratenum, regionnum and null cdrtypenum
358 if ( !@details and $cdrtypenum ) {
359 @details = qsearch( 'rate_detail', {
364 # find one of those matching weektime
365 if ( defined($weektime) ) {
367 my $rate_time = $_->rate_time;
368 $rate_time && $rate_time->contains($weektime)
373 elsif ( @exact > 1 ) {
374 die "overlapping rate_detail times (region $regionnum, time $weektime)\n"
378 # if not found or there is no weektime, find one matching null weektime
380 return $_ if $_->ratetimenum eq '';
388 Returns all region-specific details (see L<FS::rate_detail>) for this rate.
398 Job-queue processor for web interface adds/edits
402 use Storable qw(thaw);
408 my $param = thaw(decode_base64(shift));
409 warn Dumper($param) if $DEBUG;
411 my $old = qsearchs('rate', { 'ratenum' => $param->{'ratenum'} } )
412 if $param->{'ratenum'};
414 my @rate_detail = map {
416 my $regionnum = $_->regionnum;
417 if ( $param->{"sec_granularity$regionnum"} ) {
419 new FS::rate_detail {
420 'dest_regionnum' => $regionnum,
421 map { $_ => $param->{"$_$regionnum"} }
422 qw( min_included min_charge sec_granularity )
423 #qw( min_included conn_charge conn_sec min_charge sec_granularity )
428 new FS::rate_detail {
429 'dest_regionnum' => $regionnum,
435 'sec_granularity' => '60'
440 } qsearch('rate_region', {} );
442 my $rate = new FS::rate {
443 map { $_ => $param->{$_} }
448 if ( $param->{'ratenum'} ) {
449 warn "$rate replacing $old (". $param->{'ratenum'}. ")\n" if $DEBUG;
451 my @param = ( 'job'=>$job );
452 push @param, 'rate_detail'=>\@rate_detail
453 unless $param->{'preserve_rate_detail'};
455 $error = $rate->replace( $old, @param );
458 warn "inserting $rate\n" if $DEBUG;
459 $error = $rate->insert( 'rate_detail' => \@rate_detail,
462 #$ratenum = $rate->getfield('ratenum');
465 die "$error\n" if $error;
473 L<FS::Record>, schema.html from the base documentation.