117 lines
4.1 KiB
JavaScript
117 lines
4.1 KiB
JavaScript
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<number>} categoryIds - The category ids the article should belong to (at least one).
|
|
* @param {object} [tx] - Optional transaction to run within.
|
|
* @returns {Promise<object>} 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));
|
|
};
|
|
}
|
|
|