175 lines
6.0 KiB
JavaScript
175 lines
6.0 KiB
JavaScript
import sql from 'mssql';
|
|
import { getPool } from '../db.js';
|
|
import { getCustomerGroupIds } from './customer-groups.js';
|
|
import { getProductAttributes } from './product-attributes.js';
|
|
import { getActiveShopId } from '../shop.js';
|
|
|
|
const LANGUAGE_ID = Number(process.env.LANGUAGE_ID) || 1;
|
|
const TAX_ZONE_NAME = process.env.TAX_ZONE_NAME || 'Zone-EU';
|
|
const IMAGE_PLATFORM_ID = Number(process.env.IMAGE_PLATFORM_ID) || 1;
|
|
const IMAGE_SHOP_ID = Number(process.env.IMAGE_SHOP_ID) || 0;
|
|
|
|
const PRODUCT_LIST_SQL = `
|
|
WITH TaxRates AS (
|
|
SELECT kSteuerklasse, fSteuersatz
|
|
FROM dbo.tSteuersatz
|
|
WHERE kSteuerzone IN (SELECT kSteuerzone FROM dbo.tSteuerzone WHERE cName = @taxZoneName)
|
|
),
|
|
ImageRV AS (
|
|
SELECT kArtikel, MAX(CONVERT(BIGINT, bRowversion)) AS maxImageRV
|
|
FROM dbo.tArtikelbildPlattform
|
|
WHERE kShop = @kShop
|
|
GROUP BY kArtikel
|
|
)
|
|
SELECT TOP (@limit)
|
|
a.kArtikel AS id,
|
|
a.cArtNr AS sku,
|
|
ab.cName AS name,
|
|
a.fVKNetto AS netPrice,
|
|
tr.fSteuersatz AS taxRate,
|
|
a.dErstelldatum AS createdAt,
|
|
CASE
|
|
WHEN ir.maxImageRV IS NOT NULL AND ir.maxImageRV > CONVERT(BIGINT, a.bRowversion)
|
|
THEN ir.maxImageRV
|
|
ELSE CONVERT(BIGINT, a.bRowversion)
|
|
END AS lastChanged,
|
|
(
|
|
SELECT TOP 1 img.cHash
|
|
FROM dbo.tArtikelbildPlattform abp
|
|
INNER JOIN dbo.tBild img ON img.kBild = abp.kBild
|
|
WHERE abp.kArtikel = a.kArtikel
|
|
AND abp.kPlattform = @imagePlatformId
|
|
AND abp.kShop = @imageShopId
|
|
ORDER BY abp.nNr
|
|
) AS imgHash,
|
|
(
|
|
SELECT STRING_AGG(CAST(ka.kKategorie AS varchar(20)), ',')
|
|
FROM dbo.tkategorieartikel ka
|
|
WHERE ka.kArtikel = a.kArtikel
|
|
) AS categoryIds,
|
|
a.nIstVater AS isParent,
|
|
a.kVaterArtikel AS parentArticleId,
|
|
CASE WHEN a.kStueckliste <> 0 THEN '1' ELSE '0' END AS isCompositeProduct,
|
|
(
|
|
SELECT TOP 1 pv.cVariantName
|
|
FROM Pos.vProductVariant pv
|
|
WHERE pv.kProduct = a.kArtikel
|
|
) AS variantName
|
|
FROM dbo.tArtikel a
|
|
INNER JOIN dbo.tArtikelBeschreibung ab ON ab.kArtikel = a.kArtikel AND ab.kSprache = @languageId
|
|
LEFT JOIN TaxRates tr ON tr.kSteuerklasse = a.kSteuerklasse
|
|
LEFT JOIN ImageRV ir ON ir.kArtikel = a.kArtikel
|
|
WHERE a.cAktiv = 'Y'
|
|
AND (@kShop = 0 OR EXISTS (
|
|
SELECT 1 FROM dbo.tKategorieArtikel ka
|
|
INNER JOIN dbo.tKategorieShop ks ON ks.kKategorie = ka.kKategorie AND ks.kShop = @kShop
|
|
WHERE ka.kArtikel = a.kArtikel
|
|
))
|
|
AND (
|
|
CONVERT(BIGINT, a.bRowversion) > @cursor
|
|
OR (ir.maxImageRV IS NOT NULL AND ir.maxImageRV > @cursor)
|
|
)
|
|
ORDER BY lastChanged ASC;
|
|
`;
|
|
|
|
function priceOverridesSql(articleIds) {
|
|
const idList = articleIds.join(',');
|
|
return `
|
|
SELECT p.kArtikel AS articleId, p.kKundenGruppe AS customerGroupId, MIN(pd.fNettoPreis) AS netPrice
|
|
FROM dbo.tPreis p
|
|
INNER JOIN dbo.tPreisDetail pd ON pd.kPreis = p.kPreis
|
|
WHERE p.kArtikel IN (${idList}) AND p.kShop = 0 AND pd.nAnzahlAb = 0
|
|
GROUP BY p.kArtikel, p.kKundenGruppe;
|
|
`;
|
|
}
|
|
|
|
function formatDateTime(date) {
|
|
if (!date) {
|
|
return '0001-01-01 00:00:00';
|
|
}
|
|
const pad = (n) => String(n).padStart(2, '0');
|
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
}
|
|
|
|
function grossPrice(netPrice, taxRate) {
|
|
return (Number(netPrice) * (1 + Number(taxRate || 0) / 100)).toFixed(2);
|
|
}
|
|
|
|
export async function getProductList({ cursor = 0, limit = 20 } = {}) {
|
|
const pool = getPool();
|
|
|
|
const [productResult, customerGroupIds] = await Promise.all([
|
|
pool
|
|
.request()
|
|
.input('cursor', sql.BigInt, cursor)
|
|
.input('limit', sql.Int, limit)
|
|
.input('languageId', sql.Int, LANGUAGE_ID)
|
|
.input('taxZoneName', sql.NVarChar, TAX_ZONE_NAME)
|
|
.input('imagePlatformId', sql.Int, IMAGE_PLATFORM_ID)
|
|
.input('imageShopId', sql.Int, IMAGE_SHOP_ID)
|
|
.input('kShop', sql.Int, getActiveShopId())
|
|
.query(PRODUCT_LIST_SQL),
|
|
getCustomerGroupIds(),
|
|
]);
|
|
|
|
const products = productResult.recordset;
|
|
const articleIds = products.map((p) => p.id);
|
|
|
|
const overridesByArticle = new Map();
|
|
let attributesByArticle = new Map();
|
|
if (articleIds.length > 0) {
|
|
const [overrideResult, attributeMap] = await Promise.all([
|
|
pool.request().query(priceOverridesSql(articleIds)),
|
|
getProductAttributes(pool, articleIds),
|
|
]);
|
|
attributesByArticle = attributeMap;
|
|
for (const row of overrideResult.recordset) {
|
|
if (!overridesByArticle.has(row.articleId)) {
|
|
overridesByArticle.set(row.articleId, new Map());
|
|
}
|
|
overridesByArticle.get(row.articleId).set(row.customerGroupId, row.netPrice);
|
|
}
|
|
}
|
|
|
|
return products.map((product) => {
|
|
const overrides = overridesByArticle.get(product.id);
|
|
const basePrice = grossPrice(product.netPrice, product.taxRate);
|
|
|
|
const prices = customerGroupIds.map((customerGroupId) => {
|
|
const overrideNetPrice = overrides?.get(customerGroupId);
|
|
const price =
|
|
overrideNetPrice !== undefined ? grossPrice(overrideNetPrice, product.taxRate) : basePrice;
|
|
return {
|
|
customerGroupId: String(customerGroupId),
|
|
customerId: '0',
|
|
price,
|
|
quantity: '0',
|
|
};
|
|
});
|
|
|
|
const categoryIds = product.categoryIds ? product.categoryIds.split(',') : [];
|
|
const articleAttributes = attributesByArticle.get(product.id);
|
|
|
|
return {
|
|
_id: String(product.id),
|
|
imghash: product.imgHash ?? null,
|
|
imgsrc: product.imgHash ?? null,
|
|
sku: product.sku,
|
|
name: product.name,
|
|
tax_rate: String(Math.round(Number(product.taxRate || 0))),
|
|
price: basePrice,
|
|
created_at: formatDateTime(product.createdAt),
|
|
lastChanged: String(product.lastChanged),
|
|
categories_id: categoryIds[0] ?? '0',
|
|
categories: categoryIds.map((categoryId) => ({ categoryId })),
|
|
prices,
|
|
is_parent: product.isParent ? '1' : '0',
|
|
parent: product.parentArticleId > 0 ? String(product.parentArticleId) : '0',
|
|
variants: product.variantName ?? '',
|
|
isCompositeProduct: product.isCompositeProduct,
|
|
attributes: articleAttributes?.attributes ?? [],
|
|
...(articleAttributes?.deposit ?? {}),
|
|
};
|
|
});
|
|
}
|