diff --git a/src/Apps/W1/Quality Management/Test Library/src/QltyInspectionUtility.Codeunit.al b/src/Apps/W1/Quality Management/Test Library/src/QltyInspectionUtility.Codeunit.al
index 14c1d2a042..0d5dbc6542 100644
--- a/src/Apps/W1/Quality Management/Test Library/src/QltyInspectionUtility.Codeunit.al
+++ b/src/Apps/W1/Quality Management/Test Library/src/QltyInspectionUtility.Codeunit.al
@@ -894,6 +894,20 @@ codeunit 139940 "Qlty. Inspection Utility"
exit(QltyResultEvaluation.CheckIfValueIsString(ValueToCheck, AcceptableValue, QltyCaseSensitivity));
end;
+ ///
+ /// Wrapper for internal procedure CheckIfValueIsInPredefinedList from Qlty. Result Evaluation codeunit.
+ ///
+ /// The value to check.
+ /// The acceptable value condition: a comma or pipe separated list of literal values. A blank value means no condition, and the default "anything except empty" tokens are also accepted.
+ /// The case sensitivity option.
+ /// True if the value matches one of the listed literal values, false otherwise.
+ internal procedure CheckIfValueIsInPredefinedList(ValueToCheck: Text; AcceptableValue: Text; QltyCaseSensitivity: Enum "Qlty. Case Sensitivity"): Boolean
+ var
+ QltyResultEvaluation: Codeunit "Qlty. Result Evaluation";
+ begin
+ exit(QltyResultEvaluation.CheckIfValueIsInPredefinedList(ValueToCheck, AcceptableValue, QltyCaseSensitivity));
+ end;
+
///
/// Wrapper for internal procedure ValidateQltyInspectionLine from Qlty. Result Evaluation codeunit.
/// Validates an inspection line using the single-parameter internal signature.
diff --git a/src/Apps/W1/Quality Management/app/src/Configuration/Result/QltyResultEvaluation.Codeunit.al b/src/Apps/W1/Quality Management/app/src/Configuration/Result/QltyResultEvaluation.Codeunit.al
index c1b2942c55..3d9a296c17 100644
--- a/src/Apps/W1/Quality Management/app/src/Configuration/Result/QltyResultEvaluation.Codeunit.al
+++ b/src/Apps/W1/Quality Management/app/src/Configuration/Result/QltyResultEvaluation.Codeunit.al
@@ -104,7 +104,9 @@ codeunit 20410 "Qlty. Result Evaluation"
LoopConditionMet := QltyBooleanParsing.GetBooleanFor(TestValue) = QltyBooleanParsing.GetBooleanFor(Condition)
else
LoopConditionMet := CheckIfValueIsString(TestValue, Condition, QltyCaseSensitivity);
- QltyTestValueType::"Value Type Text", QltyTestValueType::"Value Type Option", QltyTestValueType::"Value Type Table Lookup", QltyTestValueType::"Value Type Text Expression":
+ QltyTestValueType::"Value Type Option", QltyTestValueType::"Value Type Table Lookup":
+ LoopConditionMet := CheckIfValueIsInPredefinedList(TestValue, Condition, QltyCaseSensitivity);
+ QltyTestValueType::"Value Type Text", QltyTestValueType::"Value Type Text Expression":
LoopConditionMet := CheckIfValueIsString(TestValue, Condition, QltyCaseSensitivity);
QltyTestValueType::"Value Type Date":
begin
@@ -700,6 +702,36 @@ codeunit 20410 "Qlty. Result Evaluation"
exit(not TempTestStringValueQltyTest.IsEmpty());
end;
+ internal procedure CheckIfValueIsInPredefinedList(ValueToCheck: Text; AcceptableValue: Text; QltyCaseSensitivity: Enum "Qlty. Case Sensitivity"): Boolean
+ var
+ SingleAcceptableValue: Text;
+ begin
+ // Option and Table Lookup tests use a fixed set of predefined values. Their conditions are a
+ // comma or pipe separated list of literal values that may contain special filter characters such
+ // as parentheses, so they must be compared literally instead of being interpreted as a filter.
+ if IsAnythingExceptEmptyCondition(AcceptableValue) then
+ exit(ValueToCheck <> '');
+
+ if IsBlankOrEmptyCondition(AcceptableValue) then
+ exit(true);
+
+ if QltyCaseSensitivity = QltyCaseSensitivity::Insensitive then begin
+ ValueToCheck := ValueToCheck.ToLower();
+ AcceptableValue := AcceptableValue.ToLower();
+ end;
+
+ AcceptableValue := AcceptableValue.Replace('|', ',');
+ foreach SingleAcceptableValue in AcceptableValue.Split(',') do begin
+ SingleAcceptableValue := SingleAcceptableValue.Trim();
+ if SingleAcceptableValue = '' then
+ continue;
+ if ValueToCheck = SingleAcceptableValue then
+ exit(true);
+ end;
+
+ exit(false);
+ end;
+
///
/// OnBeforeEvaluateResult gives an opportunity to change how a result is evaluated.
///
diff --git a/src/Apps/W1/Quality Management/test/src/QltyTestsResultEval.Codeunit.al b/src/Apps/W1/Quality Management/test/src/QltyTestsResultEval.Codeunit.al
index 05f7d8a17a..05b6b27056 100644
--- a/src/Apps/W1/Quality Management/test/src/QltyTestsResultEval.Codeunit.al
+++ b/src/Apps/W1/Quality Management/test/src/QltyTestsResultEval.Codeunit.al
@@ -172,6 +172,42 @@ codeunit 139963 "Qlty. Tests - Result Eval."
LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsString('wildCardSearch', 'wild*'), 'String wildcard 2');
end;
+ [Test]
+ procedure ValueOptionWithSpecialCharacters()
+ var
+ QltyInspectionUtility: Codeunit "Qlty. Inspection Utility";
+ CaseOption: Enum "Qlty. Case Sensitivity";
+ begin
+ // [SCENARIO 639903] Option/Table Lookup conditions are matched literally, so predefined values containing
+ // special filter characters such as parentheses do not raise a filter error.
+
+ // [GIVEN] An option test with values that contain parentheses 'Test(1),Test(2)'
+ // [WHEN] Evaluating a selected option value against a comma separated pass condition
+ // [THEN] The value matches its literal option without interpreting '(' as a filter operator
+ LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsInPredefinedList('Test(1)', 'Test(1),Test(2)', CaseOption::Sensitive), 'Option special chars comma list first');
+ LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsInPredefinedList('Test(2)', 'Test(1),Test(2)', CaseOption::Sensitive), 'Option special chars comma list second');
+ LibraryAssert.IsFalse(QltyInspectionUtility.CheckIfValueIsInPredefinedList('Test(3)', 'Test(1),Test(2)', CaseOption::Sensitive), 'Option special chars not in list');
+
+ // [THEN] A trailing space after the condition list is tolerated
+ LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsInPredefinedList('Test(2)', 'Test(1),Test(2) ', CaseOption::Sensitive), 'Option special chars trailing space');
+
+ // [THEN] Pipe separated lists are also supported
+ LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsInPredefinedList('C', 'C|D', CaseOption::Sensitive), 'Option pipe list match');
+ LibraryAssert.IsFalse(QltyInspectionUtility.CheckIfValueIsInPredefinedList('A', 'C|D', CaseOption::Sensitive), 'Option pipe list no match');
+
+ // [THEN] Empty value does not match a non-empty condition and an empty condition matches anything
+ LibraryAssert.IsFalse(QltyInspectionUtility.CheckIfValueIsInPredefinedList('', 'C|D', CaseOption::Sensitive), 'Option empty value');
+ LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsInPredefinedList('A', '', CaseOption::Sensitive), 'Option empty condition');
+
+ // [THEN] Empty tokens from a malformed list (trailing or doubled delimiters) do not match an empty value
+ LibraryAssert.IsFalse(QltyInspectionUtility.CheckIfValueIsInPredefinedList('', 'A,', CaseOption::Sensitive), 'Option empty token trailing delimiter');
+ LibraryAssert.IsFalse(QltyInspectionUtility.CheckIfValueIsInPredefinedList('', 'A||B', CaseOption::Sensitive), 'Option empty token doubled delimiter');
+ LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsInPredefinedList('B', 'A||B', CaseOption::Sensitive), 'Option doubled delimiter still matches real value');
+
+ // [THEN] Case insensitive comparison matches values that differ only by case
+ LibraryAssert.IsTrue(QltyInspectionUtility.CheckIfValueIsInPredefinedList('test(1)', 'Test(1),Test(2)', CaseOption::Insensitive), 'Option special chars case insensitive');
+ end;
+
[TryFunction]
procedure Try_TestValueDateIntentionallyBad()
var