[Rt-commit] rt branch 5.0/cf-numeric-values created. rt-5.0.4-244-g01bbe3e5ca
BPS Git Server
git at git.bestpractical.com
Wed Oct 11 14:09:36 UTC 2023
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".
The branch, 5.0/cf-numeric-values has been created
at 01bbe3e5cae4daec9d974b15c892e174affb30fb (commit)
- Log -----------------------------------------------------------------
commit 01bbe3e5cae4daec9d974b15c892e174affb30fb
Author: sunnavy <sunnavy at bestpractical.com>
Date: Tue Feb 15 03:57:43 2022 +0800
Test searching/sorting cf values numerically
diff --git a/t/ticket/search_by_cf_numeric.t b/t/ticket/search_by_cf_numeric.t
new file mode 100644
index 0000000000..493847e307
--- /dev/null
+++ b/t/ticket/search_by_cf_numeric.t
@@ -0,0 +1,51 @@
+
+use strict;
+use warnings;
+
+use RT::Test nodata => 1, tests => undef;
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+my $cf = RT::Test->load_or_create_custom_field( Name => 'test_cf', Queue => $queue->id, Type => 'FreeformSingle' );
+ok( $cf->SetNumericValues(1) );
+my $cfid = $cf->id;
+
+my $cf2 = RT::Test->load_or_create_custom_field( Name => 'test_cf2', Queue => $queue->id, Type => 'FreeformSingle' );
+ok( $cf2->SetNumericValues(1) );
+my $cf2id = $cf2->id;
+
+my @tickets = RT::Test->create_tickets(
+ { Queue => $queue->Name },
+ { Subject => 'Big', "CustomField-$cfid" => 12, "CustomField-$cf2id" => 5 },
+ { Subject => 'Small', "CustomField-$cfid" => 3, "CustomField-$cf2id" => 10 },
+);
+
+my $tickets = RT::Tickets->new( RT->SystemUser );
+$tickets->FromSQL(q{Queue = 'General' AND CF.test_cf > 5 });
+is( $tickets->Count, 1, 'Found 1 ticket' );
+is( $tickets->First->id, $tickets[0]->id, 'Found the big ticket' );
+
+$tickets->FromSQL(q{Queue = 'General' AND CF.test_cf = 12 });
+is( $tickets->Count, 1, 'Found 1 ticket' );
+is( $tickets->First->id, $tickets[0]->id, 'Found the big ticket' );
+
+$tickets->FromSQL(q{Queue = 'General' AND CF.test_cf < 5});
+is( $tickets->Count, 1, 'Found 1 ticket' );
+is( $tickets->First->id, $tickets[1]->id, 'Found the small ticket' );
+
+$tickets->FromSQL(q{Queue = 'General' AND CF.test_cf = 3});
+is( $tickets->Count, 1, 'Found 1 ticket' );
+is( $tickets->First->id, $tickets[1]->id, 'Found the small ticket' );
+
+$tickets->FromSQL(q{Queue = 'General' AND CF.test_cf < CF.test_cf2 });
+is( $tickets->Count, 1, 'Found 1 ticket' );
+is( $tickets->First->id, $tickets[1]->id, 'Found the small ticket' );
+
+$tickets->FromSQL(q{Queue = 'General'});
+is( $tickets->Count, 2, 'Found 2 tickets' );
+$tickets->OrderByCols( { FIELD => 'CustomField.test_cf' } );
+is( $tickets->First->id, $tickets[1]->id, 'Small ticket first' );
+
+$tickets->OrderByCols( { FIELD => 'CustomField.test_cf', ORDER => 'DESC' } );
+is( $tickets->First->id, $tickets[0]->id, 'Big ticket first' );
+
+done_testing;
commit b8e143337cec0261274093836ce98d9ce8a7ce8b
Author: sunnavy <sunnavy at bestpractical.com>
Date: Sat Feb 12 00:32:46 2022 +0800
Support to search/sort cf values numerically
diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 79c1f1f5a5..0bd276d2f7 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -2359,6 +2359,61 @@ sub CleanupDefaultValues {
}
}
+=head2 SupportNumericValues
+
+Return true if this custom field supports numeric values, false otherwise.
+
+Currently it's supported for Freeform/Select/Combobox/Autocomplete.
+
+=cut
+
+sub SupportNumericValues {
+ my $self = shift;
+ return 0 unless $self->Id;
+ return $self->Type =~ /Freeform|Select|Combobox|Autocomplete/;
+}
+
+=head2 SetNumericValues BOOL
+
+Enable or Disable numeric values.
+
+=cut
+
+sub SetNumericValues {
+ my $self = shift;
+ my $value = shift || 0;
+ return 0 unless $self->Id;
+ return 1 if $self->NumericValues == $value;
+
+ my ( $ret, $msg ) = $self->SetAttribute( Name => 'NumericValues', Description => '', Content => $value );
+ if ($ret) {
+ if ($value) {
+ return ( $ret, $self->loc('Numeric values enabled') );
+ }
+ else {
+ return ( $ret, $self->loc('Numeric values disabled') );
+ }
+ }
+ else {
+ return ( $ret, $self->loc( "Can't set NumericValues to [_1]: [_2]", $value, $msg ) );
+ }
+}
+
+=head2 NumericValues
+
+Return true if this custom field has numeric values enabled, false otherwise.
+
+=cut
+
+sub NumericValues {
+ my $self = shift;
+ return 0 unless $self->Id && $self->SupportNumericValues;
+ if ( my $attr = $self->FirstAttribute('NumericValues') ) {
+ return $attr->Content;
+ }
+ return 0;
+}
+
=head2 id
Returns the current value of id.
diff --git a/lib/RT/SearchBuilder.pm b/lib/RT/SearchBuilder.pm
index adb1e4e953..1cfa03d661 100644
--- a/lib/RT/SearchBuilder.pm
+++ b/lib/RT/SearchBuilder.pm
@@ -169,8 +169,15 @@ sub _OrderByCF {
ENTRYAGGREGATOR => 'AND'
);
- return { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' },
- { %$row, ALIAS => $ocfvs, FIELD => 'Content' };
+ return { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' },
+ {
+ %$row,
+ ALIAS => $ocfvs,
+ FIELD => 'Content',
+ blessed $cf && $cf->NumericValues
+ ? ( FUNCTION => $self->_CastToDecimal('Content') )
+ : ()
+ };
}
sub OrderByCols {
@@ -529,9 +536,20 @@ sub _LimitCustomField {
my $fix_op = sub {
+ my %args = @_;
+
+ if ( $args{'FIELD'} eq 'Content'
+ && blessed $cf
+ && $cf->NumericValues
+ && ( !$args{QUOTEVALUE} || Scalar::Util::looks_like_number($args{'VALUE'}) ) )
+ {
+ $args{QUOTEVALUE} = 0;
+ $args{FUNCTION} = $self->_CastToDecimal( "$args{ALIAS}.$args{FIELD}" );
+ return %args;
+ }
+
return @_ unless RT->Config->Get('DatabaseType') eq 'Oracle';
- my %args = @_;
return %args unless $args{'FIELD'} eq 'LargeContent';
my $op = $args{'OPERATOR'};
@@ -827,17 +845,18 @@ sub _LimitCustomField {
);
} else {
# Otherwise, go looking at the Content
- $self->Limit(
+ $self->Limit( $fix_op->(
%args,
ALIAS => $ocfvalias,
FIELD => 'Content',
OPERATOR => $op,
VALUE => $value,
CASESENSITIVE => 0,
- );
+ ) );
}
- if (!$value_is_long and $op eq "=") {
+ if ( ( blessed($cf) and $cf->NumericValues ) or ( !$value_is_long and $op eq "=" ) ) {
+ # Skip LargeContent comparison for numeric values.
# Doesn't matter what LargeContent contains, as it cannot match
# the short value.
} elsif (!$value_is_long and $op =~ /^(!=|<>)$/) {
@@ -1150,6 +1169,23 @@ sub DistinctFieldValues {
return @values;
}
+sub _CastToDecimal {
+ my $self = shift;
+ my $field = shift or return;
+
+ my $db_type = RT->Config->Get('DatabaseType');
+ if ( $db_type eq 'Oracle' ) {
+ return "TO_NUMBER($field)";
+ }
+ elsif ( $db_type eq 'mysql' ) {
+ # mysql's CAST decimal requires precision specification, which we don't know.
+ return "($field+0)";
+ }
+ else {
+ return "CAST($field AS DECIMAL)";
+ }
+}
+
RT::Base->_ImportOverlays();
1;
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index a62b7de6a1..2d8b7c18a4 100644
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -1713,7 +1713,14 @@ sub OrderByCols {
ENTRYAGGREGATOR => 'AND'
);
push @res, { %$row, ALIAS => $CFvs, FIELD => 'SortOrder' },
- { %$row, ALIAS => $ocfvs, FIELD => 'Content' };
+ {
+ %$row,
+ ALIAS => $ocfvs,
+ FIELD => 'Content',
+ blessed $cf && $cf->NumericValues
+ ? ( FUNCTION => $self->_CastToDecimal('Content') )
+ : ()
+ };
}
else {
RT->Logger->warning("Couldn't load user custom field $cf_name");
@@ -3594,28 +3601,39 @@ sub _parser {
$value = "main.$value" if $class eq 'RT::Tickets' && $value =~ /^\w+$/;
if ( $class eq 'RT::ObjectCustomFieldValues' ) {
+ my $cast_to;
+ if ( $meta->[0] eq 'CUSTOMFIELD' ) {
+ my ($object, $field, $cf, $column) = $self->_CustomFieldDecipher( $subkey );
+ if ( $cf && $cf->NumericValues ) {
+ $cast_to = 'DECIMAL';
+ }
+ }
+
if ( RT->Config->Get('DatabaseType') eq 'Pg' ) {
- my $cast_to;
- if ($subkey) {
+ if ( !$cast_to ) {
+ if ($subkey) {
- # like Requestor.id
- if ( $subkey eq 'id' ) {
- $cast_to = 'INTEGER';
- }
- }
- elsif ( my $meta = $self->RecordClass->_ClassAccessible->{$key} ) {
- if ( $meta->{is_numeric} ) {
- $cast_to = 'INTEGER';
+ # like Requestor.id
+ if ( $subkey eq 'id' ) {
+ $cast_to = 'INTEGER';
+ }
}
- elsif ( $meta->{type} eq 'datetime' ) {
- $cast_to = 'TIMESTAMP';
+ elsif ( my $meta = $self->RecordClass->_ClassAccessible->{$key} ) {
+ if ( $meta->{is_numeric} ) {
+ $cast_to = 'INTEGER';
+ }
+ elsif ( $meta->{type} eq 'datetime' ) {
+ $cast_to = 'TIMESTAMP';
+ }
}
}
-
$value = "CAST($value AS $cast_to)" if $cast_to;
}
elsif ( RT->Config->Get('DatabaseType') eq 'Oracle' ) {
- if ($subkey) {
+ if ( $cast_to && $cast_to eq 'DECIMAL' ) {
+ $value = "TO_NUMBER($value)";
+ }
+ elsif ($subkey) {
# like Requestor.id
if ( $subkey eq 'id' ) {
diff --git a/share/html/Admin/CustomFields/Modify.html b/share/html/Admin/CustomFields/Modify.html
index beee4675a0..5db86e56f3 100644
--- a/share/html/Admin/CustomFields/Modify.html
+++ b/share/html/Admin/CustomFields/Modify.html
@@ -206,6 +206,21 @@ jQuery( function() {
</div>
</&>
+% if ( $CustomFieldObj->Id && $CustomFieldObj->SupportNumericValues ) {
+<div class="form-row">
+ <div class="label col-3"></div>
+ <div class="value col-9">
+ <input type="hidden" class="hidden" name="SetNumericValues" value="1" />
+ <div class="custom-control custom-checkbox">
+ <input type="checkbox" class="custom-control-input" id="NumericValues" name="NumericValues" value="1" <% $NumericValuesChecked |n %> />
+ <label class="custom-control-label" for="NumericValues">
+ <&|/l&>Values are numeric</&>
+ </label>
+ </div>
+ </div>
+</div>
+% }
+
% $m->callback(CallbackName => 'BeforeEnabled', CustomField => $CustomFieldObj, CFvalidations => \@CFvalidations);
<&| /Elements/LabeledValue, Label => "" &>
@@ -458,6 +473,10 @@ if ( $ARGS{'Update'} && $id ne 'new' ) {
}
}
+ if ( $CustomFieldObj->SupportNumericValues ) {
+ my ( $ret, $msg ) = $CustomFieldObj->SetNumericValues( $NumericValues ? 1 : 0 );
+ push @results, $msg;
+ }
}
# for old tests only, users should use ajax calls and this should never be called in real usage.
@@ -516,6 +535,8 @@ my $UniqueValuesChecked = qq[checked="checked"];
$UniqueValuesChecked = '' if !$CustomFieldObj->UniqueValues;
my @CFvalidations = RT->Config->Get('CustomFieldValuesValidations');
+my $NumericValuesChecked = '';
+$NumericValuesChecked = qq[checked="checked"] if $CustomFieldObj->NumericValues;
$m->callback(CallbackName => 'ValidationPatterns', Values => \@CFvalidations);
@@ -533,6 +554,7 @@ $SetEnabled => undef
$Enabled => 0
$SetUniqueValues => undef
$UniqueValues => 0
+$NumericValues => 0
$ValuesClass => 'RT::CustomFieldValues'
$CanonicalizeClass => undef
$RenderType => undef
-----------------------------------------------------------------------
hooks/post-receive
--
rt
More information about the rt-commit
mailing list