Files
dashlibstorage/src/store/article/delete.js
2026-10-23 00:47:38 +02:00

121 lines
4.5 KiB
JavaScript

import * as mssqlQueries from '../../providers/mssql/queries/articleDelete.js';
import * as sqliteQueries from '../../providers/sqlite3/queries/articleDelete.js';
import * as mssqlArticle from '../../providers/mssql/queries/article.js';
import * as sqliteArticle from '../../providers/sqlite3/queries/article.js';
import resolveArticleBySku from './resolveArticleBySku.js';
import recordObituary from '../posterminal/recordObituary.js';
import makeRunner from '../runner.js';
export default function del(provider) {
const resolve = resolveArticleBySku(provider);
const recordObit = recordObituary(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const queries = isSqlite ? sqliteQueries : mssqlQueries;
const articleQueries = isSqlite ? sqliteArticle : mssqlArticle;
/**
* Deletes an article by its SKU, refusing when it still has children unless cascade is true.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {string} sku - The article SKU.
* @param {object|*} [optionsOrTx] - Options object ({ cascade?: boolean }) or optional transaction.
* @param {object} [maybeTx] - Optional transaction if options was passed.
* @returns {Promise<void>}
* @throws {Error} If the article has children (when cascade=false) or is not found.
*
* @example
* await store.article.delete(42, 'SKU-1');
* await store.article.delete(42, 'SKU-1', { cascade: true });
*/
return async (googleAuthId, sku, optionsOrTx, maybeTx) => {
let options = {};
let tx;
if (optionsOrTx && typeof optionsOrTx === 'object' && typeof optionsOrTx.query !== 'function') {
options = optionsOrTx;
tx = maybeTx;
} else {
tx = optionsOrTx;
}
const cascade = Boolean(options.cascade);
const runDelete = async (runner) => {
const { articleId, companyId } = await resolve(googleAuthId, sku, runner);
const deleteArticleAndLists = async (id) => {
const listIds = await runner.query(queries.GET_LIST_IDS, { articleId: id });
const { articlelistId, optionslistId } = listIds.rows[0] ?? {};
await recordObit({ companyId, entityType: 'article', entityId: id }, runner);
await runner.query(queries.DELETE_PICS_BY_ARTICLE, { articleId: id });
// Touch any parent articles that used this article as an option before deleting it
await runner.query(articleQueries.TOUCH_OPTION_PARENT_ARTICLES_UPDATED_AT, { articleId: id });
const deleted = await runner.query(queries.DELETE_BY_ID, { articleId: id });
if (!deleted.rowsAffected) {
throw new Error('Article not found');
}
if (articlelistId != null) {
await runner.query(queries.DELETE_ARTICLELIST, { id: articlelistId });
}
if (optionslistId != null) {
await runner.query(queries.DELETE_OPTIONSLIST, { id: optionslistId });
}
};
const deleteSubtree = async (id) => {
const children = await runner.query(queries.GET_CHILDREN_IDS, { id });
for (const child of children.rows) {
await deleteSubtree(child.id);
}
await deleteArticleAndLists(id);
};
const parentRes = await runner.query(
'SELECT parentarticle_id FROM article WHERE id = @articleId',
{ articleId },
);
const parentArticleId = parentRes.rows[0]?.parentarticle_id;
const childCount = await runner.query(queries.GET_CHILD_COUNT, { articleId });
if (childCount.rows[0].count > 0) {
if (!cascade) {
throw new Error('Article has children');
}
await deleteSubtree(articleId);
} else {
await deleteArticleAndLists(articleId);
}
if (parentArticleId != null) {
const remainingChildrenRes = await runner.query(
'SELECT id, parentsort FROM article WHERE parentarticle_id = @parentArticleId ORDER BY parentsort ASC, id ASC',
{ parentArticleId },
);
for (let i = 0; i < remainingChildrenRes.rows.length; i++) {
const childRow = remainingChildrenRes.rows[i];
if (Number(childRow.parentsort) !== i) {
await runner.query(articleQueries.SET_PARENT_SORT, {
articleId: childRow.id,
parentArticleId,
parentsort: i,
});
}
}
}
};
if (tx) {
return runDelete(makeRunner(provider, tx));
}
if (typeof provider?.transaction === 'function') {
return provider.transaction(runDelete);
}
return runDelete(makeRunner(provider));
};
}