use Tie::IxHash;
use Time::Local qw( timelocal timelocal_nocheck );
use MIME::Entity;
-use FS::UID qw( getotaker dbh );
+use FS::UID qw( getotaker dbh driver_name );
use FS::Misc qw( send_email );
use FS::Record qw( qsearch qsearchs fields );
use FS::CurrentUser;
my $svc_error = $svc_x->insert;
if ( $svc_error && $options{svc_fatal} ) {
$dbh->rollback if $oldAutoCommit;
- return $error;
+ return $svc_error;
+ } else {
+ my $cust_svc = qsearchs('cust_svc', { 'svcnum' => $svc_x->svcnum });
+ if ( $cust_svc ) {
+ my $cs_error = $cust_svc->delete;
+ if ( $cs_error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $cs_error;
+ }
+ }
}
push @svc_errors, $svc_error if $svc_error;
}
}
if ( $opt{'svcdb'} ) {
$search{addl_from} = ' LEFT JOIN part_svc USING ( svcpart ) ';
- $search{hashref}->{svcdb} = $opt{'svcdb'};
+ $search{extra_sql} = ' AND svcdb = '. dbh->quote( $opt{'svcdb'} );
}
cluck "cust_pkg->cust_svc called" if $DEBUG > 2;
sub available_part_svc {
my $self = shift;
+
+ my $pkg_quantity = $self->quantity || 1;
+
grep { $_->num_avail > 0 }
map {
my $part_svc = $_->part_svc;
$part_svc->{'Hash'}{'num_avail'} = #evil encapsulation-breaking
- $_->quantity - $self->num_cust_svc($_->svcpart);
+ $pkg_quantity * $_->quantity - $self->num_cust_svc($_->svcpart);
# more evil encapsulation breakage
if($part_svc->{'Hash'}{'num_avail'} > 0) {
my $self = shift;
my %opt = @_;
+ my $pkg_quantity = $self->quantity || 1;
+
#XXX some sort of sort order besides numeric by svcpart...
my @part_svc = sort { $a->svcpart <=> $b->svcpart } map {
my $pkg_svc = $_;
my $num_cust_svc = $self->num_cust_svc($part_svc->svcpart);
$part_svc->{'Hash'}{'num_cust_svc'} = $num_cust_svc; #more evil
$part_svc->{'Hash'}{'num_avail'} =
- max( 0, $pkg_svc->quantity - $num_cust_svc );
+ max( 0, $pkg_quantity * $pkg_svc->quantity - $num_cust_svc );
$part_svc->{'Hash'}{'cust_pkg_svc'} =
$num_cust_svc ? [ $self->cust_svc($part_svc->svcpart) ] : []
unless exists($opt{summarize_size}) && $opt{summarize_size} > 0
#end of subs in location_Mixin.pm now... unfortunately the POD doesn't mixin
+=item tax_locationnum
+
+Returns the foreign key to a L<FS::cust_location> object for calculating
+tax on this package, as determined by the C<tax-pkg_address> and
+C<tax-ship_address> configuration flags.
+
+=cut
+
+sub tax_locationnum {
+ my $self = shift;
+ my $conf = FS::Conf->new;
+ if ( $conf->exists('tax-pkg_address') ) {
+ return $self->locationnum;
+ }
+ elsif ( $conf->exists('tax-ship_address') ) {
+ return $self->cust_main->ship_locationnum;
+ }
+ else {
+ return $self->cust_main->bill_locationnum;
+ }
+}
+
+=item tax_location
+
+Returns the L<FS::cust_location> object for tax_locationnum.
+
+=cut
+
+sub tax_location {
+ my $self = shift;
+ FS::cust_location->by_key( $self->tax_locationnum )
+}
+
=item seconds_since TIMESTAMP
Returns the number of seconds all accounts (see L<FS::svc_acct>) in this
}
+=item tax_locationnum_sql
+
+Returns an SQL expression for the tax location for a package, based
+on the settings of 'tax-pkg_address' and 'tax-ship_address'.
+
+=cut
+
+sub tax_locationnum_sql {
+ my $conf = FS::Conf->new;
+ if ( $conf->exists('tax-pkg_address') ) {
+ 'cust_pkg.locationnum';
+ }
+ elsif ( $conf->exists('tax-ship_address') ) {
+ 'cust_main.ship_locationnum';
+ }
+ else {
+ 'cust_main.bill_locationnum';
+ }
+}
=item location_sql
my $or_empty_county = " OR ( ? = '' AND $table.${prefix}county IS NULL )";
my $or_empty_state = " OR ( ? = '' AND $table.${prefix}state IS NULL )";
+ my $text = (driver_name =~ /^mysql/i) ? 'char' : 'text';
+
# ( $table.${prefix}city = ? $or_empty_city $ornull )
"
- ( $table.district = ? OR ? = '' OR CAST(? AS text) IS NULL )
- AND ( $table.${prefix}city = ? OR ? = '' OR CAST(? AS text) IS NULL )
+ ( $table.district = ? OR ? = '' OR CAST(? AS $text) IS NULL )
+ AND ( $table.${prefix}city = ? OR ? = '' OR CAST(? AS $text) IS NULL )
AND ( $table.${prefix}county = ? $or_empty_county $ornull )
AND ( $table.${prefix}state = ? $or_empty_state $ornull )
AND $table.${prefix}country = ?