hedera-web/back/common/models/item.js

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;
}