Files
dashlibstorage/src/store/article/createUnderParent.js
2026-10-08 12:46:55 +02:00

140 lines
5.7 KiB
JavaScript

import { CREATE as MSSQL_CREATE_ARTICLE } from '../../providers/mssql/queries/article.js';
import { CREATE as SQLITE_CREATE_ARTICLE } from '../../providers/sqlite3/queries/article.js';
import { ADD as MSSQL_ADD_CATEGORYARTICLE } from '../../providers/mssql/queries/categoryarticle.js';
import { ADD as SQLITE_ADD_CATEGORYARTICLE } from '../../providers/sqlite3/queries/categoryarticle.js';
import { CREATE as MSSQL_CREATE_ARTICLETEXT } from '../../providers/mssql/queries/articletext.js';
import { CREATE as SQLITE_CREATE_ARTICLETEXT } from '../../providers/sqlite3/queries/articletext.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 { isUniqueConstraintError } from '../../providers/provider.js';
import resolveCategoryAccess from '../category/resolveCategoryAccess.js';
import ensureDefaultUnit from '../unit/ensureDefault.js';
import nextSku from '../numbers/nextSku.js';
import makeRunner from '../runner.js';
import resolveUniqueSlug from './resolveUniqueSlug.js';
export default function createUnderParent(provider) {
const access = resolveCategoryAccess(provider);
const ensureUnit = ensureDefaultUnit(provider);
const allocateSku = nextSku(provider);
const resolveSlug = resolveUniqueSlug(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const createArticleSql = isSqlite ? SQLITE_CREATE_ARTICLE : MSSQL_CREATE_ARTICLE;
const addCategoryArticleSql = isSqlite ? SQLITE_ADD_CATEGORYARTICLE : MSSQL_ADD_CATEGORYARTICLE;
const createArticleTextSql = isSqlite ? SQLITE_CREATE_ARTICLETEXT : MSSQL_CREATE_ARTICLETEXT;
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
/**
* Creates a new article under a given parent category, allocating a SKU and
* a default unit.
*
* @param {object} input - The article creation input.
* @param {number} input.googleAuthId - The id of the Google auth user.
* @param {number} input.parentId - The id of the parent category.
* @param {string} input.name - The article name.
* @param {string} [input.description] - The article description.
* @param {string} [input.desc] - Alias for description.
* @param {object|string} [input.seo] - The SEO configuration or JSON string.
* @param {string} [input.seoTitle] - The SEO title.
* @param {string} [input.seoDescription] - The SEO meta description.
* @param {boolean|number} [input.track_stock=true] - Whether inventory stock is tracked.
* @param {object} [tx] - Optional transaction to run within.
* @returns {Promise<{id: number, parentId: number, name: string, sku: string, track_stock: boolean}>} The created article.
*
* @example
* const article = await store.article.createUnderParent({ googleAuthId: 42, parentId: 5, name: 'Cola' });
* // => { id, parentId, name, sku, track_stock }
*/
return async ({ googleAuthId, parentId, name, description, desc, shortDescription, seo, seoTitle, seoDescription, track_stock, trackStock, sku: inputSku }, tx) => {
const rawTrack = track_stock ?? trackStock;
const finalTrackStock = rawTrack == null ? 1 : (rawTrack ? 1 : 0);
const runCreate = async (runner) => {
const { companyId, languageId, companyuserId } = await access(
googleAuthId,
parentId,
runner,
);
const unitId = await ensureUnit(runner, languageId, companyId);
const sku = (typeof inputSku === 'string' && inputSku.trim()) ? inputSku.trim() : await allocateSku(companyId, runner);
const slug = await resolveSlug(runner, { companyId, languageId, name });
let article;
try {
article = await runner.query(createArticleSql, {
companyId,
sku,
unitId,
unitamount: 1,
weight: 0,
trackStock: finalTrackStock,
});
} catch (err) {
if (isUniqueConstraintError(err)) {
throw new Error(`SKU "${sku}" already exists`);
}
throw err;
}
const id = article.rows[0]?.id ?? article.insertId;
await runner.query(addCategoryArticleSql, {
categoryId: parentId,
articleId: id,
});
const rawDesc = description !== undefined ? description : desc;
const finalDesc = rawDesc != null && rawDesc !== '' ? String(rawDesc) : null;
let finalSeo = null;
if (shortDescription !== undefined) {
finalSeo = shortDescription != null && shortDescription !== '' ? String(shortDescription) : null;
} else if (typeof seo === 'string') {
finalSeo = seo !== '' ? seo : null;
} else if (seoDescription && !seoTitle) {
finalSeo = String(seoDescription);
} else if (seo != null) {
finalSeo = typeof seo === 'string' ? seo : JSON.stringify(seo);
} else if (seoTitle || seoDescription) {
finalSeo = JSON.stringify({
title: seoTitle != null && seoTitle !== '' ? String(seoTitle) : '',
description: seoDescription != null && seoDescription !== '' ? String(seoDescription) : '',
});
}
await runner.query(createArticleTextSql, {
articleId: id,
languageId,
name,
slug,
desc: finalDesc,
seo: finalSeo,
});
await runner.query(insertHistorySql, {
articleId: id,
companyuserId,
text: `Artikel "${name}" angelegt`,
});
return {
id,
parentId,
name,
sku,
track_stock: Boolean(finalTrackStock),
};
};
if (tx) {
return runCreate(makeRunner(provider, tx));
}
if (typeof provider?.transaction === 'function') {
return provider.transaction(runCreate);
}
return runCreate(makeRunner(provider));
};
}