a2a853912b7582bae84dfd1af21c59aa98f34284
[xtuple] / enyo-client / database / source / xm / javascript / item_site.sql
1 /* Delete previously misnamed record */
2 delete from xt.js where js_context='xtuple' and js_type = 'item_site';
3
4 select xt.install_js('XM','ItemSite','xtuple', $$
5   /* Copyright (c) 1999-2014 by OpenMFG LLC, d/b/a xTuple.
6      See www.xm.ple.com/CPAL for the full text of the software license. */
7
8 (function () {
9
10   if (!XM.ItemSite) { XM.ItemSite = {}; }
11
12   XM.ItemSite.isDispatchable = true;
13
14   /**
15     Return the current cost for a particular item site.
16   */
17   XM.ItemSite.cost = function (itemsiteId) {
18     if (!XT.Data.checkPrivilege('ViewCosts')) { return null; }
19     return plv8.execute('select itemcost(itemsite_id) as cost from itemsite where obj_uuid = $1;', [itemsiteId])[0].cost;
20   };
21
22   /** @private */
23   var _fetch = function (recordType, backingType, query, idColumn) {
24     query = query || {};
25     idColumn = idColumn || 'itemsite_id';
26
27     var data = Object.create(XT.Data),
28       nameSpace = recordType.beforeDot(),
29       type = recordType.afterDot(),
30       tableNamespace = backingType.beforeDot(),
31       table = backingType.afterDot(),
32       orderBy = query.orderBy,
33       orm = data.fetchOrm(nameSpace, type),
34       keyColumn = XT.Orm.primaryKey(orm, true),
35       customerId = null,
36       accountId = -1,
37       shiptoId,
38       effectiveDate = new Date(),
39       vendorId = null,
40       limit = query.rowLimit ? 'limit ' + Number(query.rowLimit) : '',
41       offset = query.rowOffset ? 'offset ' + Number(query.rowOffset) : '',
42       clause,
43       ret = {
44         nameSpace: nameSpace,
45         type: type
46       },
47       itemJoinMatches,
48       itemJoinTable,
49       keySearch = false,
50       extra = "",
51       qry,
52       counter = 1,
53       ids = [],
54       idParams = [],
55       sqlCount,
56       sql1 = 'select t1.%3$I as id ' +
57             'from %1$I.%2$I t1 {joins} ' +
58             'where {conditions} {extra} group by t1.%3$I{groupBy} ',
59       sql2 = 'select * from %1$I.%2$I where id in ({ids}) {orderBy}';
60
61     /* Handle special parameters */
62     if (query.parameters) {
63       query.parameters = query.parameters.filter(function (param) {
64         var result = false;
65
66         /* Over-ride usual search behavior */
67         if (param.keySearch) {
68           keySearch = param.value;
69           sql1 += ' and itemsite_item_id in (select item_id from item where item_number ~^ ${p1} or item_upccode ~^ ${p1}) ' +
70             'union ' +
71             'select t1.%3$I ' +
72             'from %1$I.%2$I t1 {joins} ' +
73             ' join itemalias on itemsite_item_id=itemalias_item_id ' +
74             '   and itemalias_crmacct_id is null ' +
75             'where {conditions} {extra} ' +
76             ' and (itemalias_number ~^ ${p1}) ' +
77             'union ' +
78             'select t1.%3$I ' +
79             'from %1$I.%2$I t1 {joins} ' +
80             ' join itemalias on itemsite_item_id=itemalias_item_id ' +
81             '   and itemalias_crmacct_id={accountId} ' +
82             'where {conditions} {extra} ' +
83             ' and (itemalias_number ~^ ${p1}) ';
84           return false;
85         }
86
87         switch (param.attribute)
88         {
89         case "customer":
90           customerNumber = param.value;
91           customerId = data.getId(data.fetchOrm('XM', 'CustomerProspectRelation'), param.value);
92           accountId = data.getId(data.fetchOrm('XM', 'AccountRelation'), param.value);
93           break;
94         case "shipto":
95           shiptoId = data.getId(data.fetchOrm('XM', 'CustomerShipto'), param.value);
96           break;
97         case "effectiveDate":
98           effectiveDate = param.value;
99           break;
100         case "vendor":
101           vendorId = data.getId(data.fetchOrm('XM', 'VendorRelation'), param.value);
102           break;
103         default:
104           result = true;
105         }
106         return result;
107       });
108     }
109
110     clause = data.buildClause(nameSpace, type, query.parameters, orderBy);
111
112     /* Check if public.item is already joined through clause.joins. */
113     if (clause.joins && clause.joins.length) {
114       itemJoinMatches = clause.joins.match(/(.item )(jt\d+)/g);
115
116       if (itemJoinMatches && itemJoinMatches.length) {
117         itemJoinTable = itemJoinMatches[0].match(/(jt\d+)/g);
118       }
119     }
120
121     if (!itemJoinTable) {
122       /* public.item is not already joined. Set the default name. */
123       itemJoinTable = 'sidejoin';
124     }
125
126     /* If customer passed, restrict results to item sites allowed to be sold to that customer */
127     if (customerId) {
128       extra += ' and ' + itemJoinTable + '.item_id in (' +
129              'select item_id from item where item_sold and not item_exclusive ' +
130              'union ' +
131              'select item_id from xt.custitem where cust_id=${p2} ' +
132              '  and ${p4}::date between effective and (expires - 1) ';
133
134       if (shiptoId) {
135         extra += 'union ' +
136                'select item_id from xt.shiptoitem where shipto_id=${p3}::integer ' +
137                '  and ${p4}::date between effective and (expires - 1) ';
138       }
139
140       extra += ") ";
141
142       if (!clause.joins) {
143         clause.joins = '';
144       }
145
146       /* public.item is not already joined. Add it here. */
147       if (itemJoinTable === 'sidejoin') {
148         clause.joins = clause.joins + ' left join item ' + itemJoinTable + ' on t1.itemsite_item_id = ' + itemJoinTable + '.item_id ';
149       }
150     }
151
152     /* If vendor passed, and vendor can only supply against defined item sources, then restrict results */
153     if (vendorId) {
154       extra +=  ' and ' + itemJoinTable + '.item_id in (' +
155               '  select itemsrc_item_id ' +
156               '  from itemsrc ' +
157               '  where itemsrc_active ' +
158               '    and itemsrc_vend_id=' + vendorId + ')';
159
160       if (!clause.joins) {
161         clause.joins = '';
162       }
163
164       /* public.item is not already joined. Add it here. */
165       if (itemJoinTable === 'sidejoin') {
166         clause.joins = clause.joins + ' left join item ' + itemJoinTable + ' on t1.itemsite_item_id = ' + itemJoinTable + '.item_id ';
167       }
168     }
169
170     if (query.count) {
171       /* Just get the count of rows that match the conditions */
172       sqlCount = 'select count(distinct t1.%3$I) as count from %1$I.%2$I t1 {joins} where {conditions} {extra};';
173       sqlCount = XT.format(sqlCount, [tableNamespace.decamelize(), table.decamelize(), idColumn]);
174       sqlCount = sqlCount.replace(/{conditions}/g, clause.conditions)
175                          .replace(/{extra}/g, extra)
176                          .replace('{joins}', clause.joins)
177                          .replace(/{p2}/g, clause.parameters.length + 1)
178                          .replace(/{p3}/g, clause.parameters.length + 2)
179                          .replace(/{p4}/g, clause.parameters.length + 3);
180
181       if (customerId) {
182         clause.parameters = clause.parameters.concat([customerId, shiptoId, effectiveDate]);
183       }
184
185       if (DEBUG) {
186         XT.debug('ItemSiteListItem sqlCount = ', sqlCount);
187         XT.debug('ItemSiteListItem values = ', clause.parameters);
188       }
189
190       ret.data = plv8.execute(sqlCount, clause.parameters);
191
192       return ret;
193     }
194
195     sql1 = XT.format(
196       sql1 += '{orderBy} %4$s %5$s;',
197       [tableNamespace, table, idColumn, limit, offset]
198     );
199
200     /* Because we query views of views, you can get inconsistent results */
201     /* when doing limit and offest queries without an order by. Add a default. */
202     if (limit && offset && (!orderBy || !orderBy.length) && !clause.orderByColumns) {
203       /* We only want this on sql1, not sql2's clause.orderBy. */
204       clause.orderByColumns = XT.format('order by t1.%1$I', [idColumn]);
205     }
206
207     /* Query the model */
208     sql1 = sql1.replace(/{conditions}/g, clause.conditions)
209              .replace(/{extra}/g, extra)
210              .replace(/{joins}/g, clause.joins)
211              .replace(/{groupBy}/g, clause.groupByColumns)
212              .replace('{orderBy}', clause.orderByColumns)
213              .replace('{limit}', limit)
214              .replace('{offset}', offset)
215              .replace('{accountId}', accountId)
216              .replace(/{p1}/g, clause.parameters.length + 1)
217              .replace(/{p2}/g, clause.parameters.length + (keySearch ? 2 : 1))
218              .replace(/{p3}/g, clause.parameters.length + (keySearch ? 3 : 2))
219              .replace(/{p4}/g, clause.parameters.length + (keySearch ? 4 : 3));
220
221     if (keySearch) {
222       clause.parameters.push(keySearch);
223     }
224     if (customerId) {
225       clause.parameters = clause.parameters.concat([customerId, shiptoId, effectiveDate]);
226     }
227     if (DEBUG) {
228       XT.debug('ItemSiteListItem sql1 = ', sql1.slice(0,500));
229       XT.debug(sql1.slice(500, 1000));
230       XT.debug(sql1.slice(1000, 1500));
231       XT.debug(sql1.slice(1500, 2000));
232       XT.debug(sql1.slice(2000, 2500));
233       XT.debug('ItemSiteListItem parameters = ', clause.parameters);
234     }
235     qry = plv8.execute(sql1, clause.parameters);
236
237     if (!qry.length) {
238       ret.data = [];
239       return ret;
240     }
241
242     qry.forEach(function (row) {
243       ids.push(row.id);
244       idParams.push("$" + counter);
245       counter++;
246     });
247
248     sql2 = XT.format(sql2, [nameSpace.decamelize(), type.decamelize()]);
249     sql2 = sql2.replace(/{orderBy}/g, clause.orderBy)
250                .replace('{ids}', idParams.join());
251
252     if (DEBUG) {
253       XT.debug('fetch sql2 = ', sql2);
254       XT.debug('fetch values = ', JSON.stringify(ids));
255     }
256
257     ret.data = plv8.execute(sql2, ids);
258
259     return ret;
260   };
261
262   if (!XM.ItemSiteListItem) { XM.ItemSiteListItem = {}; }
263
264   XM.ItemSiteListItem.isDispatchable = true;
265
266   /**
267     Returns item site list items using usual query means with additional special support for:
268       * Attributes `customer`,`shipto`, and `effectiveDate` for exclusive item rules.
269       * Attribute `vendor` to filter on only items with associated item sources.
270       * Cross check on `alias` and `barcode` attributes for item numbers.
271
272     @param {String} Record type. Must have `itemsite` or related view as its orm source table.
273     @param {Object} Additional query filter (Optional)
274     @returns {Array}
275   */
276   XM.ItemSiteListItem.fetch = function (query) {
277     var result = _fetch("XM.ItemSiteListItem", "public.itemsite", query);
278     return result.data;
279   };
280
281   /**
282    Wrapper for XM.ItemSiteListItem.fetch with support for REST query formatting.
283    Sample usage:
284     select xt.post('{
285       "nameSpace":"XM",
286       "type":"ItemSiteListItem",
287       "dispatch":{
288         "functionName":"restFetch",
289         "parameters":[
290           {
291             "query":[
292               {"customer":{"EQUALS":"TTOYS"}},
293               {"shipto":{"EQUALS":"1d103cb0-dac6-11e3-9c1a-0800200c9a66"}},
294               {"effectiveDate":{"EQUALS":"2014-05-01"}}
295             ]
296           }
297         ]
298       },
299       "username":"admin",
300       "encryptionKey":"hm6gnf3xsov9rudi"
301     }');
302
303    @param {Object} options: query
304    @returns Object
305   */
306   XM.ItemSiteListItem.restFetch = function (options) {
307     options = options || {};
308
309     var items = {},
310       query = {},
311       result = {};
312
313     if (options) {
314       /* Convert from rest_query to XM.Model.query structure. */
315       query = XM.Model.restQueryFormat(options);
316
317       /* Perform the query. */
318       return _fetch("XM.ItemSiteListItem", "public.itemsite", query);
319     } else {
320       throw new handleError("Bad Request", 400);
321     }
322   };
323   XM.ItemSiteListItem.restFetch.description = "Returns ItemSiteListItems with additional special support for exclusive item rules, to filter on only items with associated item sources and Cross check on `alias` and `barcode` attributes for item numbers.";
324   XM.ItemSiteListItem.restFetch.request = {
325     "$ref": "ItemSiteListItemQuery"
326   };
327   XM.ItemSiteListItem.restFetch.parameterOrder = ["options"];
328   // For JSON-Schema deff, see:
329   // https://github.com/fge/json-schema-validator/issues/46#issuecomment-14681103
330   XM.ItemSiteListItem.restFetch.schema = {
331     ItemSiteListItemQuery: {
332       properties: {
333         attributes: {
334           title: "ItemSiteListItem Service request attributes",
335           description: "An array of attributes needed to perform a ItemSiteListItem query.",
336           type: "array",
337           items: [
338             {
339               title: "Options",
340               type: "object",
341               "$ref": "ItemSiteListItemOptions"
342             }
343           ],
344           "minItems": 1,
345           "maxItems": 1,
346           required: true
347         }
348       }
349     },
350     ItemSiteListItemOptions: {
351       properties: {
352         query: {
353           title: "query",
354           description: "The query to perform.",
355           type: "array",
356           items: [
357             {
358               title: "column",
359               type: "object"
360             }
361           ],
362           "minItems": 1
363         },
364         orderby: {
365           title: "Order By",
366           description: "The query order by.",
367           type: "array",
368           items: [
369             {
370               title: "column",
371               type: "object"
372             }
373           ]
374         },
375         rowlimit: {
376           title: "Row Limit",
377           description: "The query for paged results.",
378           type: "integer"
379         },
380         maxresults: {
381           title: "Max Results",
382           description: "The query limit for total results.",
383           type: "integer"
384         },
385         pagetoken: {
386           title: "Page Token",
387           description: "The query offset page token.",
388           type: "integer"
389         },
390         count: {
391           title: "Count",
392           description: "Set to true to return only the count of results for this query.",
393           type: "boolean"
394         }
395       }
396     }
397   };
398
399   if (!XM.ItemSiteRelation) { XM.ItemSiteRelation = {}; }
400
401   XM.ItemSiteRelation.isDispatchable = true;
402
403   /**
404     Returns item site relatinos using usual query means with additional special support for:
405       * Attributes `customer`,`shipto`, and `effectiveDate` for exclusive item rules.
406       * Attribute `vendor` to filter on only items with associated item sources.
407       * Cross check on `alias` and `barcode` attributes for item numbers.
408
409     @param {String} Record type. Must have `itemsite` or related view as its orm source table.
410     @param {Object} Additional query filter (Optional)
411     @returns {Array}
412   */
413   XM.ItemSiteRelation.fetch = function (query) {
414     var result = _fetch("XM.ItemSiteRelation", "xt.itemsiteinfo", query);
415     return result.data;
416   };
417
418 }());
419
420 $$ );