import { ADD_CATEGORY as MSSQL_ADD_CATEGORY, DELETE_CATEGORIES_BY_ARTICLE as MSSQL_DELETE_CATEGORIES, } from '../../providers/mssql/queries/article.js'; import { ADD_CATEGORY as SQLITE_ADD_CATEGORY, DELETE_CATEGORIES_BY_ARTICLE as SQLITE_DELETE_CATEGORIES, } from '../../providers/sqlite3/queries/article.js'; import { INSERT as MSSQL_INSERT_HISTORY } from '../../providers/mssql/queries/articlehistory.js'; import { INSERT as SQLITE_INSERT_HISTORY } from '../../providers/sqlite3/queries/articlehistory.js'; import resolveCategoryAccess from '../category/resolveCategoryAccess.js'; import queryBySku from './queryBySku.js'; import resolveArticleBySku from './resolveArticleBySku.js'; import makeRunner from '../runner.js'; export default function setCategoryIdsBySku(provider) { const resolve = resolveArticleBySku(provider); const categoryAccess = resolveCategoryAccess(provider); const load = queryBySku(provider); const isSqlite = provider?.dialect === 'sqlite3'; const addCategorySql = isSqlite ? SQLITE_ADD_CATEGORY : MSSQL_ADD_CATEGORY; const deleteCategoriesSql = isSqlite ? SQLITE_DELETE_CATEGORIES : MSSQL_DELETE_CATEGORIES; const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY; /** * Replaces the category membership of an article by its SKU. * * @param {number} googleAuthId - The id of the Google auth user. * @param {string} sku - The article SKU. * @param {Array} categoryIds - The category ids the article should belong to (at least one). * @param {object} [tx] - Optional transaction to run within. * @returns {Promise} The updated article. * @throws {Error} If the category ids are invalid or the article is not found. * * @example * const article = await store.article.setCategoryIdsBySku(42, 'SKU-1', [5, 9]); * // => { id, sku, name, ... } */ return async (googleAuthId, sku, categoryIds, tx) => { if (!Array.isArray(categoryIds)) { throw new Error('Invalid categoryIds'); } const seen = new Set(); const normalized = []; for (const categoryId of categoryIds) { if (!Number.isFinite(categoryId) || seen.has(categoryId)) { throw new Error('Invalid categoryIds'); } seen.add(categoryId); normalized.push(categoryId); } if (normalized.length === 0) { throw new Error('Article must have at least one category'); } const runSet = async (runner) => { const { articleId, languageId, companyId, companyuserId } = await resolve( googleAuthId, sku, runner, ); // Read old category IDs for history const oldCatRes = await runner.query( 'SELECT category_id FROM categoryarticle WHERE article_id = @articleId ORDER BY category_id', { articleId }, ); const oldCategoryIds = oldCatRes.rows.map((r) => r.category_id); for (const categoryId of normalized) { // Ensures the category belongs to the same company as the article. const access = await categoryAccess(googleAuthId, categoryId, runner); if (access.companyId !== companyId) { throw new Error('Invalid categoryIds'); } } await runner.query(deleteCategoriesSql, { articleId }); for (const categoryId of normalized) { await runner.query(addCategorySql, { categoryId, articleId, }); } // Write history if categories changed const oldStr = oldCategoryIds.join(', '); const newStr = [...normalized].sort((a, b) => a - b).join(', '); if (oldStr !== newStr) { await runner.query(insertHistorySql, { articleId, companyuserId, text: `Kategorien geƤndert von [${oldStr}] auf [${newStr}]`, }); } const article = await load(articleId, languageId, runner); if (!article) { throw new Error('Article not found'); } return article; }; if (tx) { return runSet(makeRunner(provider, tx)); } if (typeof provider?.transaction === 'function') { return provider.transaction(runSet); } return runSet(makeRunner(provider)); }; }