Merge pull request #1609 from xtuple/4_5_x
[xtuple] / test / database / joins.js
1 /*jshint trailing:true, white:true, indent:2, strict:true, curly:true,
2   immed:true, eqeqeq:true, forin:true, latedef:true,
3   newcap:true, noarg:true, undef:true */
4 /*global XT:true, describe:true, it:true, require:true, __dirname:true, before:true */
5
6 var _ = require("underscore"),
7   assert = require('chai').assert,
8   datasource = require('../../node-datasource/lib/ext/datasource').dataSource,
9   path = require('path');
10
11 (function () {
12   "use strict";
13   describe('The database', function () {
14     this.timeout(10 * 1000);
15
16     var loginData = require(path.join(__dirname, "../lib/login_data.js")).data,
17       datasource = require('../../../xtuple/node-datasource/lib/ext/datasource').dataSource,
18       config = require(path.join(__dirname, "../../node-datasource/config.js")),
19       creds = config.databaseServer,
20       databaseName = loginData.org,
21       isCommercial = false; // this is awkward #refactor
22
23
24     before(function (done) {
25       var sql = "select metric_value from public.metric where metric_name = 'MultiWhs';";
26       creds.database = databaseName;
27       datasource.query(sql, creds, function (err, res) {
28         isCommercial = res.rows[0].metric_value === 't';
29         done();
30       });
31
32     });
33
34     // these tests are pretty fragile to the exact numbers in the database, but have been invaluable to
35     // make sure I'm not breaking anything in the fetch refactor
36     it('should execute a query with a join filter', function (done) {
37       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"orderBy":[{"attribute":"lastName"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"owner.username","operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"foo"}$$);';
38
39       datasource.query(sql, creds, function (err, res) {
40         var results;
41         assert.isNull(err);
42         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
43         results = JSON.parse(res.rows[1].get);
44         assert.isNumber(results.data.length);
45         done();
46       });
47     });
48
49     it('should execute a query with an array', function (done) {
50       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ActivityListItem","query":{"orderBy":[{"attribute":"dueDate"},{"attribute":"name"},{"attribute":"uuid"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":["owner.username","assignedTo.username"],"operator":"","isCharacteristic":false,"value":"admin"},{"attribute":"activityType","operator":"ANY","value":["Incident","Opportunity","ToDo","SalesOrder","SalesOrderWorkflow","PurchaseOrder","PurchaseOrderWorkflow","Project","ProjectTask","ProjectWorkflow"]}]},"username":"admin","encryptionKey":"this is any content"}$$);';
51
52       datasource.query(sql, creds, function (err, res) {
53         var results;
54         assert.isNull(err);
55         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
56         results = JSON.parse(res.rows[1].get);
57         assert.isNumber(results.data.length);
58         done();
59       });
60     });
61
62     it('should execute a query with an array with a path', function (done) {
63       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"orderBy":[{"attribute":"lastName"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"owner.username","operator":"ANY","isCharacteristic":false,"value":["admin","foo"]}]},"username":"admin","encryptionKey":"foo"}$$);';
64
65       datasource.query(sql, creds, function (err, res) {
66         var results;
67         assert.isNull(err);
68         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
69         results = JSON.parse(res.rows[1].get);
70         assert.equal(results.data.length, 5);
71         done();
72       });
73     });
74
75     it('should execute a query with a simple filter', function (done) {
76       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"orderBy":[{"attribute":"lastName"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true}]},"username":"admin","encryptionKey":"foo"}$$);';
77
78       datasource.query(sql, creds, function (err, res) {
79         var results;
80         assert.isNull(err);
81         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
82         results = JSON.parse(res.rows[1].get);
83         assert.isNumber(results.data.length);
84         done();
85       });
86     });
87
88     it('should execute a query with a simple filter and a join filter', function (done) {
89       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"orderBy":[{"attribute":"lastName"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":"owner.username","operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"foo"}$$);';
90
91       datasource.query(sql, creds, function (err, res) {
92         var results;
93         assert.isNull(err);
94         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
95         results = JSON.parse(res.rows[1].get);
96         assert.equal(results.data.length, 5);
97         done();
98       });
99     });
100
101     it('should execute a query with a simple filter and two join filters', function (done) {
102       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"orderBy":[{"attribute":"lastName"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":"account.number","operator":"","isCharacteristic":false,"value":"admin"},{"attribute":"owner.username","operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"foo"}$$);';
103
104       datasource.query(sql, creds, function (err, res) {
105         var results;
106         assert.isNull(err);
107         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
108         results = JSON.parse(res.rows[1].get);
109         assert.equal(results.length, 0);
110         done();
111       });
112     });
113
114     it('should execute a query with an array of attributes', function (done) {
115       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"orderBy":[{"attribute":"lastName"},{"attribute":"firstName"},{"attribute":"primaryEmail"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":["number","name","firstName","lastName","jobTitle","phone","alternate","fax","primaryEmail","webAddress","accountParent"],"operator":"MATCHES","value":"coltraine"},{"attribute":"isActive","operator":"=","value":true}]},"username":"admin","encryptionKey":"this is any content"}$$);';
116
117       datasource.query(sql, creds, function (err, res) {
118         var results;
119         assert.isNull(err);
120         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
121         results = JSON.parse(res.rows[1].get);
122         assert.equal(results.data.length, 1);
123         done();
124       });
125     });
126
127     it('should execute a query with two join filters on the same table', function (done) {
128       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"IncidentListItem","query":{"orderBy":[{"attribute":"priorityOrder"},{"attribute":"updated","descending":true},{"attribute":"number","descending":true,"numeric":true}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":["owner.username","assignedTo.username"],"operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"this is any content"}$$);';
129
130       datasource.query(sql, creds, function (err, res) {
131         var results;
132         assert.isNull(err);
133         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
134         results = JSON.parse(res.rows[1].get);
135         assert.equal(results.data.length, 4);
136         done();
137       });
138     });
139
140     it('should execute a query with an x.y join path when x.y.naturalKeyOfY is needed', function (done) {
141       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteListItem","dispatch":{"functionName":"fetch","parameters":{"orderBy":[{"attribute":"item.number"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":"item.classCode","operator":"","isCharacteristic":false,"value":"TOYS-CARS"}]}},"encoding":"rjson","username":"admin","encryptionKey":"this is any content"}$$);';
142
143       datasource.query(sql, creds, function (err, res) {
144         var results;
145         assert.isNull(err);
146         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
147         results = JSON.parse(res.rows[1].post);
148         assert.isNumber(results.length);
149         done();
150       });
151     });
152
153     it('should execute a query with ambiguous column filters', function (done) {
154       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ToDoListItem","query":{"orderBy":[{"attribute":"priorityOrder"},{"attribute":"dueDate"},{"attribute":"name"}],"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":["owner.username","assignedTo.username"],"operator":"","isCharacteristic":false,"value":"admin"},{"attribute":"uuid","operator":"=","value":"23eef809-2f7c-4289-9eab-a72d621a6adb"}],"rowOffset":0,"rowLimit":50},"username":"admin","encryptionKey":"this is any content"}$$);';
155
156       datasource.query(sql, creds, function (err, res) {
157         var results;
158         assert.isNull(err);
159         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
160         results = JSON.parse(res.rows[1].get);
161         done();
162       });
163     });
164
165     it('should execute a query filtering on an orm-extended field', function (done) {
166       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ProspectRelation","query":{"orderBy":[{"attribute":"number"}],"parameters":[{"attribute":"isActive","operator":"=","value":true}],"rowOffset":0,"rowLimit":50},"username":"admin","encryptionKey":"this is any content"}$$);';
167
168       datasource.query(sql, creds, function (err, res) {
169         var results;
170         assert.isNull(err);
171         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
172         results = JSON.parse(res.rows[1].get);
173         assert.equal(results.data.length, 1);
174         done();
175       });
176     });
177
178     it('should execute a simple item-site fetch', function (done) {
179       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteListItem","dispatch":{"functionName":"fetch","parameters":{"orderBy":[{"attribute":"item.number"}],"parameters":[{"attribute":"isActive","operator":"=","value":true}],"rowOffset":0,"rowLimit":50}},"username":"admin"}$$);';
180
181       datasource.query(sql, creds, function (err, res) {
182         var results;
183         assert.isNull(err);
184         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
185         results = JSON.parse(res.rows[1].post);
186         assert.isNumber(results.length);
187         done();
188       });
189     });
190
191     it('should execute an item-site fetch', function (done) {
192       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteRelation","dispatch":{"functionName":"fetch","parameters":{"parameters":[{"attribute":"item.number","value":"BTRUCK1"},{"attribute":"site.code","value":"WH1"}]}},"username":"admin","encryptionKey":"this is any content"}$$);';
193
194       datasource.query(sql, creds, function (err, res) {
195         var results;
196         assert.isNull(err);
197         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
198         results = JSON.parse(res.rows[1].post);
199         assert.equal(results.length, 1);
200         done();
201       });
202     });
203
204     it('should execute a complicated item-site fetch', function (done) {
205       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteRelation","dispatch":{"functionName":"fetch","parameters":{"parameters":[{"attribute":"item.isSold","value":true},{"attribute":"item.isActive","value":true},{"attribute":"isSold","value":true},{"attribute":"isActive","value":true},{"attribute":"site.code","value":"WH1"},{"attribute":"customer","value":"TTOYS"},{"attribute":["number","barcode"],"operator":"BEGINS_WITH","value":"bt","keySearch":true}],"orderBy":[{"attribute":"number"},{"attribute":"barcode"}],"rowLimit":1}},"username":"admin","encryptionKey":"this is any content"}$$)';
206
207       datasource.query(sql, creds, function (err, res) {
208         var results;
209         assert.isNull(err);
210         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
211         results = JSON.parse(res.rows[1].post);
212         assert.equal(results.length, 1);
213         done();
214       });
215     });
216
217     it('should be able to search item-site by number', function (done) {
218       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteListItem","dispatch":{"functionName":"fetch","parameters":{"orderBy":[{"attribute":"item.number"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":"item.number","operator":"MATCHES","isCharacteristic":false,"value":"yt"}]}},"encoding":"rjson","username":"admin","encryptionKey":"foo"}$$);';
219
220       datasource.query(sql, creds, function (err, res) {
221         var results;
222         assert.isNull(err);
223         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
224         results = JSON.parse(res.rows[1].post);
225         assert.isNumber(results.length);
226         done();
227       });
228     });
229
230     it('should be able to do an item-site search with a keysearch', function (done) {
231       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteRelation","dispatch":{"functionName":"fetch","parameters":{"parameters":[{"attribute":["number","barcode"],"operator":"BEGINS_WITH","value":"BTR","keySearch":true}],"orderBy":[{"attribute":"number"},{"attribute":"barcode"}],"rowLimit":10}},"username":"admin","encryptionKey":"this is any content"}$$);';
232
233       datasource.query(sql, creds, function (err, res) {
234         var results;
235         assert.isNull(err);
236         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
237         results = JSON.parse(res.rows[1].post);
238         assert.isNumber(results.length);
239         done();
240       });
241     });
242
243     it('should be able to do a complex item-site search with a keysearch and join table parameters', function (done) {
244       var sql = 'select xt.js_init(true);select xt.post($${"nameSpace":"XM","type":"ItemSiteRelation","dispatch":{"functionName":"fetch","parameters":{"parameters":[{"attribute":"item.isSold","value":true},{"attribute":"item.isActive","value":true},{"attribute":"isSold","value":true},{"attribute":"isActive","value":true},{"attribute":"site.code","value":"WH1"},{"attribute":"customer","value":"TTOYS"},{"attribute":["number","barcode"],"operator":"BEGINS_WITH","value":"btr","keySearch":true}],"orderBy":[{"attribute":"number"},{"attribute":"barcode"}],"rowLimit":10}},"username":"admin"}$$);';
245
246       datasource.query(sql, creds, function (err, res) {
247         var results;
248         assert.isNull(err);
249         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
250         results = JSON.parse(res.rows[1].post);
251         assert.isNumber(results.length);
252         done();
253       });
254     });
255
256     it('should support a nested order-by', function (done) {
257       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ItemSource","query":{"orderBy":[{"attribute":"vendorItemNumber"},{"attribute":"vendor.name"}],"parameters":[{"attribute":"isActive","value":true},{"attribute":"effective","operator":"<=","value":"2014-03-20T04:00:00.000Z"},{"attribute":"expires","operator":">=","value":"2014-03-22T01:18:09.202Z"}],"rowOffset":0,"rowLimit":50},"username":"admin","encryptionKey":"this is any content"}$$);';
258
259       datasource.query(sql, creds, function (err, res) {
260         var results;
261         assert.isNull(err);
262         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
263         results = JSON.parse(res.rows[1].get);
264         assert.equal(results.data.length, 20);
265         done();
266       });
267     });
268
269     it.skip('should support an ambiguous primary key', function (done) {
270       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"IssueToShipping","query":{"orderBy":[{"attribute":"lineNumber"},{"attribute":"subNumber"}],"parameters":[{"attribute":"order.uuid","operator":"=","value":"d3538bbd-826a-4351-b35c-795d7db99ba0"}],"rowOffset":0,"rowLimit":50},"username":"admin","encryptionKey":"this is any content"}$$);';
271
272       if (!isCommercial) {
273         // forget about it
274         done();
275         return;
276       }
277
278       datasource.query(sql, creds, function (err, res) {
279         var results;
280         assert.isNull(err);
281         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
282         results = JSON.parse(res.rows[1].get);
283         assert.equal(results.length, 0);
284         done();
285       });
286     });
287
288     it('should allow the shortcut of querying a toOne directly by its natural key', function (done) {
289       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"IncidentListItem","query":{"orderBy":[{"attribute":"priorityOrder"},{"attribute":"updated","descending":true},{"attribute":"number","descending":true,"numeric":true}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"category","operator":"","isCharacteristic":false,"value":"Customer"},{"attribute":["owner.username","assignedTo.username"],"operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"this is any content"}$$);';
290
291       datasource.query(sql, creds, function (err, res) {
292         var results;
293         assert.isNull(err);
294         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
295         results = JSON.parse(res.rows[1].get);
296         assert.isNumber(results.data.length);
297         done();
298       });
299     });
300
301     it('should allow the shortcut of querying a toOne directly by its natural key with an attribute array', function (done) {
302       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"SalesOrderListItem","query":{"orderBy":[{"attribute":"number"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":["number","customerPurchaseOrderNumber","status","orderNotes","currency","billtoName","billtoCity","billtoState","billtoCountry","shiptoName","shiptoCity","shiptoState","shiptoCountry"],"operator":"MATCHES","value":"trem"},{"attribute":"status","value":"O"}]},"username":"admin","encryptionKey":"this is any content"}$$);';
303
304       datasource.query(sql, creds, function (err, res) {
305         var results;
306         assert.isNull(err);
307         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
308         results = JSON.parse(res.rows[1].get);
309         assert.isNumber(results.data.length);
310         done();
311       });
312     });
313
314     it('should allow querying by characteristics', function (done) {
315       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"orderBy":[{"attribute":"lastName"},{"attribute":"firstName"},{"attribute":"primaryEmail"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":"CONTACT-BIRTHDAY","operator":"MATCHES","isCharacteristic":true,"value":"foo"}]},"username":"admin","encryptionKey":"this is any content"}$$);';
316
317       datasource.query(sql, creds, function (err, res) {
318         var results;
319         assert.isNull(err);
320         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
321         results = JSON.parse(res.rows[1].get);
322         assert.equal(results.length, 0);
323         done();
324       });
325     });
326
327     it('should work with an empty parameters list', function (done) {
328       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"IncidentListItem","query":{"parameters":[],"orderBy":[],"rowLimit":100},"username":"admin","encryptionKey":"xq5j2"}$$);';
329
330       datasource.query(sql, creds, function (err, res) {
331         var results;
332         assert.isNull(err);
333         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
334         results = JSON.parse(res.rows[1].get);
335         assert.equal(results.data.length, 4);
336         done();
337       });
338     });
339
340     it('should facilitate the count query', function (done) {
341       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ContactListItem","query":{"count":true,"orderBy":[{"attribute":"lastName"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":"isActive","operator":"=","value":true},{"attribute":"owner.username","operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"this is any content"}$$);';
342       datasource.query(sql, creds, function (err, res) {
343         var results;
344         assert.isNull(err);
345         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
346         results = JSON.parse(res.rows[1].get);
347         assert.equal(results.data[0].count, 5);
348         done();
349       });
350     });
351
352     it('should allow an order-by on a toOne', function (done) {
353       var sql = 'select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"TaxAssignment","query":{"orderBy":[{"attribute":"tax"}],"rowOffset":0,"rowLimit":50},"username":"admin","encryptionKey":"this is any content"}$$)';
354
355       datasource.query(sql, creds, function (err, res) {
356         var results;
357         assert.isNull(err);
358         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
359         results = JSON.parse(res.rows[1].get);
360         assert.equal(results.data.length, 4);
361         done();
362       });
363     });
364
365     it('should correctly fix a long list of natural key attributes', function (done) {
366       var sql = 'select xt.js_init();select xt.get($${"nameSpace":"XM","type":"IncidentListItem","query":{"orderBy":[{"attribute":"priorityOrder"},{"attribute":"updated","descending":true},{"attribute":"number","descending":true,"numeric":true}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":["number","description","status","category","severity","priority","resolution","project"],"operator":"MATCHES","value":"Certify"},{"attribute":["owner.username","assignedTo.username"],"operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"this is any content"}$$);';
367
368       datasource.query(sql, creds, function (err, res) {
369         var results;
370         assert.isNull(err);
371         assert.equal(1, res.rowCount, JSON.stringify(res.rows));
372         results = JSON.parse(res.rows[1].get);
373         assert.isNumber(results.data.length);
374         done();
375       });
376     });
377
378
379 // incident plus
380 //select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"IncidentListItem","query":{"orderBy":[{"attribute":"priorityOrder"},{"attribute":"updated","descending":true},{"attribute":"number","descending":true,"numeric":true}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":["owner.username","assignedTo.username"],"operator":"","isCharacteristic":false,"value":"admin"},{"attribute":"project","operator":"","isCharacteristic":false,"value":"GREENLEAF"},{"attribute":"foundIn","operator":"","isCharacteristic":false,"value":"d0e6c507-eac5-461c-f63e-91e352a3ffb1"}]},"username":"admin","encryptionKey":"this is any content"}$$)
381
382
383
384 // T&E
385 //select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ProjectListItem","query":{"orderBy":[{"attribute":"number"}],"rowOffset":0,"rowLimit":50,"parameters":[{"attribute":["number","name","projectType","status","department"],"operator":"MATCHES","value":"foo"},{"attribute":"status","operator":"!=","value":"C"},{"attribute":"number","operator":"MATCHES","isCharacteristic":false,"value":"tre"},{"attribute":["owner.username","assignedTo.username"],"operator":"","isCharacteristic":false,"value":"admin"}]},"username":"admin","encryptionKey":"this is any content"}$$)
386 //select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ItemRelation","query":{"parameters":[{"attribute":"projectExpenseMethod","operator":"=","value":"E"},{"attribute":"isActive","value":true},{"attribute":"number","operator":"BEGINS_WITH","value":"pro","keySearch":false}],"orderBy":[{"attribute":"number"}],"rowLimit":1},"username":"admin","encryptionKey":"this is any content"}$$)
387 //select xt.js_init(true);select xt.get($${"nameSpace":"XM","type":"ItemRelation","query":{"parameters":[{"attribute":"projectExpenseMethod","operator":"ANY","value":["E","A"]},{"attribute":"isActive","value":true},{"attribute":"number","operator":"BEGINS_WITH","value":"pro","keySearch":false}],"orderBy":[{"attribute":"number"}],"rowLimit":1},"username":"admin","encryptionKey":"this is any content"}$$)
388
389   });
390 }());
391
392