u
This commit is contained in:
@@ -140,8 +140,10 @@ SELECT
|
|||||||
av.value AS attributeValue
|
av.value AS attributeValue
|
||||||
FROM article child
|
FROM article child
|
||||||
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = @languageId
|
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = @languageId
|
||||||
LEFT JOIN articleattributevalue aav ON aav.article_id = child.id
|
LEFT JOIN (
|
||||||
LEFT JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = @attributeId
|
articleattributevalue aav
|
||||||
|
INNER JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = @attributeId
|
||||||
|
) ON aav.article_id = child.id
|
||||||
WHERE child.fatherarticle_id = @fatherArticleId
|
WHERE child.fatherarticle_id = @fatherArticleId
|
||||||
ORDER BY child.fathersort ASC, child.id ASC
|
ORDER BY child.fathersort ASC, child.id ASC
|
||||||
`;
|
`;
|
||||||
@@ -499,8 +501,10 @@ SELECT
|
|||||||
av.value AS attributeValue
|
av.value AS attributeValue
|
||||||
FROM article child
|
FROM article child
|
||||||
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = auth.languageId
|
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = auth.languageId
|
||||||
LEFT JOIN articleattributevalue aav ON aav.article_id = child.id
|
LEFT JOIN (
|
||||||
LEFT JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = a.fatherattributename_id
|
articleattributevalue aav
|
||||||
|
INNER JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = a.fatherattributename_id
|
||||||
|
) ON aav.article_id = child.id
|
||||||
WHERE child.fatherarticle_id = a.id
|
WHERE child.fatherarticle_id = a.id
|
||||||
ORDER BY child.fathersort ASC, child.id ASC
|
ORDER BY child.fathersort ASC, child.id ASC
|
||||||
FOR JSON PATH
|
FOR JSON PATH
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ FROM article
|
|||||||
WHERE fatherarticle_id = @articleId
|
WHERE fatherarticle_id = @articleId
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GET_CHILDREN_IDS = `
|
||||||
|
SELECT id
|
||||||
|
FROM article
|
||||||
|
WHERE fatherarticle_id = @id
|
||||||
|
`;
|
||||||
|
|
||||||
export const GET_LIST_IDS = `
|
export const GET_LIST_IDS = `
|
||||||
SELECT articlelist_id AS articlelistId, optionslist_id AS optionslistId
|
SELECT articlelist_id AS articlelistId, optionslist_id AS optionslistId
|
||||||
FROM article
|
FROM article
|
||||||
|
|||||||
@@ -175,8 +175,10 @@ SELECT auth.*,
|
|||||||
'attributeValue', av.value
|
'attributeValue', av.value
|
||||||
)), '[]') FROM article child
|
)), '[]') FROM article child
|
||||||
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = auth.languageId
|
LEFT JOIN articletext at_child ON at_child.article_id = child.id AND at_child.language_id = auth.languageId
|
||||||
LEFT JOIN articleattributevalue aav ON aav.article_id = child.id
|
LEFT JOIN (
|
||||||
LEFT JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = a.fatherattributename_id
|
articleattributevalue aav
|
||||||
|
INNER JOIN attributevalue av ON av.id = aav.attributevalue_id AND av.attribute_id = a.fatherattributename_id
|
||||||
|
) ON aav.article_id = child.id
|
||||||
WHERE child.fatherarticle_id = a.id
|
WHERE child.fatherarticle_id = a.id
|
||||||
ORDER BY child.fathersort ASC, child.id ASC)),
|
ORDER BY child.fathersort ASC, child.id ASC)),
|
||||||
'categoryIdsJson', json(${JSON_CATEGORIES}),
|
'categoryIdsJson', json(${JSON_CATEGORIES}),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { translateQueries } from './translate.js';
|
|||||||
const queries = translateQueries(source, 'articleDelete');
|
const queries = translateQueries(source, 'articleDelete');
|
||||||
export const {
|
export const {
|
||||||
DELETE_ARTICLELIST, DELETE_BY_ID, DELETE_OPTIONSLIST, GET_CHILD_COUNT,
|
DELETE_ARTICLELIST, DELETE_BY_ID, DELETE_OPTIONSLIST, GET_CHILD_COUNT,
|
||||||
GET_LIST_IDS,
|
GET_CHILDREN_IDS, GET_LIST_IDS,
|
||||||
} = queries;
|
} = queries;
|
||||||
|
|
||||||
export const DELETE_PICS_BY_ARTICLE = `
|
export const DELETE_PICS_BY_ARTICLE = `
|
||||||
|
|||||||
@@ -78,8 +78,11 @@ export default function addChild(provider) {
|
|||||||
let finalValue = null;
|
let finalValue = null;
|
||||||
|
|
||||||
if (options.attributeValueId != null) {
|
if (options.attributeValueId != null) {
|
||||||
|
if (typeof options.attributeValueId !== 'number' || !Number.isInteger(options.attributeValueId) || options.attributeValueId <= 0) {
|
||||||
|
throw new Error('Invalid attributeValueId');
|
||||||
|
}
|
||||||
const valRes = await runner.query(attrValQueries.GET_BY_ID_AND_COMPANY, {
|
const valRes = await runner.query(attrValQueries.GET_BY_ID_AND_COMPANY, {
|
||||||
id: Number(options.attributeValueId),
|
id: options.attributeValueId,
|
||||||
companyId,
|
companyId,
|
||||||
});
|
});
|
||||||
const row = valRes.rows[0];
|
const row = valRes.rows[0];
|
||||||
|
|||||||
@@ -9,32 +9,40 @@ export default function del(provider) {
|
|||||||
const queries = isSqlite ? sqliteQueries : mssqlQueries;
|
const queries = isSqlite ? sqliteQueries : mssqlQueries;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes an article by its SKU, refusing when it still has children.
|
* 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 {number} googleAuthId - The id of the Google auth user.
|
||||||
* @param {string} sku - The article SKU.
|
* @param {string} sku - The article SKU.
|
||||||
* @param {object} [tx] - Optional transaction to run within.
|
* @param {object|*} [optionsOrTx] - Options object ({ cascade?: boolean }) or optional transaction.
|
||||||
|
* @param {object} [maybeTx] - Optional transaction if options was passed.
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
* @throws {Error} If the article has children or is not found.
|
* @throws {Error} If the article has children (when cascade=false) or is not found.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* await store.article.delete(42, 'SKU-1');
|
* await store.article.delete(42, 'SKU-1');
|
||||||
|
* await store.article.delete(42, 'SKU-1', { cascade: true });
|
||||||
*/
|
*/
|
||||||
return async (googleAuthId, sku, tx) => {
|
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 runDelete = async (runner) => {
|
||||||
const { articleId } = await resolve(googleAuthId, sku, runner);
|
const { articleId } = await resolve(googleAuthId, sku, runner);
|
||||||
|
|
||||||
const childCount = await runner.query(queries.GET_CHILD_COUNT, { articleId });
|
const deleteArticleAndLists = async (id) => {
|
||||||
if (childCount.rows[0].count > 0) {
|
const listIds = await runner.query(queries.GET_LIST_IDS, { articleId: id });
|
||||||
throw new Error('Article has children');
|
|
||||||
}
|
|
||||||
|
|
||||||
const listIds = await runner.query(queries.GET_LIST_IDS, { articleId });
|
|
||||||
const { articlelistId, optionslistId } = listIds.rows[0] ?? {};
|
const { articlelistId, optionslistId } = listIds.rows[0] ?? {};
|
||||||
|
|
||||||
await runner.query(queries.DELETE_PICS_BY_ARTICLE, { articleId });
|
await runner.query(queries.DELETE_PICS_BY_ARTICLE, { articleId: id });
|
||||||
|
|
||||||
const deleted = await runner.query(queries.DELETE_BY_ID, { articleId });
|
const deleted = await runner.query(queries.DELETE_BY_ID, { articleId: id });
|
||||||
if (!deleted.rowsAffected) {
|
if (!deleted.rowsAffected) {
|
||||||
throw new Error('Article not found');
|
throw new Error('Article not found');
|
||||||
}
|
}
|
||||||
@@ -48,6 +56,26 @@ export default function del(provider) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteArticleAndLists(articleId);
|
||||||
|
};
|
||||||
|
|
||||||
if (tx) {
|
if (tx) {
|
||||||
return runDelete(makeRunner(provider, tx));
|
return runDelete(makeRunner(provider, tx));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export default function removeChild(provider) {
|
|||||||
* @param {string} childSku - The child article SKU.
|
* @param {string} childSku - The child article SKU.
|
||||||
* @param {object} [options]
|
* @param {object} [options]
|
||||||
* @param {boolean} [options.deleteArticle=false] - Whether to delete the child article entity entirely.
|
* @param {boolean} [options.deleteArticle=false] - Whether to delete the child article entity entirely.
|
||||||
|
* @param {boolean} [options.cascade=true] - Whether to cascade delete child subtrees when deleteArticle is true.
|
||||||
* @param {object} [tx] - Optional transaction.
|
* @param {object} [tx] - Optional transaction.
|
||||||
* @returns {Promise<{success: boolean, fatherSku: string, childSku: string, deleted: boolean}>}
|
* @returns {Promise<{success: boolean, fatherSku: string, childSku: string, deleted: boolean}>}
|
||||||
*/
|
*/
|
||||||
@@ -45,7 +46,8 @@ export default function removeChild(provider) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.deleteArticle) {
|
if (options.deleteArticle) {
|
||||||
await del(googleAuthId, childSku, runner);
|
const cascade = options.cascade !== undefined ? Boolean(options.cascade) : true;
|
||||||
|
await del(googleAuthId, childSku, { cascade }, runner);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
fatherSku,
|
fatherSku,
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ export default function setBrandIdBySku(provider) {
|
|||||||
runner,
|
runner,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (brandId !== null && !Number.isFinite(brandId)) {
|
if (brandId !== null && (typeof brandId !== 'number' || !Number.isInteger(brandId) || brandId <= 0)) {
|
||||||
throw new Error('Brand not found');
|
throw new Error('Invalid brandId');
|
||||||
}
|
}
|
||||||
|
|
||||||
let resolvedBrandId = null;
|
let resolvedBrandId = null;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ADD_EAN as SQLITE_ADD_EAN,
|
ADD_EAN as SQLITE_ADD_EAN,
|
||||||
DELETE_EANS_BY_ARTICLE as SQLITE_DELETE_EANS,
|
DELETE_EANS_BY_ARTICLE as SQLITE_DELETE_EANS,
|
||||||
} from '../../providers/sqlite3/queries/article.js';
|
} from '../../providers/sqlite3/queries/article.js';
|
||||||
|
import { isUniqueConstraintError } from '../../providers/provider.js';
|
||||||
import queryBySku from './queryBySku.js';
|
import queryBySku from './queryBySku.js';
|
||||||
import resolveArticleBySku from './resolveArticleBySku.js';
|
import resolveArticleBySku from './resolveArticleBySku.js';
|
||||||
import makeRunner from '../runner.js';
|
import makeRunner from '../runner.js';
|
||||||
@@ -55,9 +56,16 @@ export default function setEansBySku(provider) {
|
|||||||
|
|
||||||
await runner.query(deleteEansSql, { articleId });
|
await runner.query(deleteEansSql, { articleId });
|
||||||
|
|
||||||
|
try {
|
||||||
for (const ean of normalized) {
|
for (const ean of normalized) {
|
||||||
await runner.query(addEanSql, { articleId, ean });
|
await runner.query(addEanSql, { articleId, ean });
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (isUniqueConstraintError(err)) {
|
||||||
|
throw new Error('EAN already in use');
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
const article = await load(articleId, languageId, runner);
|
const article = await load(articleId, languageId, runner);
|
||||||
if (!article) {
|
if (!article) {
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ function normalizePrices(prices) {
|
|||||||
? 1
|
? 1
|
||||||
: entry.minmultiplier;
|
: entry.minmultiplier;
|
||||||
|
|
||||||
if (!Number.isFinite(price) || !Number.isFinite(tax) || !Number.isFinite(minmultiplier)) {
|
if (
|
||||||
throw new Error('Invalid prices');
|
typeof price !== 'number' || !Number.isFinite(price) || price < 0 ||
|
||||||
}
|
typeof tax !== 'number' || !Number.isFinite(tax) || tax < 0 ||
|
||||||
if (minmultiplier <= 0) {
|
typeof minmultiplier !== 'number' || !Number.isFinite(minmultiplier) || minmultiplier <= 0
|
||||||
|
) {
|
||||||
throw new Error('Invalid prices');
|
throw new Error('Invalid prices');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,11 @@ export async function assertSupplier(runnerOrPool, txOrCompanyId, maybeCompanyId
|
|||||||
supplierId = maybeCompanyId;
|
supplierId = maybeCompanyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supplierId == null || !Number.isFinite(supplierId)) {
|
if (supplierId == null) {
|
||||||
throw new Error('Supplier not found');
|
throw new Error('supplierId is required');
|
||||||
|
}
|
||||||
|
if (typeof supplierId !== 'number' || !Number.isInteger(supplierId) || supplierId <= 0) {
|
||||||
|
throw new Error('Invalid supplierId');
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSqlite = runner?.dialect === 'sqlite3';
|
const isSqlite = runner?.dialect === 'sqlite3';
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ export default function resolveCustomerGroupAccess(provider) {
|
|||||||
: MSSQL_GET_BY_COMPANY_AND_ID;
|
: MSSQL_GET_BY_COMPANY_AND_ID;
|
||||||
|
|
||||||
return async (companyId, customergroupId, tx) => {
|
return async (companyId, customergroupId, tx) => {
|
||||||
if (!Number.isFinite(customergroupId)) {
|
if (typeof customergroupId !== 'number' || !Number.isInteger(customergroupId) || customergroupId <= 0) {
|
||||||
throw new Error('Customer group not found');
|
throw new Error('Invalid customergroupId');
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await makeRunner(provider, tx).query(sql, {
|
const result = await makeRunner(provider, tx).query(sql, {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ export async function assertSupplier(provider, tx, companyId, supplierId) {
|
|||||||
if (supplierId == null) {
|
if (supplierId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!Number.isFinite(supplierId)) {
|
if (typeof supplierId !== 'number' || !Number.isInteger(supplierId) || supplierId <= 0) {
|
||||||
throw new Error('Supplier not found');
|
throw new Error('Invalid supplierId');
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSqlite = provider?.dialect === 'sqlite3';
|
const isSqlite = provider?.dialect === 'sqlite3';
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ export async function assertCustomerEntity(runnerOrPool, txOrCompanyId, maybeCom
|
|||||||
if (entityId == null) {
|
if (entityId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!Number.isFinite(entityId)) {
|
if (typeof entityId !== 'number' || !Number.isInteger(entityId) || entityId <= 0) {
|
||||||
throw new Error('Entity not found');
|
throw new Error('Invalid entityId');
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSqlite = runner?.dialect === 'sqlite3';
|
const isSqlite = runner?.dialect === 'sqlite3';
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ export function normalizeLines(lines) {
|
|||||||
: String(entry.comment);
|
: String(entry.comment);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!Number.isFinite(pu) ||
|
typeof pu !== 'number' || !Number.isFinite(pu) || pu <= 0 ||
|
||||||
!Number.isFinite(amount) ||
|
typeof amount !== 'number' || !Number.isFinite(amount) ||
|
||||||
!Number.isFinite(net) ||
|
typeof net !== 'number' || !Number.isFinite(net) ||
|
||||||
!Number.isFinite(rebate) ||
|
typeof rebate !== 'number' || !Number.isFinite(rebate) ||
|
||||||
!Number.isFinite(taxrate)
|
typeof taxrate !== 'number' || !Number.isFinite(taxrate) || taxrate < 0
|
||||||
) {
|
) {
|
||||||
throw new Error('Invalid lines');
|
throw new Error('Invalid lines');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,18 +82,18 @@ export default function adjust(provider) {
|
|||||||
if (rawWhId == null) {
|
if (rawWhId == null) {
|
||||||
throw new Error(`Warehouse required${itemLabel} (${sku})`);
|
throw new Error(`Warehouse required${itemLabel} (${sku})`);
|
||||||
}
|
}
|
||||||
const warehouseId = Number(rawWhId);
|
if (typeof rawWhId !== 'number' || !Number.isInteger(rawWhId) || rawWhId <= 0) {
|
||||||
if (!Number.isInteger(warehouseId) || warehouseId <= 0) {
|
|
||||||
throw new Error(`Warehouse must be a positive integer${itemLabel} (${sku})`);
|
throw new Error(`Warehouse must be a positive integer${itemLabel} (${sku})`);
|
||||||
}
|
}
|
||||||
|
const warehouseId = rawWhId;
|
||||||
|
|
||||||
if (line.amount == null) {
|
if (line.amount == null) {
|
||||||
throw new Error(`Amount required${itemLabel} (${sku})`);
|
throw new Error(`Amount required${itemLabel} (${sku})`);
|
||||||
}
|
}
|
||||||
const amount = Number(line.amount);
|
if (typeof line.amount !== 'number' || !Number.isFinite(line.amount) || line.amount === 0) {
|
||||||
if (!Number.isFinite(amount) || amount === 0) {
|
|
||||||
throw new Error(`Invalid amount${itemLabel} (${sku}): must be non-zero number`);
|
throw new Error(`Invalid amount${itemLabel} (${sku}): must be non-zero number`);
|
||||||
}
|
}
|
||||||
|
const amount = line.amount;
|
||||||
|
|
||||||
const text = (line.text ?? line.transactionText ?? line.transaction_text ?? defaultText)?.trim?.() ||
|
const text = (line.text ?? line.transactionText ?? line.transaction_text ?? defaultText)?.trim?.() ||
|
||||||
(amount > 0 ? `Manual stock addition (+${amount})` : `Manual stock removal (${amount})`);
|
(amount > 0 ? `Manual stock addition (+${amount})` : `Manual stock removal (${amount})`);
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ export default function create(provider) {
|
|||||||
* // => 1
|
* // => 1
|
||||||
*/
|
*/
|
||||||
return async (rate, tx) => {
|
return async (rate, tx) => {
|
||||||
|
if (typeof rate !== 'number' || !Number.isFinite(rate) || rate < 0) {
|
||||||
|
throw new Error('Invalid tax rate');
|
||||||
|
}
|
||||||
const result = await makeRunner(provider, tx).query(sql, { rate });
|
const result = await makeRunner(provider, tx).query(sql, { rate });
|
||||||
return result.rows[0]?.id ?? result.insertId;
|
return result.rows[0]?.id ?? result.insertId;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ export default function ensure(provider) {
|
|||||||
* // => 1
|
* // => 1
|
||||||
*/
|
*/
|
||||||
return async (rate, tx) => {
|
return async (rate, tx) => {
|
||||||
|
if (typeof rate !== 'number' || !Number.isFinite(rate) || rate < 0) {
|
||||||
|
throw new Error('Invalid tax rate');
|
||||||
|
}
|
||||||
const row = await get(rate, tx);
|
const row = await get(rate, tx);
|
||||||
if (row) return row.id;
|
if (row) return row.id;
|
||||||
return add(rate, tx);
|
return add(rate, tx);
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ export default function getByRate(provider) {
|
|||||||
* // => { id: 1, rate: 19 }
|
* // => { id: 1, rate: 19 }
|
||||||
*/
|
*/
|
||||||
return async (rate, tx) => {
|
return async (rate, tx) => {
|
||||||
|
if (typeof rate !== 'number' || !Number.isFinite(rate) || rate < 0) {
|
||||||
|
throw new Error('Invalid tax rate');
|
||||||
|
}
|
||||||
const result = await makeRunner(provider, tx).query(sql, { rate });
|
const result = await makeRunner(provider, tx).query(sql, { rate });
|
||||||
return result.rows[0];
|
return result.rows[0];
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user