sqlparser-devexpress 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -1
- package/src/core/converter.js +42 -16
- package/src/core/parser.js +6 -4
- package/src/core/tokenizer.js +5 -5
- package/src/debug.js +40 -0
- package/src/index.js +3 -27
- package/tests/parser.test.js +38 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sqlparser-devexpress",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.2",
|
|
4
4
|
"main": "src/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"require": "./src/index.js"
|
|
12
12
|
},
|
|
13
13
|
"types": "src/@types/default.d.ts",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/RohitM-IN/SQLParser.git"
|
|
17
|
+
},
|
|
14
18
|
"keywords": [
|
|
15
19
|
"sql",
|
|
16
20
|
"parser",
|
package/src/core/converter.js
CHANGED
|
@@ -31,7 +31,7 @@ function DevExpressConverter() {
|
|
|
31
31
|
let result = processAstNode(ast);
|
|
32
32
|
|
|
33
33
|
// Handle special cases for short circuit
|
|
34
|
-
if(result === true || result === false || result === null) return [];
|
|
34
|
+
if (result === true || result === false || result === null) return [];
|
|
35
35
|
|
|
36
36
|
return processAstNode(ast);
|
|
37
37
|
}
|
|
@@ -136,13 +136,21 @@ function DevExpressConverter() {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
// Handle "IN" condition, including comma-separated values
|
|
139
|
-
if (operator === "IN") {
|
|
140
|
-
return handleInOperator(ast);
|
|
139
|
+
if (operator === "IN" || operator === "NOT IN") {
|
|
140
|
+
return handleInOperator(ast, operator);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
const left = ast.left !== undefined ? processAstNode(ast.left) : convertValue(ast.field);
|
|
144
144
|
const right = ast.right !== undefined ? processAstNode(ast.right) : convertValue(ast.value);
|
|
145
|
-
const
|
|
145
|
+
const operatorToken = ast.operator.toLowerCase();
|
|
146
|
+
|
|
147
|
+
let comparison = [left, operatorToken, right];
|
|
148
|
+
|
|
149
|
+
if (isFunctionNullCheck(ast.left, true)) {
|
|
150
|
+
comparison = [[left, operatorToken, right], 'or', [left, operatorToken, null]];
|
|
151
|
+
} else if (isFunctionNullCheck(ast.right, true)) {
|
|
152
|
+
comparison = [[left, operatorToken, right], 'or', [right, operatorToken, null]];
|
|
153
|
+
}
|
|
146
154
|
|
|
147
155
|
// Apply short-circuit evaluation if enabled
|
|
148
156
|
if (EnableShortCircuit) {
|
|
@@ -179,7 +187,7 @@ function DevExpressConverter() {
|
|
|
179
187
|
* @param {Object} ast - The comparison operator AST node.
|
|
180
188
|
* @returns {Array} DevExpress format filter.
|
|
181
189
|
*/
|
|
182
|
-
function handleInOperator(ast) {
|
|
190
|
+
function handleInOperator(ast, operator) {
|
|
183
191
|
let resolvedValue = convertValue(ast.value);
|
|
184
192
|
|
|
185
193
|
// Handle comma-separated values in a string
|
|
@@ -190,19 +198,18 @@ function DevExpressConverter() {
|
|
|
190
198
|
} else {
|
|
191
199
|
resolvedValue = firstValue;
|
|
192
200
|
}
|
|
201
|
+
} else if (typeof resolvedValue === 'string' && resolvedValue.includes(',')) {
|
|
202
|
+
resolvedValue = resolvedValue.split(',').map(v => v.trim());
|
|
193
203
|
}
|
|
194
204
|
|
|
195
|
-
|
|
205
|
+
let operatorToken = operator === "IN" ? '=' : operator === "NOT IN" ? '!=' : operator;
|
|
206
|
+
let joinOperatorToken = operator === "IN" ? 'or' : operator === "NOT IN" ? 'and' : operator;
|
|
196
207
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
[],
|
|
200
|
-
resolvedValue.map((i) => [[ast.field, '=', i], 'or']),
|
|
201
|
-
)
|
|
202
|
-
.slice(0, -1)
|
|
208
|
+
if (Array.isArray(resolvedValue) && resolvedValue.length) {
|
|
209
|
+
return resolvedValue.flatMap(i => [[ast.field, operatorToken, i], joinOperatorToken]).slice(0, -1);
|
|
203
210
|
}
|
|
204
211
|
|
|
205
|
-
return [ast.field,
|
|
212
|
+
return [ast.field, operatorToken, resolvedValue];
|
|
206
213
|
}
|
|
207
214
|
|
|
208
215
|
/**
|
|
@@ -225,7 +232,7 @@ function DevExpressConverter() {
|
|
|
225
232
|
}
|
|
226
233
|
|
|
227
234
|
// Special handling for ISNULL function
|
|
228
|
-
if (val
|
|
235
|
+
if (isFunctionNullCheck(val)) {
|
|
229
236
|
return convertValue(val.args[0]);
|
|
230
237
|
}
|
|
231
238
|
|
|
@@ -260,6 +267,18 @@ function DevExpressConverter() {
|
|
|
260
267
|
return node?.type === "function" && node.name === "ISNULL" && valueNode?.type === "value";
|
|
261
268
|
}
|
|
262
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Checks if a node is a ISNULL function without value
|
|
272
|
+
* @param {Object} node
|
|
273
|
+
* @returns {boolean} True if this is an ISNULL check.
|
|
274
|
+
*/
|
|
275
|
+
function isFunctionNullCheck(node, isPlaceholderCheck = false) {
|
|
276
|
+
const isValidFunction = node?.type === "function" && node?.name === "ISNULL" && node?.args?.length >= 2;
|
|
277
|
+
|
|
278
|
+
return isPlaceholderCheck ? isValidFunction && node?.args[0]?.value?.type !== "placeholder" : isValidFunction;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
263
282
|
/**
|
|
264
283
|
* Determines whether the logical tree should be flattened.
|
|
265
284
|
* This is based on the parent operator and the current operator.
|
|
@@ -329,7 +348,14 @@ function DevExpressConverter() {
|
|
|
329
348
|
* @returns {boolean|null} The result of the evaluation or null if not evaluable.
|
|
330
349
|
*/
|
|
331
350
|
function evaluateExpression(left, operator, right) {
|
|
332
|
-
if (
|
|
351
|
+
if ((left !== null && isNaN(left)) || (right !== null && isNaN(right))) return null;
|
|
352
|
+
|
|
353
|
+
if (left === null || right === null) {
|
|
354
|
+
if (operator === '=' || operator === '==') return left === right;
|
|
355
|
+
if (operator === '<>' || operator === '!=') return left !== right;
|
|
356
|
+
return null; // Any comparison with null should return null
|
|
357
|
+
}
|
|
358
|
+
|
|
333
359
|
switch (operator) {
|
|
334
360
|
case '=': case '==': return left === right;
|
|
335
361
|
case '<>': case '!=': return left !== right;
|
|
@@ -337,7 +363,7 @@ function DevExpressConverter() {
|
|
|
337
363
|
case '>=': return left >= right;
|
|
338
364
|
case '<': return left < right;
|
|
339
365
|
case '<=': return left <= right;
|
|
340
|
-
default: return
|
|
366
|
+
default: return null; // Invalid operator
|
|
341
367
|
}
|
|
342
368
|
}
|
|
343
369
|
|
package/src/core/parser.js
CHANGED
|
@@ -25,9 +25,9 @@ export function parse(input, variables = []) {
|
|
|
25
25
|
// const tokens = [];
|
|
26
26
|
// let tempToken = currentToken;
|
|
27
27
|
// while (tempToken) {
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
28
|
+
// tokens.push(tempToken);
|
|
29
|
+
// tempToken = tokenizer.peekNextToken();
|
|
30
|
+
// tokenizer.nextToken();
|
|
31
31
|
// }
|
|
32
32
|
|
|
33
33
|
// console.log("Tokens:", tokens);
|
|
@@ -208,8 +208,10 @@ export function parse(input, variables = []) {
|
|
|
208
208
|
return { type: "placeholder", value: val };
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
+
operatorToken = operatorToken.toUpperCase();
|
|
212
|
+
|
|
211
213
|
// Handle IN operator which requires a list of values
|
|
212
|
-
if (operatorToken && operatorToken
|
|
214
|
+
if (operatorToken && (operatorToken === "IN" || operatorToken === "NOT IN")) return parseInList(token);
|
|
213
215
|
|
|
214
216
|
throw new Error(`Unexpected value: ${token.value}`);
|
|
215
217
|
}
|
package/src/core/tokenizer.js
CHANGED
|
@@ -6,7 +6,7 @@ const tokenPatterns = {
|
|
|
6
6
|
number: "\\d+", // Matches numerical values
|
|
7
7
|
placeholder: "'?\\{[^}]+\\}'?", // Matches placeholders like {variable} or '{variable}'
|
|
8
8
|
string: "'(?:''|[^'])*'", // Matches strings, allowing for escaped single quotes ('')
|
|
9
|
-
operator: "=>|<=|!=|>=|=|<>|>|<|\\bAND\\b|\\bOR\\b|\\bBETWEEN\\b|\\bIN\\b|\\bLIKE\\b|\\bIS NOT\\b|\\bNOT LIKE\\b|\\bIS\\b", // Matches SQL operators and logical keywords
|
|
9
|
+
operator: "=>|<=|!=|>=|=|<>|>|<|\\bAND\\b|\\bOR\\b|\\bBETWEEN\\b|\\bIN\\b|\\bNOT IN\\b|\\bLIKE\\b|\\bIS NOT\\b|\\bNOT LIKE\\b|\\bIS\\b", // Matches SQL operators and logical keywords
|
|
10
10
|
identifier: "[\\w.]+", // Matches identifiers, including table.column format
|
|
11
11
|
paren: "[()]", // Matches parentheses
|
|
12
12
|
comma: "," // Matches commas
|
|
@@ -49,14 +49,14 @@ class Tokenizer {
|
|
|
49
49
|
|
|
50
50
|
if (type === "operator") {
|
|
51
51
|
const lowerValue = value.toLowerCase();
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
if (lowerValue === "is") {
|
|
54
54
|
value = "=";
|
|
55
55
|
} else if (lowerValue === "is not") {
|
|
56
56
|
value = "!=";
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
return { type, value };
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -66,7 +66,7 @@ class Tokenizer {
|
|
|
66
66
|
|
|
67
67
|
peekNextToken() {
|
|
68
68
|
if (this.index >= this.input.length) return null;
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
const savedIndex = this.index; // Save current index
|
|
71
71
|
try {
|
|
72
72
|
return this.nextToken(); // Get next token
|
|
@@ -78,7 +78,7 @@ class Tokenizer {
|
|
|
78
78
|
reset() {
|
|
79
79
|
this.index = 0; // Reset index to the beginning of the input
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export { Tokenizer };
|
package/src/debug.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// // Example usage
|
|
2
|
+
// // const devExpressFilter = parseFilterString("((ISNULL({0}, 0) = 0 AND CompanyID = {1}) OR CompanyID IS NULL) OR BranchID = {0} | [LeadDocument.BranchID] | [LeadDocument.CompanyID]", sampleResultObject);
|
|
3
|
+
// // const devExpressFilter = parseFilterString("FromDate <= '{TransferOutwardDocument.DocDate}' ", sampleResultObject, "TransferOutwardDocument", "789");
|
|
4
|
+
// // const devExpressFilter = parseFilterString("(RS2ID in ({SaleOrderStatusStmtGlobalRpt.StateID}) Or ({SaleOrderStatusStmtGlobalRpt.StateID} =0)) And (RS3ID in (0,{SaleOrderStatusStmtGlobalRpt.RegionID}) Or {SaleOrderStatusStmtGlobalRpt.RegionID} =0 )", sampleResultObject,);
|
|
5
|
+
|
|
6
|
+
// import { convertToDevExpressFormat } from "./core/converter.js";
|
|
7
|
+
// import { parse } from "./core/parser.js";
|
|
8
|
+
// import { sanitizeQuery } from "./core/sanitizer.js";
|
|
9
|
+
// import { convertAstToDevextreme, convertSQLToAst } from "./index.js";
|
|
10
|
+
|
|
11
|
+
// const sampleData = {
|
|
12
|
+
// 'LeadStatementGlobalRpt.StateID': null,
|
|
13
|
+
// 'LeadStatementGlobalRpt.RegionID': null,
|
|
14
|
+
// 'ServiceOrderDocument.SourceID': 2
|
|
15
|
+
// }
|
|
16
|
+
|
|
17
|
+
// export function parseFilterString(filterString, sampleData = null) {
|
|
18
|
+
// if (filterString.toUpperCase().startsWith("SELECT")) return null; // Skip full SQL queries
|
|
19
|
+
|
|
20
|
+
// let { sanitizedSQL, extractedVariables } = sanitizeQuery(filterString);
|
|
21
|
+
// console.log("Sanitized SQL:", sanitizedSQL, "\n");
|
|
22
|
+
|
|
23
|
+
// const parsedResult = parse(sanitizedSQL, extractedVariables);
|
|
24
|
+
// extractedVariables = parsedResult.variables;
|
|
25
|
+
// console.log("Extracted Variables:", JSON.stringify(extractedVariables, null, 2), "\n");
|
|
26
|
+
|
|
27
|
+
// const astTree = parsedResult.ast;
|
|
28
|
+
// console.log("AST Tree:", JSON.stringify(astTree, null, 2), "\n");
|
|
29
|
+
|
|
30
|
+
// return convertToDevExpressFormat({ ast: astTree, variables: extractedVariables, resultObject: sampleData });
|
|
31
|
+
// }
|
|
32
|
+
|
|
33
|
+
// // const devexpress = parseFilterString("(RS2ID in ({LeadStatementGlobalRpt.StateID}) Or ({LeadStatementGlobalRpt.StateID} =0)) And (RS3ID in (0,{LeadStatementGlobalRpt.RegionID}) Or {LeadStatementGlobalRpt.RegionID} =0 )", sampleData);
|
|
34
|
+
// const devexpress = parseFilterString("{LeadStatementGlobalRpt.RegionID} =0", sampleData);
|
|
35
|
+
// console.log("DevExpress Filter:", JSON.stringify(devexpress, null, 2));
|
|
36
|
+
|
|
37
|
+
// // const devExpressFilter = convertSQLToAst("(RS2ID in ({LeadStatementGlobalRpt.StateID}) Or ({LeadStatementGlobalRpt.StateID} =0)) And (RS3ID in (0,{LeadStatementGlobalRpt.RegionID}) Or {LeadStatementGlobalRpt.RegionID} =0 ) ");
|
|
38
|
+
// // const devExpressFilterresult = convertAstToDevextreme(devExpressFilter.ast, devExpressFilter.variables, sampleData);
|
|
39
|
+
// // console.log("DevExpress Filter:", JSON.stringify(devExpressFilter, null, 2));
|
|
40
|
+
// // console.log("DevExpress Result:", JSON.stringify(devExpressFilterresult, null, 2));
|
package/src/index.js
CHANGED
|
@@ -5,12 +5,12 @@ import { sanitizeQuery } from "./core/sanitizer.js";
|
|
|
5
5
|
|
|
6
6
|
export function convertSQLToAst(filterString, enableConsoleLogs = false) {
|
|
7
7
|
let { sanitizedSQL, extractedVariables } = sanitizeQuery(filterString);
|
|
8
|
-
enableConsoleLogs && console.log("Sanitized SQL:", sanitizedSQL, "\n");
|
|
8
|
+
!!enableConsoleLogs && console.log("Sanitized SQL:", sanitizedSQL, "\n");
|
|
9
9
|
|
|
10
10
|
const parsedResult = parse(sanitizedSQL, extractedVariables);
|
|
11
11
|
|
|
12
|
-
enableConsoleLogs && console.log("Extracted Variables:", JSON.stringify(parsedResult.variables, null, 2), "\n");
|
|
13
|
-
enableConsoleLogs && console.log("AST Tree:", JSON.stringify(parsedResult.ast, null, 2), "\n");
|
|
12
|
+
!!enableConsoleLogs && console.log("Extracted Variables:", JSON.stringify(parsedResult.variables, null, 2), "\n");
|
|
13
|
+
!!enableConsoleLogs && console.log("AST Tree:", JSON.stringify(parsedResult.ast, null, 2), "\n");
|
|
14
14
|
|
|
15
15
|
return parsedResult;
|
|
16
16
|
}
|
|
@@ -19,29 +19,5 @@ export function convertAstToDevextreme(ast, variables, state) {
|
|
|
19
19
|
return convertToDevExpressFormat({ ast, variables, resultObject: state })
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
// export function parseFilterString(filterString, sampleData = null) {
|
|
23
|
-
// if (filterString.toUpperCase().startsWith("SELECT")) return null; // Skip full SQL queries
|
|
24
22
|
|
|
25
|
-
// let { sanitizedSQL, extractedVariables } = sanitizeQuery(filterString);
|
|
26
|
-
// console.log("Sanitized SQL:", sanitizedSQL, "\n");
|
|
27
|
-
|
|
28
|
-
// const parsedResult = parse(sanitizedSQL, extractedVariables);
|
|
29
|
-
// extractedVariables = parsedResult.variables;
|
|
30
|
-
// console.log("Extracted Variables:", JSON.stringify(extractedVariables, null, 2), "\n");
|
|
31
|
-
|
|
32
|
-
// const astTree = parsedResult.ast;
|
|
33
|
-
// console.log("AST Tree:", JSON.stringify(astTree, null, 2), "\n");
|
|
34
|
-
|
|
35
|
-
// return convertToDevExpressFormat({ ast: astTree, variables: extractedVariables, resultObject: sampleData });
|
|
36
|
-
// }
|
|
37
|
-
|
|
38
|
-
// Example usage
|
|
39
|
-
// const devExpressFilter = parseFilterString("((ISNULL({0}, 0) = 0 AND CompanyID = {1}) OR CompanyID IS NULL) OR BranchID = {0} | [LeadDocument.BranchID] | [LeadDocument.CompanyID]", sampleResultObject);
|
|
40
|
-
// const devExpressFilter = parseFilterString("FromDate <= '{TransferOutwardDocument.DocDate}' ", sampleResultObject, "TransferOutwardDocument", "789");
|
|
41
|
-
// const devExpressFilter = parseFilterString("(RS2ID in ({SaleOrderStatusStmtGlobalRpt.StateID}) Or ({SaleOrderStatusStmtGlobalRpt.StateID} =0)) And (RS3ID in (0,{SaleOrderStatusStmtGlobalRpt.RegionID}) Or {SaleOrderStatusStmtGlobalRpt.RegionID} =0 )", sampleResultObject,);
|
|
42
|
-
|
|
43
|
-
// const devExpressFilter = convertSQLToAst("CompanyID = CompanyID2 = {AccountingRule.CompanyID}");
|
|
44
|
-
// const devExpressFilterresult = convertAstToDevextreme(devExpressFilter.ast, devExpressFilter.variables,{'ServiceOrderDocument.SourceID': 2});
|
|
45
|
-
// console.log("DevExpress Filter:", JSON.stringify(devExpressFilter, null, 2));
|
|
46
|
-
// console.log("DevExpress Result:", JSON.stringify(devExpressFilterresult, null, 2));
|
|
47
23
|
|
package/tests/parser.test.js
CHANGED
|
@@ -37,7 +37,7 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
37
37
|
{
|
|
38
38
|
input: "ID IN ({WorkOrderLine.ApplicableUoms}) AND (CompanyID = {WorkOrderDocument.CompanyID} OR {WorkOrderDocument.CompanyID} = 0)",
|
|
39
39
|
expected: [
|
|
40
|
-
[["ID", "=", "UOM1"]
|
|
40
|
+
[["ID", "=", "UOM1"], 'or', ["ID", "=", "UOM2"], 'or', ["ID", "=", "UOM3"]],
|
|
41
41
|
"and",
|
|
42
42
|
// [
|
|
43
43
|
["CompanyID", "=", 42],
|
|
@@ -145,12 +145,44 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
145
145
|
expected: [
|
|
146
146
|
["SourceID", "=", 2],
|
|
147
147
|
"or",
|
|
148
|
-
["SourceID", "=",
|
|
148
|
+
["SourceID", "=", null],
|
|
149
|
+
"or",
|
|
150
|
+
["SourceID", "=", 0],
|
|
151
|
+
"or",
|
|
152
|
+
["SourceID", "=", null]
|
|
149
153
|
]
|
|
150
154
|
},
|
|
151
155
|
{
|
|
152
156
|
input: "CompanyID = CompanyID2 = {AccountingRule.CompanyID}",
|
|
153
157
|
expected: "Error: Invalid comparison: CompanyID = CompanyID2",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
input: "(CompanyID = {LeadDocument.CompanyID} OR ISNULL(CompanyID,0) = 0) AND (ISNULL(IsSubdealer,0) = {LeadDocument.AllowSubDealer})",
|
|
161
|
+
expected: [
|
|
162
|
+
[
|
|
163
|
+
["CompanyID", "=", 7],
|
|
164
|
+
"or",
|
|
165
|
+
[
|
|
166
|
+
["CompanyID", "=", 0],
|
|
167
|
+
"or",
|
|
168
|
+
["CompanyID", "=", null]
|
|
169
|
+
]
|
|
170
|
+
],
|
|
171
|
+
"and",
|
|
172
|
+
[
|
|
173
|
+
["IsSubdealer", "=", true],
|
|
174
|
+
"or",
|
|
175
|
+
["IsSubdealer", "=", null]
|
|
176
|
+
]
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
input: 'AddressType NOT IN (2, 4)',
|
|
181
|
+
expected: [
|
|
182
|
+
["AddressType", "!=", 2],
|
|
183
|
+
"and",
|
|
184
|
+
["AddressType", "!=", 4]
|
|
185
|
+
]
|
|
154
186
|
}
|
|
155
187
|
];
|
|
156
188
|
|
|
@@ -165,7 +197,7 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
165
197
|
try {
|
|
166
198
|
astwithVariables = convertSQLToAst(input);
|
|
167
199
|
} catch (error) {
|
|
168
|
-
expect(error.message).toEqual(expected.replace("Error: ",""));
|
|
200
|
+
expect(error.message).toEqual(expected.replace("Error: ", ""));
|
|
169
201
|
return;
|
|
170
202
|
}
|
|
171
203
|
|
|
@@ -186,7 +218,7 @@ describe("Parser SQL to dx Filter Builder", () => {
|
|
|
186
218
|
|
|
187
219
|
expect(result).toEqual(expected);
|
|
188
220
|
});
|
|
189
|
-
|
|
221
|
+
});
|
|
190
222
|
});
|
|
191
223
|
|
|
192
224
|
|
|
@@ -213,5 +245,6 @@ const sampleData = {
|
|
|
213
245
|
"TransferOutwardDocument.CompanyID": 7,
|
|
214
246
|
"LeadDocument.BranchID": 42,
|
|
215
247
|
"LeadDocument.CompanyID": 7,
|
|
216
|
-
"ServiceOrderDocument.SourceID": 2
|
|
248
|
+
"ServiceOrderDocument.SourceID": 2,
|
|
249
|
+
"LeadDocument.AllowSubDealer": true
|
|
217
250
|
};
|