369 lines
8.5 KiB
JavaScript
369 lines
8.5 KiB
JavaScript
const warehouseIds = [1, 44];
|
|
|
|
module.exports = Self => {
|
|
Self.remoteMethod('calcCatalog', {
|
|
description: 'Get the available and prices list for an item',
|
|
accessType: 'READ',
|
|
accepts: [
|
|
{
|
|
arg: 'id',
|
|
type: 'Number',
|
|
description: 'The item id'
|
|
}, {
|
|
arg: 'dated',
|
|
type: 'Date',
|
|
description: 'The date'
|
|
}, {
|
|
arg: 'addressFk',
|
|
type: 'Number',
|
|
description: 'The address id'
|
|
}, {
|
|
arg: 'agencyModeFk',
|
|
type: 'Number',
|
|
description: 'The agency id'
|
|
}
|
|
],
|
|
returns: {
|
|
type: ['Object'],
|
|
description: 'The item available and prices list',
|
|
root: true,
|
|
},
|
|
http: {
|
|
path: `/:id/calcCatalog`,
|
|
verb: 'GET'
|
|
}
|
|
});
|
|
|
|
Self.calcCatalog = (id, dated, addressFk, agencyModeFk, cb) => {
|
|
Self.dataSource.connector.query(
|
|
`CALL hedera.item_calcCatalog(?, ?, ?, ?)`,
|
|
[id, dated, addressFk, agencyModeFk],
|
|
(err, res) => {
|
|
if (err) return cb(err)
|
|
return cb(null, res[0])
|
|
}
|
|
);
|
|
};
|
|
|
|
Self.remoteMethod('catalog', {
|
|
description: 'Get the catalog',
|
|
accessType: 'READ',
|
|
accepts: [
|
|
{
|
|
arg: 'dated',
|
|
type: 'Date',
|
|
description: 'The available date'
|
|
}, {
|
|
arg: 'typeFk',
|
|
type: 'Number',
|
|
description: 'The item type id'
|
|
}, {
|
|
arg: 'categoryFk',
|
|
type: 'Number',
|
|
description: 'The item category id'
|
|
}, {
|
|
arg: 'search',
|
|
type: 'String',
|
|
description: 'The search string'
|
|
}, {
|
|
arg: 'order',
|
|
type: 'String',
|
|
description: 'The order string'
|
|
}, {
|
|
arg: 'limit',
|
|
type: 'Number',
|
|
description: 'The maximum number of registers'
|
|
}, {
|
|
arg: 'tagFilter',
|
|
type: ['Object'],
|
|
description: 'The tag filter object'
|
|
}
|
|
],
|
|
returns: {
|
|
type: ['Object'],
|
|
description: 'The item list',
|
|
root: true,
|
|
},
|
|
http: {
|
|
path: `/catalog`,
|
|
verb: 'GET'
|
|
}
|
|
});
|
|
|
|
Self.catalog = async (dated, typeFk, categoryFk, search, order, limit, tagFilter) => {
|
|
let $ = Self.app.models;
|
|
let itemIds;
|
|
|
|
let inboundWhere = {
|
|
warehouseFk: {inq: warehouseIds},
|
|
available: {gt: 0},
|
|
dated: {lte: dated},
|
|
and: [
|
|
{or: [
|
|
{expired: {gt: dated}},
|
|
{expired: null}
|
|
]}
|
|
]
|
|
};
|
|
|
|
// Applies base filters
|
|
|
|
if (/^[0-9]+$/.test(search)) {
|
|
itemIds = [parseInt(search)];
|
|
} else {
|
|
let inbounds = await $.Inbound.find({
|
|
fields: ['itemFk'],
|
|
where: inboundWhere
|
|
});
|
|
itemIds = toValues(inbounds, 'itemFk');
|
|
|
|
if (categoryFk || typeFk || search) {
|
|
let where = {
|
|
id: {inq: itemIds}
|
|
};
|
|
|
|
if (typeFk) {
|
|
where.typeFk = typeFk;
|
|
} else if (categoryFk) {
|
|
let types = await $.ItemType.find({
|
|
fields: ['id'],
|
|
where: {categoryFk}
|
|
});
|
|
where.typeFk = {inq: toValues(types, 'id')};
|
|
}
|
|
|
|
if (search)
|
|
where.longName = {like: `%${search}%`};
|
|
|
|
let filter = {
|
|
fields: ['id'],
|
|
where
|
|
};
|
|
|
|
let items = await Self.find(filter);
|
|
itemIds = items.map(i => i.id);
|
|
}
|
|
}
|
|
|
|
// Applies tag filters
|
|
|
|
let baseItemIds = itemIds;
|
|
let tagItems = [];
|
|
let tagFilterIds = [];
|
|
|
|
if (tagFilter && tagFilter.length) {
|
|
for (let filter of tagFilter) {
|
|
let cond;
|
|
let values = filter.values;
|
|
|
|
if (values.length)
|
|
cond = {value: {inq: values}};
|
|
else if (values.min && values.max)
|
|
cond = {intValue: {between: [values.min, values.max]}};
|
|
else if (values.min)
|
|
cond = {intValue: {gte: values.min}};
|
|
else if (values.max)
|
|
cond = {intValue: {lte: values.max}};
|
|
|
|
let where = {
|
|
itemFk: {inq: itemIds},
|
|
tagFk: filter.tagFk
|
|
};
|
|
Object.assign(where, cond);
|
|
|
|
let itemTags = await $.ItemTag.find({
|
|
fields: ['itemFk'],
|
|
where
|
|
});
|
|
tagItems.push(toSet(itemTags, 'itemFk'));
|
|
tagFilterIds.push(filter.tagFk);
|
|
}
|
|
|
|
itemIds = intersect(tagItems);
|
|
}
|
|
|
|
// Obtains distinct tags and it's distinct values
|
|
|
|
let tags = [];
|
|
|
|
if (typeFk || search) {
|
|
let tagValues = await $.ItemTag.find({
|
|
fields: ['tagFk', 'value', 'intValue', 'priority'],
|
|
where: {
|
|
itemFk: {inq: itemIds},
|
|
tagFk: {nin: tagFilterIds}
|
|
},
|
|
order: 'tagFk, value'
|
|
});
|
|
let tagValueMap = toMultiMap(tagValues, 'tagFk');
|
|
|
|
for (let i = 0; i < tagItems.length; i++) {
|
|
let tagFk = tagFilter[i].tagFk;
|
|
let itemIds;
|
|
|
|
if (tagItems.length > 1) {
|
|
let siblings = tagItems.filter(v => v != tagItems[i]);
|
|
itemIds = intersect(siblings);
|
|
} else
|
|
itemIds = baseItemIds;
|
|
|
|
let tagValues = await $.ItemTag.find({
|
|
fields: ['value', 'intValue', 'priority'],
|
|
where: {
|
|
itemFk: {inq: itemIds},
|
|
tagFk: tagFk
|
|
},
|
|
order: 'value'
|
|
});
|
|
|
|
tagValueMap.set(tagFk, tagValues);
|
|
}
|
|
|
|
let tagIds = [...tagValueMap.keys()];
|
|
tags = await $.Tag.find({
|
|
fields: ['id', 'name', 'isQuantitative', 'unit'],
|
|
where: {
|
|
id: {inq: tagIds}
|
|
}
|
|
});
|
|
|
|
for (let tag of tags) {
|
|
let tagValues = tagValueMap.get(tag.id);
|
|
|
|
let filter = tagFilter && tagFilter.find(i => i.tagFk == tag.id);
|
|
filter = filter && filter.values;
|
|
|
|
let values = toSet(tagValues, 'value');
|
|
if (Array.isArray(filter))
|
|
values = new Set([...filter, ...values]);
|
|
|
|
if (tag.isQuantitative) {
|
|
let intValues = toValues(tagValues, 'intValue');
|
|
|
|
if (filter) {
|
|
if (filter.min) intValues.push(filter.min);
|
|
if (filter.max) intValues.push(filter.max);
|
|
}
|
|
|
|
let min = Math.min(...intValues);
|
|
let max = Math.max(...intValues);
|
|
let dif = max - min;
|
|
let digits = new String(dif).length;
|
|
let step = Math.pow(10, digits - 1);
|
|
if (digits > 1 && step * 5 > dif) step /= 10;
|
|
|
|
Object.assign(tag, {
|
|
step,
|
|
min: Math.floor(min / step) * step,
|
|
max: Math.ceil(max / step) * step
|
|
});
|
|
}
|
|
|
|
Object.assign(tag, {
|
|
values: [...values],
|
|
filter
|
|
});
|
|
}
|
|
}
|
|
|
|
// Obtains items data
|
|
|
|
let items = await Self.find({
|
|
fields: ['id', 'longName', 'subName', 'image'],
|
|
where: {id: {inq: itemIds}},
|
|
limit,
|
|
order,
|
|
include: [
|
|
{
|
|
relation: 'tags',
|
|
scope: {
|
|
fields: ['value', 'tagFk'],
|
|
where: {priority: {gt: 4}},
|
|
order: 'priority',
|
|
include: {
|
|
relation: 'tag',
|
|
scope: {fields: ['name']}
|
|
}
|
|
}
|
|
}, {
|
|
relation: 'inbounds',
|
|
scope: {
|
|
fields: ['available', 'dated', 'tableId'],
|
|
where: inboundWhere,
|
|
order: 'dated DESC',
|
|
include: {
|
|
relation: 'buy',
|
|
scope: {fields: ['id', 'price3']}
|
|
},
|
|
}
|
|
}
|
|
]
|
|
});
|
|
|
|
for (let item of items) {
|
|
item.inbound = item.inbounds()[0];
|
|
item.buy = item.inbound && item.inbound.buy();
|
|
item.available = sum(item.inbounds(), 'available');
|
|
}
|
|
|
|
return {items, tags};
|
|
};
|
|
};
|
|
|
|
// Array functions
|
|
|
|
function sum(array, key) {
|
|
if (!Array.isArray(array)) return 0;
|
|
return array.reduce((a, c) => a + c[key], 0);
|
|
}
|
|
|
|
function toMap(objects, key) {
|
|
let map = new Map();
|
|
for (let object of objects)
|
|
map.set(object[key], object);
|
|
return map;
|
|
}
|
|
|
|
function toMultiMap(objects, key) {
|
|
let map = new Map();
|
|
for (let object of objects) {
|
|
let value = map.get(object[key]);
|
|
if (!value) map.set(object[key], value = []);
|
|
value.push(object);
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function toSet(objects, key) {
|
|
let set = new Set();
|
|
for (let object of objects)
|
|
set.add(object[key]);
|
|
return set;
|
|
}
|
|
|
|
function toValues(objects, key) {
|
|
return [...toSet(objects, key)];
|
|
}
|
|
|
|
function intersect(sets) {
|
|
if (!sets.length) return [];
|
|
let array = [];
|
|
let mySets = sets.slice(0);
|
|
let firstSet = mySets.shift();
|
|
|
|
for (let value of firstSet) {
|
|
let isOnAll = true;
|
|
|
|
for (let set of mySets)
|
|
if (!set.has(value)) {
|
|
isOnAll = false;
|
|
break;
|
|
}
|
|
|
|
if (isOnAll)
|
|
array.push(value);
|
|
}
|
|
|
|
return array;
|
|
}
|