const PFAND_ATTR_IDS = [ 'Pfandartikel', 'Pfandart (Bezeichnung auf Ausdruck)', 'Pfandbetrag', ]; let pfandMetaCache = null; let activeShopsCache = null; async function getPfandMetadata(pool) { if (!pfandMetaCache) { const result = await pool.request().query(` SELECT a.kAttribut, a.cAttributId, a.nSortierung, a.kFeldTyp, a.cGruppeName, s.cName FROM dbo.tAttribut a INNER JOIN dbo.tAttributSprache s ON s.kAttribut = a.kAttribut AND s.kSprache = 0 WHERE a.cGruppeName = 'JTL-POS' AND a.cAttributId IN ('Pfandartikel', 'Pfandart (Bezeichnung auf Ausdruck)', 'Pfandbetrag') `); pfandMetaCache = result.recordset; } return pfandMetaCache; } async function getActiveShops(pool) { if (!activeShopsCache) { const result = await pool.request().query(` WITH ActiveShops AS ( SELECT ss.kShop, s.kKategorie AS rootKategorie FROM dbo.tShopSubshop ss INNER JOIN dbo.tShop s ON s.kShop = ss.kShop WHERE ss.nGesperrt = 0 ) SELECT DISTINCT ash.kShop FROM ActiveShops ash `); activeShopsCache = result.recordset.map((row) => row.kShop); } return activeShopsCache; } function attributesSql(articleIds, activeShops, pfandKAttribute) { const shopList = activeShops.length ? activeShops.join(',') : 'NULL'; const pfandList = pfandKAttribute.length ? pfandKAttribute.join(',') : 'NULL'; return ` SELECT aa.kArtikel AS articleId, aa.kAttribut, aa.kShop, at.nSortierung, at.kFeldTyp, at.cGruppeName, at.cAttributId, ats.cName, aas.cWertVarchar, aas.nWertInt, aas.fWertDecimal FROM dbo.tArtikelAttribut aa INNER JOIN dbo.tArtikelAttributSprache aas ON aas.kArtikelAttribut = aa.kArtikelAttribut AND aas.kSprache = 0 INNER JOIN dbo.tAttribut at ON at.kAttribut = aa.kAttribut INNER JOIN dbo.tAttributSprache ats ON ats.kAttribut = at.kAttribut AND ats.kSprache = 0 WHERE aa.kArtikel IN (${articleIds.join(',')}) AND ( aa.kShop = 0 OR ( aa.kShop IN (${shopList}) AND aa.kAttribut IN (${pfandList}) ) ) ORDER BY aa.kArtikel, at.nSortierung, aa.kShop; `; } function mergeArticleAttributes(rows) { const byAttribute = new Map(); for (const row of rows) { byAttribute.set(row.kAttribut, row); } return [...byAttribute.values()].sort((a, b) => a.nSortierung - b.nSortierung); } function toPosAttribute(row) { return { aname: row.cName, aprice: '0.0', asort: String(row.nSortierung), atype: String(row.kFeldTyp), agroup: row.cGruppeName ?? '', }; } function deriveDepositFields(rows) { const byId = new Map(rows.map((row) => [row.cAttributId, row])); const pfandArtikel = byId.get('Pfandartikel'); const pfandArt = byId.get('Pfandart (Bezeichnung auf Ausdruck)'); const pfandBetrag = byId.get('Pfandbetrag'); if (!pfandArtikel && !pfandArt && !pfandBetrag) { return null; } return { deposit: pfandArtikel?.nWertInt === 1 ? '1' : '0', deposit_name: pfandArt?.cWertVarchar ?? '', d_price: pfandBetrag?.fWertDecimal != null ? Number(pfandBetrag.fWertDecimal).toFixed(2) : '0.0', }; } export async function getProductAttributes(pool, articleIds) { if (!articleIds.length) { return new Map(); } const [pfandMeta, activeShops] = await Promise.all([ getPfandMetadata(pool), getActiveShops(pool), ]); const pfandKAttribute = pfandMeta.map((row) => row.kAttribut); const result = await pool.request().query(attributesSql(articleIds, activeShops, pfandKAttribute)); const rowsByArticle = new Map(); for (const row of result.recordset) { if (!rowsByArticle.has(row.articleId)) { rowsByArticle.set(row.articleId, []); } rowsByArticle.get(row.articleId).push(row); } const attributesByArticle = new Map(); for (const [articleId, rows] of rowsByArticle) { const merged = mergeArticleAttributes(rows); attributesByArticle.set(articleId, { attributes: merged.map(toPosAttribute), deposit: deriveDepositFields(merged), }); } return attributesByArticle; }