search
This commit is contained in:
15
src/services/article/enrichSupplier.js
Normal file
15
src/services/article/enrichSupplier.js
Normal file
@@ -0,0 +1,15 @@
|
||||
export function enrichSupplierAnmerkungRow(row) {
|
||||
const enriched = { ...row };
|
||||
|
||||
if (typeof row.cAnmerkung === 'string' && row.cAnmerkung.trim().startsWith('{')) {
|
||||
try {
|
||||
enriched.anmerkung = JSON.parse(row.cAnmerkung);
|
||||
} catch {
|
||||
enriched.anmerkung = row.cAnmerkung;
|
||||
}
|
||||
} else if (row.cAnmerkung != null) {
|
||||
enriched.anmerkung = row.cAnmerkung;
|
||||
}
|
||||
|
||||
return enriched;
|
||||
}
|
||||
192
src/services/article/getArticle.js
Normal file
192
src/services/article/getArticle.js
Normal file
@@ -0,0 +1,192 @@
|
||||
import env from '../../config/env.js';
|
||||
import { getPool } from '../../db/mssqlClient.js';
|
||||
import { getBrutto } from '../../utils/tax.js';
|
||||
import { enrichSupplierAnmerkungRow } from './enrichSupplier.js';
|
||||
import * as Q from '../../db/mssql/getArticle.js';
|
||||
|
||||
async function query(pool, sql, inputs = {}) {
|
||||
const request = pool.request();
|
||||
for (const [name, value] of Object.entries(inputs)) {
|
||||
request.input(name, value);
|
||||
}
|
||||
const result = await request.query(sql);
|
||||
return result.recordset;
|
||||
}
|
||||
|
||||
function buildKatSkipClause() {
|
||||
if (!env.article.katSkip.length) {
|
||||
return '';
|
||||
}
|
||||
return env.article.katSkip.map((id) => `kKategorie != ${id}`).join(' AND ') + ' AND ';
|
||||
}
|
||||
|
||||
async function findArticle(pool, target) {
|
||||
const rows = await query(pool, Q.FIND_ARTICLE, { target });
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
|
||||
async function loadChildren(pool, kArtikel) {
|
||||
return query(pool, Q.CHILDREN_BY_VATER, { kArtikel });
|
||||
}
|
||||
|
||||
async function loadVater(pool, kVaterArtikel) {
|
||||
const rows = await query(pool, Q.VATER_BY_ID, { kArtikel: kVaterArtikel });
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
|
||||
async function loadStueckliste(pool, kStueckliste) {
|
||||
return query(pool, Q.STUECKLISTE, { kStueckliste });
|
||||
}
|
||||
|
||||
async function loadWarehouseExtras(pool, kArtikel) {
|
||||
const [fAndereLager, EANs, KATs] = await Promise.all([
|
||||
query(pool, Q.ANDERE_LAGER, { kArtikel }),
|
||||
query(pool, Q.EANS, { kArtikel }),
|
||||
query(pool, Q.categoriesQuery(buildKatSkipClause(), env.article.katLists), { kArtikel }),
|
||||
]);
|
||||
|
||||
return { fAndereLager, EANs, KATs };
|
||||
}
|
||||
|
||||
async function loadSuppliers(pool, kArtikel) {
|
||||
const rows = await query(pool, Q.SUPPLIERS, { kArtikel });
|
||||
return rows.map(enrichSupplierAnmerkungRow);
|
||||
}
|
||||
|
||||
async function loadIncomingOrders(pool, kArtikel) {
|
||||
return query(pool, Q.INCOMING_ORDERS, { kArtikel });
|
||||
}
|
||||
|
||||
async function loadOpenIncidents(pool, kArtikel) {
|
||||
return query(pool, Q.OPEN_INCIDENTS, { kArtikel });
|
||||
}
|
||||
|
||||
async function loadMerkmale(pool, kArtikel) {
|
||||
return query(pool, Q.MERKMALE, { kArtikel });
|
||||
}
|
||||
|
||||
async function loadAttributes(pool, kArtikel) {
|
||||
return query(pool, Q.ATTRIBUTES, { kArtikel });
|
||||
}
|
||||
|
||||
async function loadStaffel(pool, kArtikel, kSteuerklasse) {
|
||||
const rows = await query(pool, Q.STAFFEL, { kArtikel });
|
||||
return rows.map((item) => ({
|
||||
menge: item.nAnzahlAb.toString(),
|
||||
fVKBrutto: getBrutto(item.fNEttoPreis, kSteuerklasse).toFixed(2),
|
||||
}));
|
||||
}
|
||||
|
||||
async function loadExtraLager(pool, kArtikel, nLager) {
|
||||
const rows = await query(pool, Q.EXTRA_LAGER, { kArtikel, nLager });
|
||||
return rows[0]?.nMenge;
|
||||
}
|
||||
|
||||
async function loadShopSum(pool, kArtikel) {
|
||||
const rows = await query(pool, Q.SHOP_SUM, { kArtikel });
|
||||
return rows[0]?.kShop;
|
||||
}
|
||||
|
||||
async function loadHerstellerGpsr(pool, kArtikel) {
|
||||
const rows = await query(pool, Q.HERSTELLER_GPSR, { kArtikel });
|
||||
const row = rows[0];
|
||||
if (!row) {
|
||||
return { cHerstellerGPSR: null, herstellerBild: null };
|
||||
}
|
||||
|
||||
const herstellerBild = row.herstellerBild
|
||||
? Buffer.from(row.herstellerBild).toString('base64')
|
||||
: null;
|
||||
|
||||
return {
|
||||
cHerstellerGPSR: row.cHerstellerGPSR ?? null,
|
||||
herstellerBild,
|
||||
};
|
||||
}
|
||||
|
||||
async function enrichArticle(pool, article) {
|
||||
const { kArtikel, kSteuerklasse, nIstVater, kVaterArtikel, kStueckliste } = article;
|
||||
|
||||
const parallel = [];
|
||||
|
||||
if (nIstVater === 1) {
|
||||
parallel.push(
|
||||
loadChildren(pool, kArtikel).then((children) => {
|
||||
article.children = children;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (kVaterArtikel) {
|
||||
parallel.push(
|
||||
loadVater(pool, kVaterArtikel).then((vater) => {
|
||||
article.vater = vater;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (kStueckliste !== 0) {
|
||||
parallel.push(
|
||||
loadStueckliste(pool, kStueckliste).then((liste) => {
|
||||
article.liste = liste;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
parallel.push(
|
||||
loadWarehouseExtras(pool, kArtikel).then(({ fAndereLager, EANs, KATs }) => {
|
||||
article.fAndereLager = fAndereLager;
|
||||
article.EANs = EANs;
|
||||
article.KATs = KATs;
|
||||
}),
|
||||
loadSuppliers(pool, kArtikel).then((lief) => {
|
||||
article.lief = lief;
|
||||
}),
|
||||
loadIncomingOrders(pool, kArtikel).then((incoming) => {
|
||||
article.incoming = incoming;
|
||||
}),
|
||||
loadOpenIncidents(pool, kArtikel).then((orders) => {
|
||||
article.orders = orders;
|
||||
}),
|
||||
loadMerkmale(pool, kArtikel).then((merkmale) => {
|
||||
article.merkmale = merkmale;
|
||||
}),
|
||||
loadAttributes(pool, kArtikel).then((attribute) => {
|
||||
article.attribute = attribute;
|
||||
}),
|
||||
loadStaffel(pool, kArtikel, kSteuerklasse).then((staffel) => {
|
||||
article.staffel = staffel;
|
||||
}),
|
||||
loadExtraLager(pool, kArtikel, 0).then((garage) => {
|
||||
article.garage = garage;
|
||||
}),
|
||||
loadExtraLager(pool, kArtikel, 1).then((keller) => {
|
||||
article.keller = keller;
|
||||
}),
|
||||
loadShopSum(pool, kArtikel).then((shops) => {
|
||||
article.shops = shops;
|
||||
}),
|
||||
loadHerstellerGpsr(pool, kArtikel).then(({ cHerstellerGPSR, herstellerBild }) => {
|
||||
article.cHerstellerGPSR = cHerstellerGPSR;
|
||||
article.herstellerBild = herstellerBild;
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(parallel);
|
||||
return article;
|
||||
}
|
||||
|
||||
export async function getArticle(target) {
|
||||
const trimmed = target?.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pool = await getPool();
|
||||
const article = await findArticle(pool, trimmed);
|
||||
if (!article) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return enrichArticle(pool, article);
|
||||
}
|
||||
146
src/services/article/searchArticles.js
Normal file
146
src/services/article/searchArticles.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import { getPool } from '../../db/mssqlClient.js';
|
||||
import { LOAD_KAT_MAP } from '../../db/mssql/categories.js';
|
||||
import * as Q from '../../db/mssql/searchArticles.js';
|
||||
|
||||
let katMapCache;
|
||||
|
||||
export function parseFilter(raw) {
|
||||
if (raw === undefined || raw === null || raw === '') {
|
||||
return [''];
|
||||
}
|
||||
const parts = String(raw).split(',').map((s) => s.trim()).filter(Boolean);
|
||||
return parts.length ? parts : [''];
|
||||
}
|
||||
|
||||
function getUpPath(kat, katMap) {
|
||||
const list = [kat];
|
||||
if (katMap[kat]) {
|
||||
for (const item of getUpPath(katMap[kat], katMap)) {
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
async function loadKatMap(pool) {
|
||||
if (katMapCache) {
|
||||
return katMapCache;
|
||||
}
|
||||
|
||||
const result = await pool.request().query(LOAD_KAT_MAP);
|
||||
katMapCache = {};
|
||||
for (const item of result.recordset) {
|
||||
katMapCache[item.kKategorie] = item.kOberKategorie;
|
||||
}
|
||||
return katMapCache;
|
||||
}
|
||||
|
||||
function buildKatAgg(recordset, katMap) {
|
||||
const katagg = {};
|
||||
|
||||
for (const item of recordset) {
|
||||
if (!item.katList) {
|
||||
continue;
|
||||
}
|
||||
for (const kat of item.katList.split(',')) {
|
||||
for (const katpart of getUpPath(kat, katMap)) {
|
||||
if (!katagg[katpart]) {
|
||||
katagg[katpart] = 0;
|
||||
}
|
||||
katagg[katpart]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return katagg;
|
||||
}
|
||||
|
||||
function buildTargetClause(request, target) {
|
||||
const parts = [];
|
||||
let i = 0;
|
||||
|
||||
const terms = target
|
||||
.toString()
|
||||
.trim()
|
||||
.replace(/ {2,}/g, ' ')
|
||||
.split(' ')
|
||||
.filter(Boolean);
|
||||
|
||||
for (const part of terms) {
|
||||
request.input(`target_${i}`, `%${part}%`);
|
||||
parts.push(
|
||||
'tGebinde.cEAN LIKE @target_' + i +
|
||||
' OR tLiefArtikel.cLiefArtNr LIKE @target_' + i +
|
||||
' OR tArtikelBeschreibung.cName LIKE @target_' + i +
|
||||
' OR tArtikel.cArtNr LIKE @target_' + i +
|
||||
' OR tArtikel.cBarcode LIKE @target_' + i
|
||||
);
|
||||
i++;
|
||||
}
|
||||
|
||||
return { parts, nextIndex: i };
|
||||
}
|
||||
|
||||
function buildCategoryClause(request, filter, startIndex) {
|
||||
const parts = [];
|
||||
let i = startIndex;
|
||||
|
||||
for (const part of filter) {
|
||||
request.input(`target_${i}`, part);
|
||||
parts.push(`tkategorieartikel.kKategorie = @target_${i}`);
|
||||
i++;
|
||||
}
|
||||
|
||||
return {
|
||||
clause: '(' + parts.join(') OR (') + ')',
|
||||
nextIndex: i,
|
||||
};
|
||||
}
|
||||
|
||||
function buildWhereClause(parts, filter) {
|
||||
const aktivClause = filter?.[0] == '751' ? '' : " AND (tArtikel.cAktiv = 'Y')";
|
||||
if (!parts.length) {
|
||||
return `1=1${aktivClause}`;
|
||||
}
|
||||
return '(' + parts.join(') AND (') + ')' + aktivClause;
|
||||
}
|
||||
|
||||
function buildQuery(filter, categoryFilter) {
|
||||
const select = filter[0] === '' || filter[0] === null
|
||||
? `SELECT TOP (2000) ${Q.SELECT_COLUMNS}`
|
||||
: `SELECT ${Q.SELECT_COLUMNS}`;
|
||||
|
||||
const categoryJoin = categoryFilter
|
||||
? Q.CATEGORY_JOIN.replace('{categoryFilter}', categoryFilter)
|
||||
: '';
|
||||
|
||||
return `${select}
|
||||
${Q.FROM_JOINS}
|
||||
${categoryJoin}
|
||||
WHERE `;
|
||||
}
|
||||
|
||||
export async function searchArticles(target, filter = ['']) {
|
||||
const trimmed = target?.toString().trim();
|
||||
if (!trimmed) {
|
||||
return { katagg: {}, recordset: [] };
|
||||
}
|
||||
|
||||
const pool = await getPool();
|
||||
const request = pool.request();
|
||||
const { parts } = buildTargetClause(request, trimmed);
|
||||
const whereClause = buildWhereClause(parts, filter);
|
||||
|
||||
let sql = buildQuery(filter, null) + whereClause + Q.GROUP_ORDER;
|
||||
|
||||
if (filter[0] !== '' && filter[0] !== null) {
|
||||
const { clause: categoryFilter } = buildCategoryClause(request, filter, parts.length);
|
||||
sql = buildQuery(filter, categoryFilter) + whereClause + Q.GROUP_ORDER;
|
||||
}
|
||||
|
||||
const result = await request.query(sql);
|
||||
const katMap = await loadKatMap(pool);
|
||||
const katagg = buildKatAgg(result.recordset, katMap);
|
||||
|
||||
return { katagg, recordset: result.recordset };
|
||||
}
|
||||
Reference in New Issue
Block a user