Genesis
This commit is contained in:
18
src/queries/category-count.js
Normal file
18
src/queries/category-count.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import sql from 'mssql';
|
||||
import { getPool } from '../db.js';
|
||||
import { CATEGORY_TREE_CTE, categoryTreeRequest, getRootCategoryId } from './category-tree.js';
|
||||
|
||||
const CATEGORY_COUNT_SQL = `
|
||||
${CATEGORY_TREE_CTE}
|
||||
SELECT COUNT(*) AS CategoryCount
|
||||
FROM dbo.tKategorie k
|
||||
WHERE k.kKategorie IN (SELECT kKategorie FROM CategoryTree)
|
||||
AND CONVERT(BIGINT, k.bRowversion) > @cursor;
|
||||
`;
|
||||
|
||||
export async function getCategoryCount({ cursor = 0, rootCategoryId = getRootCategoryId() } = {}) {
|
||||
const result = await categoryTreeRequest(getPool(), rootCategoryId)
|
||||
.input('cursor', sql.BigInt, cursor)
|
||||
.query(CATEGORY_COUNT_SQL);
|
||||
return result.recordset[0]?.CategoryCount ?? 0;
|
||||
}
|
||||
48
src/queries/category-list.js
Normal file
48
src/queries/category-list.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import sql from 'mssql';
|
||||
import { getPool } from '../db.js';
|
||||
import { CATEGORY_TREE_CTE, categoryTreeRequest, getRootCategoryId } from './category-tree.js';
|
||||
|
||||
const LANGUAGE_ID = Number(process.env.LANGUAGE_ID) || 1;
|
||||
const IMAGE_PLATFORM_ID = Number(process.env.IMAGE_PLATFORM_ID) || 1;
|
||||
const IMAGE_SHOP_ID = Number(process.env.IMAGE_SHOP_ID) || 0;
|
||||
|
||||
const CATEGORY_LIST_SQL = `
|
||||
${CATEGORY_TREE_CTE}
|
||||
SELECT TOP (@limit)
|
||||
k.kKategorie AS id,
|
||||
k.kOberKategorie AS pid,
|
||||
k.nSort AS sort,
|
||||
ks.cName AS name,
|
||||
b.cHash AS imgHash,
|
||||
CONVERT(BIGINT, k.bRowversion) AS lastChanged
|
||||
FROM dbo.tKategorie k
|
||||
INNER JOIN dbo.tKategorieSprache ks ON ks.kKategorie = k.kKategorie AND ks.kSprache = @languageId
|
||||
LEFT JOIN dbo.tKategoriebildPlattform kbp
|
||||
ON kbp.kKategorie = k.kKategorie AND kbp.kPlattform = @imagePlatformId AND kbp.kShop = @imageShopId
|
||||
LEFT JOIN dbo.tBild b ON b.kBild = kbp.kBild
|
||||
WHERE k.kKategorie IN (SELECT kKategorie FROM CategoryTree WHERE kKategorie <> @rootCategoryId)
|
||||
AND k.cAktiv = 'Y'
|
||||
AND CONVERT(BIGINT, k.bRowversion) > @cursor
|
||||
ORDER BY lastChanged ASC;
|
||||
`;
|
||||
|
||||
export async function getCategoryList({ cursor = 0, limit = 20, rootCategoryId = getRootCategoryId() } = {}) {
|
||||
const result = await categoryTreeRequest(getPool(), rootCategoryId)
|
||||
.input('cursor', sql.BigInt, cursor)
|
||||
.input('limit', sql.Int, limit)
|
||||
.input('languageId', sql.Int, LANGUAGE_ID)
|
||||
.input('imagePlatformId', sql.Int, IMAGE_PLATFORM_ID)
|
||||
.input('imageShopId', sql.Int, IMAGE_SHOP_ID)
|
||||
.query(CATEGORY_LIST_SQL);
|
||||
|
||||
return result.recordset.map((row) => ({
|
||||
_id: String(row.id),
|
||||
imghash: row.imgHash ?? null,
|
||||
imgsrc: row.imgHash ?? null,
|
||||
name: row.name,
|
||||
pid: String(row.pid),
|
||||
discounts: [],
|
||||
sort: String(row.sort),
|
||||
lastChanged: String(row.lastChanged),
|
||||
}));
|
||||
}
|
||||
18
src/queries/category-tree.js
Normal file
18
src/queries/category-tree.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import sql from 'mssql';
|
||||
|
||||
export const CATEGORY_TREE_CTE = `
|
||||
WITH CategoryTree AS (
|
||||
SELECT kKategorie FROM dbo.tKategorie WHERE kKategorie = @rootCategoryId
|
||||
UNION ALL
|
||||
SELECT t.kKategorie
|
||||
FROM dbo.tKategorie t
|
||||
INNER JOIN CategoryTree ct ON t.kOberKategorie = ct.kKategorie
|
||||
)`;
|
||||
|
||||
export function getRootCategoryId() {
|
||||
return Number(process.env.ROOT_CATEGORY_ID) || 1;
|
||||
}
|
||||
|
||||
export function categoryTreeRequest(pool, rootCategoryId = getRootCategoryId()) {
|
||||
return pool.request().input('rootCategoryId', sql.Int, rootCategoryId);
|
||||
}
|
||||
48
src/queries/customer-groups.js
Normal file
48
src/queries/customer-groups.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import sql from 'mssql';
|
||||
import { getPool } from '../db.js';
|
||||
|
||||
const CUSTOMER_GROUP_IDS_SQL = `
|
||||
SELECT kKundenGruppe
|
||||
FROM dbo.tKundenGruppe
|
||||
ORDER BY kKundenGruppe;
|
||||
`;
|
||||
|
||||
const CUSTOMER_GROUP_LIST_SQL = `
|
||||
SELECT
|
||||
kKundenGruppe AS id,
|
||||
cName AS name,
|
||||
nStandard AS standard,
|
||||
fRabatt AS discountPercent,
|
||||
CONVERT(BIGINT, bRowversion) AS lastChanged
|
||||
FROM dbo.tKundenGruppe
|
||||
WHERE CONVERT(BIGINT, bRowversion) > @cursor
|
||||
ORDER BY lastChanged ASC;
|
||||
`;
|
||||
|
||||
const CUSTOMER_GROUP_COUNT_SQL = `
|
||||
SELECT COUNT(*) AS CustomerGroupCount
|
||||
FROM dbo.tKundenGruppe
|
||||
WHERE CONVERT(BIGINT, bRowversion) > @cursor;
|
||||
`;
|
||||
|
||||
export async function getCustomerGroupIds() {
|
||||
const result = await getPool().request().query(CUSTOMER_GROUP_IDS_SQL);
|
||||
return result.recordset.map((row) => row.kKundenGruppe);
|
||||
}
|
||||
|
||||
export async function getCustomerGroupList({ cursor = 0 } = {}) {
|
||||
const result = await getPool().request().input('cursor', sql.BigInt, cursor).query(CUSTOMER_GROUP_LIST_SQL);
|
||||
|
||||
return result.recordset.map((row) => ({
|
||||
customerGroupId: String(row.id),
|
||||
name: row.name,
|
||||
standard: String(row.standard),
|
||||
discountPercent: Number(row.discountPercent).toFixed(2),
|
||||
lastChanged: String(row.lastChanged),
|
||||
}));
|
||||
}
|
||||
|
||||
export async function getCustomerGroupCount({ cursor = 0 } = {}) {
|
||||
const result = await getPool().request().input('cursor', sql.BigInt, cursor).query(CUSTOMER_GROUP_COUNT_SQL);
|
||||
return result.recordset[0]?.CustomerGroupCount ?? 0;
|
||||
}
|
||||
43
src/queries/image.js
Normal file
43
src/queries/image.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import sql from 'mssql';
|
||||
import { getPool } from '../db.js';
|
||||
|
||||
const IMAGE_BY_HASH_SQL = `
|
||||
SELECT bBild, bVorschauBild, nBreite, nHoehe, nVorschauBreite, nVorschauHoehe, cQuelle
|
||||
FROM dbo.tBild
|
||||
WHERE cHash = @hash;
|
||||
`;
|
||||
|
||||
function contentTypeFor(cQuelle) {
|
||||
const extension = (cQuelle || '').split('.').pop()?.toLowerCase();
|
||||
switch (extension) {
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
case 'webp':
|
||||
return 'image/webp';
|
||||
case 'jpg':
|
||||
case 'jpeg':
|
||||
default:
|
||||
return 'image/jpeg';
|
||||
}
|
||||
}
|
||||
|
||||
export async function getImageByHash(hash, size) {
|
||||
const result = await getPool().request().input('hash', sql.NVarChar, hash).query(IMAGE_BY_HASH_SQL);
|
||||
|
||||
const row = result.recordset[0];
|
||||
if (!row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const previewMaxDimension = Math.max(row.nVorschauBreite || 0, row.nVorschauHoehe || 0);
|
||||
const useFull = !previewMaxDimension || Number(size) > previewMaxDimension;
|
||||
const buffer = useFull ? row.bBild : row.bVorschauBild;
|
||||
|
||||
if (!buffer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { buffer, contentType: contentTypeFor(row.cQuelle) };
|
||||
}
|
||||
149
src/queries/product-attributes.js
Normal file
149
src/queries/product-attributes.js
Normal file
@@ -0,0 +1,149 @@
|
||||
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;
|
||||
}
|
||||
20
src/queries/product-count.js
Normal file
20
src/queries/product-count.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import sql from 'mssql';
|
||||
import { getPool } from '../db.js';
|
||||
import { CATEGORY_TREE_CTE, categoryTreeRequest, getRootCategoryId } from './category-tree.js';
|
||||
|
||||
const PRODUCT_COUNT_SQL = `
|
||||
${CATEGORY_TREE_CTE}
|
||||
SELECT COUNT(DISTINCT a.kArtikel) AS ProductCount
|
||||
FROM dbo.tArtikel a
|
||||
INNER JOIN dbo.tKategorieArtikel ka ON ka.kArtikel = a.kArtikel
|
||||
WHERE a.cAktiv = 'Y'
|
||||
AND ka.kKategorie IN (SELECT kKategorie FROM CategoryTree)
|
||||
AND CONVERT(BIGINT, a.bRowversion) > @cursor;
|
||||
`;
|
||||
|
||||
export async function getProductCount({ cursor = 0, rootCategoryId = getRootCategoryId() } = {}) {
|
||||
const result = await categoryTreeRequest(getPool(), rootCategoryId)
|
||||
.input('cursor', sql.BigInt, cursor)
|
||||
.query(PRODUCT_COUNT_SQL);
|
||||
return result.recordset[0]?.ProductCount ?? 0;
|
||||
}
|
||||
151
src/queries/product-list.js
Normal file
151
src/queries/product-list.js
Normal file
@@ -0,0 +1,151 @@
|
||||
import sql from 'mssql';
|
||||
import { getPool } from '../db.js';
|
||||
import { getCustomerGroupIds } from './customer-groups.js';
|
||||
import { getProductAttributes } from './product-attributes.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)
|
||||
)
|
||||
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,
|
||||
CONVERT(BIGINT, a.bRowversion) 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,
|
||||
(
|
||||
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
|
||||
WHERE a.cAktiv = 'Y'
|
||||
AND CONVERT(BIGINT, a.bRowversion) > @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)
|
||||
.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 ?? '',
|
||||
attributes: articleAttributes?.attributes ?? [],
|
||||
...(articleAttributes?.deposit ?? {}),
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user