6 use FS::Record qw( qsearch qsearchs );
12 FS::tax_class - Object methods for tax_class records
18 $record = new FS::tax_class \%hash;
19 $record = new FS::tax_class { 'column' => 'value' };
21 $error = $record->insert;
23 $error = $new_record->replace($old_record);
25 $error = $record->delete;
27 $error = $record->check;
31 An FS::tax_class object represents a tax class. FS::tax_class
32 inherits from FS::Record. The following fields are currently supported:
42 Vendor of the tax data
50 Human readable description of the tax class
60 Creates a new tax class. To add the tax class to the database, see L<"insert">.
62 Note that this stores the hash reference, not a distinct copy of the hash it
63 points to. You can ask the object for a copy with the I<hash> method.
67 sub table { 'tax_class'; }
71 Adds this record to the database. If there is an error, returns the error,
72 otherwise returns false.
78 Delete this record from the database.
85 return "Can't delete a tax class which has tax rates!"
86 if qsearch( 'tax_rate', { 'taxclassnum' => $self->taxclassnum } );
88 return "Can't delete a tax class which has package tax rates!"
89 if qsearch( 'part_pkg_taxrate', { 'taxclassnum' => $self->taxclassnum } );
91 return "Can't delete a tax class which has package tax rates!"
92 if qsearch( 'part_pkg_taxrate', { 'taxclassnumtaxed' => $self->taxclassnum } );
94 return "Can't delete a tax class which has package tax overrides!"
95 if qsearch( 'part_pkg_taxoverride', { 'taxclassnum' => $self->taxclassnum } );
97 $self->SUPER::delete(@_);
101 =item replace OLD_RECORD
103 Replaces the OLD_RECORD with this one in the database. If there is an error,
104 returns the error, otherwise returns false.
110 Checks all fields to make sure this is a valid tax class. If there is
111 an error, returns the error, otherwise returns false. Called by the insert
120 $self->ut_numbern('taxclassnum')
121 || $self->ut_text('taxclass')
122 || $self->ut_textn('data_vendor')
123 || $self->ut_textn('description')
125 return $error if $error;
132 Loads part_pkg_taxrate records from an external CSV file. If there is
133 an error, returns the error, otherwise returns false.
138 my ($param, $job) = @_;
140 my $fh = $param->{filehandle};
141 my $format = $param->{'format'};
151 my ( $count, $last, $min_sec ) = (0, time, 5); #progressbar
154 while ( defined($line=<$fh>) );
158 if ( $format eq 'cch' || $format eq 'cch-update' ) {
159 @fields = qw( table name pos number length value description );
160 push @fields, 'actionflag' if $format eq 'cch-update';
165 if ($hash->{'table'} eq 'DETAIL') {
166 push @{$data->{'taxcat'}}, [ $hash->{'value'}, $hash->{'description'} ]
167 if ($hash->{'name'} eq 'TAXCAT' &&
168 (!exists($hash->{actionflag}) || $hash->{actionflag} eq 'I') );
170 push @{$data->{'taxtype'}}, [ $hash->{'value'}, $hash->{'description'} ]
171 if ($hash->{'name'} eq 'TAXTYPE' &&
172 (!exists($hash->{actionflag}) || $hash->{actionflag} eq 'I') );
174 if (exists($hash->{actionflag}) && $hash->{actionflag} eq 'D') {
175 my $name = $hash->{'name'};
176 my $value = $hash->{'value'};
177 return "Bad value for $name: $value"
178 unless $value =~ /^\d+$/;
180 if ($name eq 'TAXCAT' || $name eq 'TAXTYPE') {
181 my @tax_class = qsearch( 'tax_class',
182 { 'data_vendor' => 'cch' },
184 "AND taxclass LIKE '".
185 ($name eq 'TAXTYPE' ? $value : '%').":".
186 ($name eq 'TAXCAT' ? $value : '%')."'",
188 foreach (@tax_class) {
189 my $error = $_->delete;
190 return $error if $error;
198 for qw( data_vendor table name pos number length value description );
199 delete($hash->{actionflag}) if exists($hash->{actionflag});
207 my $sql = "SELECT DISTINCT ".
208 "substring(taxclass from 1 for position(':' in taxclass)-1),".
209 "substring(description from 1 for position(':' in description)-1) ".
210 "FROM tax_class WHERE data_vendor='cch'";
212 my $sth = $dbh->prepare($sql) or die $dbh->errstr;
213 $sth->execute or die $sth->errstr;
214 my @old_types = @{$sth->fetchall_arrayref};
216 $sql = "SELECT DISTINCT ".
217 "substring(taxclass from position(':' in taxclass)+1),".
218 "substring(description from position(':' in description)+1) ".
219 "FROM tax_class WHERE data_vendor='cch'";
221 $sth = $dbh->prepare($sql) or die $dbh->errstr;
222 $sth->execute or die $sth->errstr;
223 my @old_cats = @{$sth->fetchall_arrayref};
225 my $catcount = exists($data->{'taxcat'}) ? scalar(@{$data->{'taxcat'}})
227 my $typecount = exists($data->{'taxtype'}) ? scalar(@{$data->{'taxtype'}})
230 my $count = scalar(@old_types) * $catcount
231 + $typecount * (scalar(@old_cats) + $catcount);
233 $imported = 1 if $format eq 'cch-update'; #empty file ok
235 foreach my $type (@old_types) {
236 foreach my $cat (@{$data->{'taxcat'}}) {
238 if ( $job ) { # progress bar
239 if ( time - $min_sec > $last ) {
240 my $error = $job->update_statustext(
241 int( 100 * $imported / $count )
243 die $error if $error;
249 new FS::tax_class( { 'data_vendor' => 'cch',
250 'taxclass' => $type->[0].':'.$cat->[0],
251 'description' => $type->[1].':'.$cat->[1],
253 my $error = $tax_class->insert;
254 return $error if $error;
259 foreach my $type (@{$data->{'taxtype'}}) {
260 foreach my $cat (@old_cats, @{$data->{'taxcat'}}) {
262 if ( $job ) { # progress bar
263 if ( time - $min_sec > $last ) {
264 my $error = $job->update_statustext(
265 int( 100 * $imported / $count )
267 die $error if $error;
273 new FS::tax_class( { 'data_vendor' => 'cch',
274 'taxclass' => $type->[0].':'.$cat->[0],
275 'description' => $type->[1].':'.$cat->[1],
277 my $error = $tax_class->insert;
278 return $error if $error;
286 } elsif ( $format eq 'extended' ) {
287 die "unimplemented\n";
291 die "unknown format $format";
294 eval "use Text::CSV_XS;";
297 my $csv = new Text::CSV_XS;
299 local $SIG{HUP} = 'IGNORE';
300 local $SIG{INT} = 'IGNORE';
301 local $SIG{QUIT} = 'IGNORE';
302 local $SIG{TERM} = 'IGNORE';
303 local $SIG{TSTP} = 'IGNORE';
304 local $SIG{PIPE} = 'IGNORE';
306 my $oldAutoCommit = $FS::UID::AutoCommit;
307 local $FS::UID::AutoCommit = 0;
309 while ( defined($line=<$fh>) ) {
311 if ( $job ) { # progress bar
312 if ( time - $min_sec > $last ) {
313 my $error = $job->update_statustext(
314 int( 100 * $imported / $count )
316 die $error if $error;
321 $csv->parse($line) or do {
322 $dbh->rollback if $oldAutoCommit;
323 return "can't parse: ". $csv->error_input();
326 my @columns = $csv->fields();
328 my %tax_class = ( 'data_vendor' => $format );
329 foreach my $field ( @fields ) {
330 $tax_class{$field} = shift @columns;
332 if ( scalar( @columns ) ) {
333 $dbh->rollback if $oldAutoCommit;
334 return "Unexpected trailing columns in line (wrong format?): $line";
337 my $error = &{$hook}(\%tax_class);
339 $dbh->rollback if $oldAutoCommit;
343 next unless scalar(keys %tax_class);
345 my $tax_class = new FS::tax_class( \%tax_class );
346 $error = $tax_class->insert;
348 $dbh->rollback if $oldAutoCommit;
349 return "can't insert tax_class for $line: $error";
355 my $error = &{$endhook}();
357 $dbh->rollback if $oldAutoCommit;
358 return "can't insert tax_class for $line: $error";
361 $dbh->commit or die $dbh->errstr if $oldAutoCommit;
363 return "Empty File!" unless $imported;
373 batch_import does not handle mixed I and D records in the same file for
378 L<FS::Record>, schema.html from the base documentation.